Роберт Лав - Разработка ядра Linux
- Название:Разработка ядра Linux
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2006
- Город:Москва
- ISBN:5-8459-1085-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роберт Лав - Разработка ядра Linux краткое содержание
В книге детально рассмотрены основные подсистемы и функции ядер Linux серии 2.6, включая особенности построения, реализации и соответствующие программны интерфейсы. Рассмотренные вопросы включают: планирование выполнения процессов, управление временем и таймеры ядра, интерфейс системных вызовов, особенности адресации и управления памятью, страничный кэш, подсистему VFS, механизмы синхронизации, проблемы переносимости и особенности отладки. Автор книги является разработчиком основных подсистем ядра Linux. Ядро рассматривается как с теоретической, так и с прикладной точек зрения, что может привлечь читателей различными интересами и потребностями.
Книга может быть рекомендована как начинающим, так и опытным разработчикам программного обеспечения, а также в качестве дополнительных учебных материалов.
Разработка ядра Linux - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Получение страниц памяти
Теперь, имея некоторое понятие о том, как ядро управляет памятью с помощью страниц, зон и так далее, давайте рассмотрим интерфейсы, которые реализованы в ядре для того, чтобы выделять и освобождать память внутри ядра. Ядро предоставляет один низкоуровневый интерфейс для выделения памяти и несколько интерфейсов для доступа к ней. Все эти интерфейсы выделяют память в объеме, кратном размеру страницы, и определены в файле . Основная функция выделения памяти следующая.
struct page * alloc_pages(unsigned int gfp_mask, unsigned int order);
Данная функция позволяет выделить 2 order
(т.е. 1 << order
) смежных страниц (один непрерывный участок) физической памяти и возвращает указатель на структуру page
, которая соответствует первой выделенной странице памяти. В случае ошибки возвращается значение NULL
. Параметр gfp_mask
будет рассмотрен несколько позже. Полученную страницу памяти можно конвертировать в ее логический адрес с помощью следующей функции.
void *page_address(struct page *page);
Эта функция возвращает указатель на логический адрес, которому в данный момент соответствует начало указанной страницы физической памяти. Если нет необходимости в соответствующей структуре struct page
, то можно использовать следующую функцию.
unsigned long __get_free_pages(unsigned int gfp_mask,
unsigned int order);
Эта функция работает так же, как и функция alloc_pages()
, за исключением того, что она сразу возвращает логический адрес первой выделенной страницы памяти. Так как выделяются смежные страницы памяти, то другие страницы просто следуют за первой.
Если необходима всего одна страница памяти, то для этой цели определены следующие функции-оболочки, которые позволяют уменьшить количество работы по набору кода программы.
struct page * alloc_page(unsigned int gfp_mask);
unsigned long __get_free_page(unsigned int gfp_mask);
Эти функции работают так же, как и ранее описанные, по для них в качестве параметра order
передается нуль (2 0= одна страница памяти).
Получение страниц заполненных нулями
Для того чтобы получаемые страницы памяти были заполнены нулями, необходимо использовать следующую функцию.
unsigned long get_zeroed_page(unsigned int gfp_mask);
Эта функция аналогична функции __get_free_page()
, за исключением того, что после выделения страницы памяти она заполняется нулями. Это полезно для страниц памяти, которые возвращаются в пространство пользователя, так как случайный "мусор", который находится в страницах памяти, может оказаться не совсем случайным и случайно может содержать некоторые (например, секретные) данные. Все данные необходимо обнулить или очистить каким-либо другим образом перед тем, как возвращать информацию в пространство пользователя, чтобы при этом не пострадала безопасность системы. В табл. 11.2 приведен список всех низкоуровневых средств выделения памяти.
Таблица 11.2. Низкоуровневые средства выделения памяти
Функция | Описание |
---|---|
alloc_page(gfp_mask) |
Выделяет одну страницу памяти и возвращает указатель на соответствующую ей структуру page |
alloc_pages(gfp_mask, order) |
Выделяет 2 order страниц памяти и возвращает указатель на структуру page первой страницы |
__get_free_page(gfp_mask) |
Выделяет одну страницу памяти и возвращает указатель на ее логический адрес |
__get_free_pages(gfp_mask, order) |
Выделяет 2 order страниц памяти и возвращает указатель на логический адрес первой страницы |
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()
— наиболее предпочтительный интерфейс.
Рассматриваемая функция определена в файле следующим образом.
void * kmalloc(size_t size, int flags);
Данная функция возвращает указатель на участок памяти, который имеет размер хотя бы size
байт [63] Данная функция может выделить памяти больше, чем указано, и нет никакой возможности узнать, на сколько больше! Поскольку в своей основе система выделения памяти в ядре базируется на страницах, некоторые запросы на выделение памяти могут округляться, чтобы хорошо вписываться е области доступной памяти. Ядро никогда не выделит меньше памяти, чем необходимо. Если ядро не в состоянии найти хотя бы указанное количество байтов, то операция завершится неудачно и функции возвратит значение NULL .
. Выделенный участок памяти содержит физически смежные страницы. В случае ошибки функция возвращает значение NULL
. Выделение памяти в ядре заканчивается успешно, только если доступно достаточное количество памяти. Поэтому после вызова функции kmalloc()
всегда необходимо проверять возвращаемое значение на равенство значению NULL
и соответственным образом обрабатывать ошибку.
Интервал:
Закладка: