Книга: Разработка ядра Linux
Пример использования слябового распределителя памяти
Пример использования слябового распределителя памяти
Давайте рассмотрим пример из реальной жизни, связанный с работой со структурами 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)
/* ошибка ликвидации кэша */
Достаточно просто, не так ли? Уровень слябового распределения памяти скрывает все низкоуровневые операции, связанные с выравниванием, "раскрашиванием", выделением и освобождением памяти, "сборкой мусора" в случае нехватки памяти. Коли часто необходимо создавать много объектов одного типа, то следует подумать об использовании слябового кэша. И уж точно не нужно писать свою реализацию списка свободных ресурсов!
- Пример установочного скрипта
- Пример из практики
- ПРИМЕР ПРОСТОЙ ПРОГРАММЫ НА ЯЗЫКЕ СИ
- Примеры получения статистики
- Пример применения метода «пять почему»
- Пример 12-8. Частота встречаемости отдельных слов
- 1.2.5. Пример программы
- 3.2.1.2. Начальное выделение памяти: malloc()
- Пример 17-10. Блочный комментарий
- Примеры
- 2. Пример создания базового отношения в записи на псевдокоде
- Пример 9-8. Содержимое $* и $@, когда переменная $IFS -- пуста