Чтение онлайн

на главную - закладки

Жанры

Разработка ядра Linux (Второе издание)
Шрифт:

В случае успешного выполнения функция

kmem_cache_create
возвращает указатель на созданный кэш. В противном случае возвращается
NULL
. Данная функция не может вызываться в контексте прерывания, так как она может переводить процесс в состояние ожидания. Для ликвидации кэша необходимо вызвать следующую функцию.

int kmem_cache_destroy(kmem_cache_t *cachep);

Эта функция ликвидирует указанный кэш. Она обычно вызывается при выгрузке модуля, который создает свой кэш. Из контекста прерывания эту функцию вызывать нельзя, так как она может переводить вызывающий процесс в состояние ожидания. Перед вызовом этой функции необходимо, чтобы были выполнены следующие два условия.

• Все слябы кэша являются пустыми. Действительно,

если в каком-либо слябе существует объект, который все еще используется, то как можно ликвидировать кэш?

• Никто не будет обращаться к кэшу во время и особенно после вызова функции

kmem_cache_destroy
. Эту синхронизацию должен обеспечить вызывающий код.

В случае успешного выполнения функция возвращает нуль, в других случаях возвращается ненулевое значение.

После того как кэш создан, из него можно получить объект путем вызова следующей функции.

void* kmem_cache_alloc(kmem_cache_t *cachep, int flags);

Эта функция возвращает указатель на объект из кэша, на который указывает параметр

cachep
. Если ни в одном из слябов нет свободных объектов, то уровень слябового распределения должен получить новые страницы памяти с помощью функции
kmem_getpages
, значение параметра
flags
передается в функцию
__get_free_pages
. Это те самые флаги, которые были рассмотрены ранее. Скорее всего, необходимо указывать
GFP_KERNEL
или
GFP_ATOMIC
.

Далее для удаления объекта и возвращения его в сляб, из которого он был выделен, необходимо использовать следующую функцию.

void kmem_cache_free(kmem_cache_t *cachep, void *objp);

Данная функция помечает объект, на который указывает параметр

objp
, как свободный.

Пример использования слябового распределителя памяти

Давайте рассмотрим пример из реальной жизни, связанный с работой со структурами

task_struct
(дескрипторы процессов). Показанный ниже код в несколько более сложной форме приведен в файле
kernel/fork.c.

В ядре определена глобальная переменная, в которой хранится указатель на кэш объектов

task_struct
:

kmem_cache_t *task_struct_cachep;

Во время инициализации ядра, в функции

fork_init
, этот кэш создается следующим образом.

task_struct_cachep = kmem_cache_create("task_struct",

 sizeof(struct task_struct), ARCH_MIN_TASKALIGN,

 SLAB_PANIC, NULL, NULL);

Данный вызов создает кэш с именем

"task_struct"
, который предназначен для хранения объектов тина
struct task_struct
. Объекты создаются с начальным смещением в слябе, равным
ARCH_MIN_TASKALIGN
байт, и положение всех объектов выравнивается по границам строк системного кэша, значение этого выравнивания зависит от аппаратной платформы. Обычно значение выравнивания задается для каждой аппаратной платформы с помощью определения препроцессора
L1_CACHE_BYTES
, которое равно размеру процессорного кэша первого уровня в байтах. Конструктор и деструктор отсутствуют. Следует обратить внимание, что возвращаемое значение не проверяется на равенство
NULL
, поскольку указан флаг
SLAB_PANIC
. В случае, когда при выделении памяти произошла ошибка, слябовый распределитель памяти вызовет функцию
panic
. Если этот флаг не указан, то нужно проверять возвращаемое значение на равенство
NULL
, что сигнализирует об ошибке. Флаг
SLAB_PANIC
здесь используется потому, что этот каш является необходимым для работы системы (без дескрипторов процессов работать как-то не хорошо).

Каждый раз, когда процесс вызывает функцию

fork
, должен создаваться новый дескриптор
процесса (вспомните главу 3, "Управление процессами"). Это выполняется следующим образом в функции
dup_task_struct
, которая вызывается из функции
do_fork
.

struct task_struct *tsk;

tsk = kmem_cache_alloc(task struct_cachep, GFP_KERNEL);

if (!tsk)

 return NULL;

Когда процесс завершается, если нет порожденных процессов, которые ожидают на завершение родительского процесса, то дескриптор освобождается и возвращается обратно в кэш

task_struct_cachep
. Эти действия выполняются в функции
free_task_struct
, как показано ниже (где параметр
tsk
указывает на удаляемый дескриптор).

kmem_cache_free(task_struct_cachep, tsk);

Так как дескрипторы процессов принадлежат к основным компонентам ядра и всегда необходимы, то кэш

task_struct_cachep
никогда не ликвидируется. Если бы он ликвидировался, то делать это необходимо было бы следующим образом.

int err;

err = kmem_cache_destroy(task_struct_cachep);

if (err)

 /* ошибка ликвидации кэша */

Достаточно просто, не так ли? Уровень слябового распределения памяти скрывает все низкоуровневые операции, связанные с выравниванием, "раскрашиванием", выделением и освобождением памяти, "сборкой мусора" в случае нехватки памяти. Коли часто необходимо создавать много объектов одного типа, то следует подумать об использовании слябового кэша. И уж точно не нужно писать свою реализацию списка свободных ресурсов!

Статическое выделение памяти в стеке

В пространстве пользователя многие операции выделения памяти, в частности некоторые рассмотренные ранее примеры, могут быть выполнены с использованием стека, потому что априори известен размер выделяемой области памяти. В пространстве пользователя доступна такая роскошь, как очень большой и динамически увеличивающийся стек задачи, однако в режиме ядра такой роскоши нет — стек ядра маленький и фиксирован по размеру. Когда процессу выделяется небольшой и фиксированный по размеру стек, то затраты памяти уменьшаются и ядру нет необходимости выполнять дополнительные функции по управлению памятью.

Размер стека зависит как от аппаратной платформы, так и от конфигурационных параметров, которые были указаны на этапе компиляции. Исторически размер стека ядра был равен двум страницам памяти для каждого процесса. Это соответствует 8 Кбайт для 32-разрядных аппаратных платформ и 16 Кбайт для 64-разрядных аппаратных платформ.

В первых версиях ядер серии 2.6 была введена возможность конфигурации, для которой размер стека ядра равен одной странице памяти. Когда устанавливается такая конфигурация, то процесс получает стек, по размеру равный всего одной странице памяти: 4 Кбайт на 32-разрядных аппаратных платформах и 8 Кбайт — на 64-разрядных. Это сделано по двум причинам. Во-первых это уменьшает затраты памяти на одну страницу для каждого процесса. Во-вторых, что наиболее важно, при увеличении времени работы системы (uptime) становится все тяжелее искать две физически смежные страницы памяти. Физическая память становится все более фрагментированной, и нагрузка на систему управления виртуальной памятью при создании новых процессов становится все более существенной.

Существует еще одна сложность (оставайтесь с нами, и Вы узнаете все о стеках ядра). Вся последовательность вложенных вызовов функций в режиме ядра должна поместиться в стеке. Исторически обработчики прерываний используют стек того процесса, выполнение которого они прервали. Это означает, что в худшем случае 8 Кбайт стека должно использоваться совместно всеми вложенными вызовами функций и еще парой обработчиков прерываний. Все это эффективно и просто, но это накладывает еще больше ограничений на использование стека ядра. Когда размер стека сократился до одной страницы памяти, обработчики прерываний туда перестали помещаться.

Поделиться:
Популярные книги

Барон Дубов 5

Карелин Сергей Витальевич
5. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Барон Дубов 5

Маленькая слабость Дракона Андреевича

Рам Янка
1. Танцы на углях
Любовные романы:
современные любовные романы
эро литература
5.25
рейтинг книги
Маленькая слабость Дракона Андреевича

Назад в СССР 5

Дамиров Рафаэль
5. Курсант
Фантастика:
попаданцы
альтернативная история
6.64
рейтинг книги
Назад в СССР 5

Стеллар. Трибут

Прокофьев Роман Юрьевич
2. Стеллар
Фантастика:
боевая фантастика
рпг
8.75
рейтинг книги
Стеллар. Трибут

Город Богов 3

Парсиев Дмитрий
3. Профсоюз водителей грузовых драконов
Фантастика:
юмористическое фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Город Богов 3

Смертельно влюблён

Громова Лиза
Любовные романы:
современные любовные романы
4.67
рейтинг книги
Смертельно влюблён

Маршал Сталина. Красный блицкриг «попаданца»

Ланцов Михаил Алексеевич
2. Маршал Советского Союза
Фантастика:
альтернативная история
8.46
рейтинг книги
Маршал Сталина. Красный блицкриг «попаданца»

Паладин из прошлого тысячелетия

Еслер Андрей
1. Соприкосновение миров
Фантастика:
боевая фантастика
попаданцы
6.25
рейтинг книги
Паладин из прошлого тысячелетия

Метатель. Книга 3

Тарасов Ник
3. Метатель
Фантастика:
попаданцы
альтернативная история
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00
рейтинг книги
Метатель. Книга 3

Счастье быть нужным

Арниева Юлия
Любовные романы:
любовно-фантастические романы
5.25
рейтинг книги
Счастье быть нужным

Звезда сомнительного счастья

Шах Ольга
Фантастика:
фэнтези
6.00
рейтинг книги
Звезда сомнительного счастья

Сын Тишайшего 3

Яманов Александр
3. Царь Федя
Фантастика:
попаданцы
альтернативная история
фэнтези
5.00
рейтинг книги
Сын Тишайшего 3

Сумеречный стрелок 6

Карелин Сергей Витальевич
6. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный стрелок 6

Меч Предназначения

Сапковский Анджей
2. Ведьмак
Фантастика:
фэнтези
9.35
рейтинг книги
Меч Предназначения