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

ЖАНРЫ

Разработка ядра Linux
Шрифт:
Флаги типов

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

Таблица 11.5. Флаги типов

Флаг Описание
GFP_ATOMIC
Запрос на выделение памяти высокоприоритетный и в состояние ожидания переходить нельзя. Этот флаг предназначен для использования в обработчика: прерываний, нижних половин и в других ситуациях, когда нельзя переходить в состояние ожидания
GFP_NOIO
Запрос на выделение памяти может блокироваться, но при его выполнении нельзя выполнять операции дискового ввода-вывода.
Этот флаг предназначен для использования в коде блочного ввода-вывода, когда нельзя инициировать новые операции ввода-вывода
GFP_NOFS
Запрос на выделение памяти может блокироваться и выполнять дисковые операции ввода-вывода, но запрещено выполнять операции, связанные с файловыми системами. Этот флаг предназначен для использования в коде файловых систем, когда нельзя начинать выполнение новых файловых операций
GFP_KERNEL
Обычный запрос на выделение памяти, который может блокироваться. Этот флаг предназначен для использования в коде, который выполняется в контексте процесса, когда безопасно переходить в состояние ожидания
GFP_USER
Обычный запрос на выделение памяти, который может блокироваться. Этот флаг используется для выделения памяти процессам пространства пользователя
GFP_HIGHUSER
Запрос на выделение памяти из зоны
ZONE_HIGHMEM
, который может блокироваться. Этот флаг используется для выделения памяти процессам пространства пользователя
GFP_DMA
Запрос на выделение памяти из зоны
ZONE_DMA
. Драйверам устройств, которым нужна память для выполнения операций по ПДП, необходимо использовать этот флаг обычно в комбинации с одним из описанных выше флагов

Таблица 11.6. Список модификаторов, соответствующих каждому флагу типа

Флаг Модификаторы
GFP_ATOMIC
__GFP_HIGH
GFP_NOIO
__GFP_WAIT
GFP_NOFS
(__GFP_WAIT | __GFP_IO)
GFP_KERNEL
(__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_USER
(__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_HIGHUSER
(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HIGHMEM)
GFP_DMA
__GFP_DMA

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

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

Можно сказать, что свойства флага

GFP_ATOMIC
лежат на противоположном конце спектра. Так как этот флаг указывает, что операция выделения памяти не может переходить в состояние ожидания, то такая операция очень ограничена в том, какую память можно использовать для выделения. Если нет доступного участка памяти заданного размера, то ядро, скорее всего, не будет пытаться освободить память, поскольку вызывающий код не может переходить в состояние ожидания. При использовании флага
GFP_KERNEL
, наоборот, ядро может перевести вызывающий код в состояние ожидания, чтобы во время ожидания вытеснить страницы на диск (swap out), очистить измененные страницы памяти путем записи их в дисковый файл (flush dirty pages) и т.д. Поскольку при использовании флага
GFP_ATOMIC
нет возможности выполнить ни одну из этих операций, то и шансов успешно выполнить выделение памяти тоже меньше (по крайней мере, когда в системе недостаточно памяти). Тем не менее использование флага
GFP_ATOMIC
— это единственная возможность, когда вызывающий код не может переходить в состояние ожидания, как в случае обработчиков прерываний и нижних половин.

По своим свойствам между рассмотренными флагами находятся флаги

GFP_NOIC
и
GFP_NOFS
. Операции выделения памяти, которые запущены с этими флагами, могут блокироваться, но они воздерживаются от выполнения некоторых действий. Выделение памяти с флагом
GFP_NOIO
не будет запускать никаких операций дискового ввода-вывода. С другой стороны, при использовании флага
GFP_NOFS
могут запускаться операции дискового ввода-вывода, но не могут запускаться операции файловых систем. Когда эти флаги могут быть
полезны? Они соответственно необходимы для определенного низкоуровневого кода блочного ввода-вывода или кода файловых систем. Представьте себе, что в некотором часто используемом участке кода файловых систем используется выделение памяти без указания флага
GFP_NOFS
. Если выделение памяти требует выполнения операций файловой системы, то выделение памяти приведет к еще большему количеству операций файловой системы, которые потребуют дополнительного выделения памяти и еще большего количества файловых операций! При разработке кода, который использует выделение памяти, необходимо гарантировать, что операции выделения памяти не будут использовать этот код, как в рассмотренном случае, иначе может возникнуть самоблокировка. Не удивительно, что и ядре рассматриваемые флаги используются только в небольшом количестве мест.

Флаг

GFP_DMA
применяется для указания, что система выделения памяти должна при выполнении запроса предоставить память из зоны
ZONE_DMA
. Этот флаг используется драйверами устройств, для которых необходимо выполнение операций прямого доступа к памяти. Обычно этот флаг должен комбинироваться с флагами
GFP_ATOMIC
или
GFP_KERNEL
.

В подавляющем большинстве случаев при разработке кода вам будет необходимо использовать флаги

GFP_ATOMIC
или
GFP_KERNEL
. В табл. 11.7 показано какие флаги и в каких ситуациях необходимо использовать. Независимо от типа операции выделения памяти, необходимо проверять результат и обрабатывать ошибки.

Таблица 11.7. Какой флаг и когда необходимо использовать

Ситуация Решение
Контекст процесса, можно переходить в состояние ожидания Используется флаг
GFP_KERNEL
Контекст процесса, нельзя переходить в состояние ожидания Используется флаг
GFP_ATOMIC
или память выделяется с использованием флага
GFP_KERNEL
но в более ранний или поздний момент, когда можно переходить в состояние ожидания
Обработчик прерывания Используется флаг
GFP_ATOMIC
Обработка нижней половины Используется флаг
GFP_ATOMIC
Необходима память для выполнения операций ПДП, можно переходить в состояние ожидания Используются флаги
(GFP_DMA | GFP_KERNEL)
Необходима память для выполнения операций ПДП, нельзя переходить в состояние ожидания Используются флаги
(GFP_DMA | GFP_ATOMIC)
или выделение выполняется в более поздний или более ранний момент времени

Функция

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
гарантирует только, что страницы будут смежными в виртуальном адресном пространстве ядра. Это реализуется путем выделения потенциально несмежных участков физической памяти и "исправления" таблиц страниц, чтобы отобразить эту физическую память в непрерывный участок логического адресного пространства.

Поделиться с друзьями: