Шрифт:
Интервал:
Закладка:
Таблица 11.2. Низкоуровневые средства выделения памяти
Функция Описание alloc_page(gfp_mask) Выделяет одну страницу памяти и возвращает указатель на соответствующую ей структуру page alloc_pages(gfp_mask, order) Выделяет 2order страниц памяти и возвращает указатель на структуру page первой страницы __get_free_page(gfp_mask) Выделяет одну страницу памяти и возвращает указатель на ее логический адрес __get_free_pages(gfp_mask, order) Выделяет 2order страниц памяти и возвращает указатель на логический адрес первой страницы get_zeroed_page(gfp_mask) Выделяет одну страницу памяти, обнуляет ее содержимое и возвращает указатель на ее логический адресОсвобождение страниц
Для освобождения страниц, которые больше не нужны, можно использовать следующие функции.
void __free_pages(struct page *page, unsigned int order);
void free_pages(unsigned long addr, unsigned int order);
void free_page(unsigned long addr);
Необходимо быть внимательными и освобождать только те страницы памяти, которые вам выделены. Передача неправильного значения параметра page, addr или order может привести к порче данных. Следует помнить, что ядро доверяет себе. В отличие от пространства пользователя, ядро с удовольствием зависнет, если вы попросите. Рассмотрим пример. Мы хотим выделить 8 страниц памяти.
page = __get_free_pages(GFP_KERNEL, 3);
if (!page) {
/* недостаточно памяти: эту ошибку необходимо обработать самим! */
return -ENOMEM;
}
/* переменная 'page' теперь содержит адрес
первой из восьми страниц памяти */
free_pages(page, 3);
/*
* наши страницы памяти теперь освобождены и нам больше нельзя
* обращаться по адресу, который хранится в переменной 'page'
*/
Значение GFP_KERNEL, которое передается в качестве параметра, — это пример флага gfp_mask, который скоро будет рассмотрен детально.
Обратите внимание на проверку ошибок после вызова функции __get_free_pages(). Выделение памяти в ядре может быть неудачным, поэтому код должен проверить и при необходимости обработать соответствующую ошибку. Это может означать, что придется пересмотреть все операции, которые были до этого сделаны. В связи с этим, часто имеет смысл выделять память в самом начале подпрограммы, чтобы упростить обработку ошибок. В противном случае после попытки выделения памяти отмена ранее выполненных действий может оказаться сложной.
Низкоуровневые функции выделения памяти полезны, когда необходимы участки памяти, которые находятся в смежных физических страницах, особенно если необходима одна страница или очень большое количество страниц. Для более общего случая, когда необходимо выделить заданное количество байтов памяти, ядро предоставляет функцию kmalloc().
Функция kmalloc()
Функция kmalloc() работает аналогично функции malloc() пространства пользователя, за исключением того, что добавляется еще один параметр flags. Функция kmalloc() — это простой интерфейс для выделения в ядре участков памяти размером в заданное количество байтов. Если необходимы смежные страницы физической памяти, особенно когда их количество близко целой степени двойки, то ранее рассмотренные интерфейсы могут оказаться лучшим выбором. Однако для большинства операций выделения памяти в ядре функция kmalloc() — наиболее предпочтительный интерфейс.
Рассматриваемая функция определена в файле <linux/slab.h> следующим образом.
void * kmalloc(size_t size, int flags);
Данная функция возвращает указатель на участок памяти, который имеет размер хотя бы size байт[63]. Выделенный участок памяти содержит физически смежные страницы. В случае ошибки функция возвращает значение NULL. Выделение памяти в ядре заканчивается успешно, только если доступно достаточное количество памяти. Поэтому после вызова функции kmalloc() всегда необходимо проверять возвращаемое значение на равенство значению NULL и соответственным образом обрабатывать ошибку.
Рассмотрим пример. Допустим, нам необходимо выделить достаточно памяти для того, чтобы в ней можно было разместить некоторую воображаемую структуру dog.
struct dog *ptr;
ptr = kmalloc(sizeof (struct dog), GFP_KERNEL);
if (!ptr)
/* здесь обработать ошибку ... */
Если вызов функции kmalloc() завершится успешно, то переменная ptr будет указывать на область памяти, размер которой больше указанного значения или равен ему. Флаг GFP_KERNEL определяет тип поведения системы выделения памяти, когда она пытается выделить необходимую память при вызове функции kmalloc().
Флаги gfp_mask
Выше были показаны различные примеры использования флагов, которые модифицируют работу системы выделения памяти, как при вызове низкоуровневых функций, работающих на уровне страниц, так и при использовании функции kmalloc(). Теперь давайте рассмотрим их более детально.
Флаги разбиты на три категории: модификаторы операций, модификаторы зон и флаги типов. Модификаторы операций указывают, каким образом ядро должно выделять указанную память. В некоторых ситуациях только некоторые методы могут использоваться для выделения памяти. Например, обработчики прерываний могут потребовать от ядра, что нельзя переходить в состояние ожидания при выделении памяти (поскольку обработчики прерывания не могут быть перепланированы), Модификаторы зоны указывают, откуда нужно выделять память. Как было рассказано, ядро делит физическую память на несколько зон, каждая из которых служит для различных целей. Модификаторы зоны указывают, из какой зоны выделять память. Флаги типов представляют собой различные комбинации модификаторов операций и зон, которые необходимы для определенного типа выделения памяти. Флаги типов содержат в себе различные модификаторы, вместо которых можно просто использовать один флаг типа. Флаг GFP_KERNEL — это флаг типа, который используется кодом, выполняющимся в ядре в контексте процесса. Рассмотрим все флаги отдельно.
Модификаторы операцийВсе флаги, включая модификаторы операций, определены в заголовочном файле <linux/gfp.h>. Подключение файла <linux/slab.h> также подключает и этот заголовочный файл, поэтому его не часто приходится подключать явно. На практике обычно лучше использовать флаги типов, которые будут рассмотрены дальше. Тем не менее полезно иметь представление об индивидуальных флагах. В табл. 11.3 показан список модификаторов операций.
Таблица 11.3. Модификаторы операций
Флаг Описание __GFP_WAIT Операция выделения памяти может переводить текущий процесс в состояние ожидания __GFP_HIGH Операция выделения памяти может обращаться к аварийным запасам __GFP_IO Операция выделения памяти может использовать дисковые операции ввода-вывода __GFP_FS Операция выделения памяти может использовать операции ввода- вывода файловой системы __GFP_COLD Операция выделения памяти должна использовать страницы памяти, содержимое которых не находится в кэше процессора (cache cold) __GFP_NOWARN Операция выделения памяти не будет печатать сообщения об ошибках __GFP_REPEAT Операция выделения памяти повторит попытку выделения в случае ошибки __GFP_NOFAIL Операция выделения памяти будет повторять попытки выделения неопределенное количество раз __GFP_NORETRY Операция выделения памяти никогда не будет повторять попытку выделения памяти __GFP_NO_GROW Используется внутри слябового распределителя памяти (slab layer) __GFP_COMP Добавить метаданные составной (compound) страницы памяти. Используется кодом поддержки больших страниц памяти (hugetlb)Описанные модификаторы можно указывать вместе, как показано в следующем примере.
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- Как спроектировать современный сайт - Чои Вин - Программирование
- Каждому проекту своя методология - Алистэр Коуберн - Программирование
- Программируем Arduino. Основы работы со скетчами - Монк Саймон - Программирование
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- Гибкое управление проектами и продуктами - Борис Вольфсон - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Программирование игр и головоломок - Жак Арсак - Программирование
- Как почистить сканы книг и сделать книгу - IvanStorogev? KpNemo - Программирование