Эта функция возвращает указатель на объект из кэша, на который указывает параметр 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()
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)
/* ошибка ликвидации кэша */
Достаточно просто, не так ли? Уровень слябового распределения памяти скрывает все низкоуровневые операции, связанные с выравниванием, "раскрашиванием", выделением и освобождением памяти, "сборкой мусора" в случае нехватки памяти. Коли часто необходимо создавать много объектов одного типа, то следует подумать об использовании слябового кэша. И уж точно не нужно писать свою реализацию списка свободных ресурсов!
Статическое выделение памяти в стеке