Выбрать главу

Функция kfree()

Обратной к функции kmalloc() является функция kfree(), которая определена в файле <linux/slab.h> следующим образом.

void kfree(const void *ptr);

Функция kfree() позволяет освободить память, ранее выделенную с помощью функции kmalloc(). Вызов этой функции для памяти, которая ранее не была выделена с помощью функции kmalloc() или уже была освобождена, приводит к очень плохим последствиям, таким как освобождение памяти, которая принадлежит другим частям ядра. Так же как и в пространстве пользователя, количество операций выделения памяти должно быть равно количеству операций освобождения, чтобы предотвратить утечку памяти и другие проблемы. Следует обратить внимание, что случай вызова kfree(NULL) специально проверяется и поэтому является безопасным.

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

char *buf;

buf = kmalloc(BUF_SIZE, GFP_ATOMIC);

if (!buf)

 /* ошибка выделения памяти! */

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

kfree(buf);

Функция vmalloc()

Функция vmalloc() работает аналогично функции kmalloc(), за исключением того, что она выделяет страницы памяти, которые только виртуально смежные и необязательно смежные физически. Точно так же работают и функции выделения памяти в пространстве пользователя: страницы памяти, которые выделяются с помощью функции malloc(), являются смежными в виртуальном адресном пространстве процесса, но нет никакой гарантии, что они смежные в физической оперативной памяти. Функция kmalloc() отличается тем, что гарантирует, что страницы памяти будут физически (и виртуально) смежными. Функция vmalloc() гарантирует только, что страницы будут смежными в виртуальном адресном пространстве ядра. Это реализуется путем выделения потенциально несмежных участков физической памяти и "исправления" таблиц страниц, чтобы отобразить эту физическую память в непрерывный участок логического адресного пространства.

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

Несмотря на то что физически смежные страницы памяти необходимы только в определенных случаях, большая часть кода ядра использует для выделения памяти функцию kmalloc(), а не vmalloc(). Это, в основном, делается из соображений производительности. Для того чтобы физически несмежные страницы памяти сделать смежными в виртуальном адресном пространстве, функция vmalloc() должна соответствующим образом заполнить таблицы страниц. Хуже того, страницы памяти, которые получаются с помощью функции vmalloc(), должны отображаться посредством страниц памяти, которые принадлежат к таблицам страниц (потому что выделяемые страницы памяти физически несмежные). Это приводит к значительно менее эффективному использованию буфера TLB[64], чем в случае, когда страницы памяти отображаются напрямую. Исходя из этих соображений функция vmalloc() используется только тогда, когда она абсолютно необходима, обычно это делается для выделения очень больших областей памяти. Например, при динамической загрузке модулей ядра, модули загружаются в память, которая выделяется с помощью функции vmalloc().

Функция vmalloc() объявлена в файле <linux/vmalloc.h> и определена в файле mm/vmalloc.с. Использование этой функции аналогично функции malloc() пространства пользователя.

void *vmalloc(unsigned long size);

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

Для освобождения памяти, выделенной с помощью функции vmalloc(), необходимо использовать функцию

void vfree(void *addr);

Эта функция освобождает участок памяти, который начинается с адреса addr и был ранее выделен с помощью функции vmalloc(). Данная функция также может переводить процесс в состояние ожидания и поэтому не может вызываться из контекста прерывания. Функция не возвращает никаких значений.

Использовать рассмотренные функции очень просто. Например, следующим образом.

char *buf;

buf = vmalloc(16 * PAGE_SIZE); /* получить 16 страниц памяти */

if (!buf)

 /* ошибка! Не удалось выделить память */

/*

* переменная buf теперь указывает на область памяти

* размером, по крайней мере, 16*PAGE_SIZE байт, которая состоит

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

*/

После того как память больше не нужна, необходимо убедиться, что она освобождается с помощью следующего вызова.

vfree(buf);

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

Выделение и освобождение структур данных — это одна из наиболее частых операций, которые выполняются в любом ядре. Для того чтобы облегчить процедуру частого выделения и освобождения данных при программировании, вводятся списки свободных ресурсов (free list). Список свободных ресурсов содержит некоторый набор уже выделенных структур данных. Когда коду необходим новый экземпляр структуры данных, он может взять одну структуру из списка свободных ресурсов, вместо того чтобы выделять необходимый объем памяти и заполнять его данными соответствующей структуры. Позже, когда структура данных больше не нужна, она снова возвращается в список свободных ресурсов, вместо того чтобы освобождать память. В этом смысле список свободных ресурсов представляет собой кэш объектов, в котором хранятся объекты одного определенного, часто используемого типа.

Одна из наибольших проблем, связанных со списком свободных ресурсов в ядре, это то, что над ними нет никакого централизованного контроля. Когда ощущается недостаток свободной памяти, нет никакой возможности взаимодействовать между ядром и всеми списками свободных ресурсов, которые в такой ситуации должны уменьшить размер своего кэша, чтобы освободить память. В ядре нет никакой информации о случайно созданных списках свободных ресурсов. Для исправления положения и для универсальности кода ядро предоставляет уровень слябового распределения памяти (slab layer), который также называется просто слябовым распределителем памяти (slab allocator). Уровень слябового распределения памяти выполняет функции общего уровня кэширования структур данных.

вернуться

64

Буфер TLB (translation lookside buffer или буфер быстрого преобразования адреса) — это аппаратный буфер памяти, который используется в большинстве аппаратных платформ для кэширования отображений виртуальных адресов памяти в физические адреса. Этот буфер позволяет существенно повысить производительность системы, так как большинство операций доступа к памяти выполняются с использованием виртуальной адресации.