Книга: Разработка ядра Linux
Флаги gfp_mask
Разделы на этой странице:
Флаги gfp_mask
Выше были показаны различные примеры использования флагов, которые модифицируют работу системы выделения памяти, как при вызове низкоуровневых функций, работающих на уровне страниц, так и при использовании функции kmalloc()
. Теперь давайте рассмотрим их более детально.
Флаги разбиты на три категории: модификаторы операций, модификаторы зон и флаги типов. Модификаторы операций указывают, каким образом ядро должно выделять указанную память. В некоторых ситуациях только некоторые методы могут использоваться для выделения памяти. Например, обработчики прерываний могут потребовать от ядра, что нельзя переходить в состояние ожидания при выделении памяти (поскольку обработчики прерывания не могут быть перепланированы), Модификаторы зоны указывают, откуда нужно выделять память. Как было рассказано, ядро делит физическую память на несколько зон, каждая из которых служит для различных целей. Модификаторы зоны указывают, из какой зоны выделять память. Флаги типов представляют собой различные комбинации модификаторов операций и зон, которые необходимы для определенного типа выделения памяти. Флаги типов содержат в себе различные модификаторы, вместо которых можно просто использовать один флаг типа. Флаг GFP_KERNEL
— это флаг типа, который используется кодом, выполняющимся в ядре в контексте процесса. Рассмотрим все флаги отдельно.
Модификаторы операций
Все флаги, включая модификаторы операций, определены в заголовочном файле <linux/gfp.h>
. Подключение файла <linux/slab.h>
также подключает и этот заголовочный файл, поэтому его не часто приходится подключать явно. На практике обычно лучше использовать флаги типов, которые будут рассмотрены дальше. Тем не менее полезно иметь представление об индивидуальных флагах. В табл. 11.3 показан список модификаторов операций.
Таблица 11.3. Модификаторы операций
Флаг | Описание |
---|---|
__GFP_WAIT |
Операция выделения памяти может переводить текущий процесс в состояние ожидания |
__GFP_HIGH |
Операция выделения памяти может обращаться к аварийным запасам |
__GFP_IO |
Операция выделения памяти может использовать дисковые операции ввода-вывода |
__GFP_FS |
Операция выделения памяти может использовать операции ввода- вывода файловой системы |
__GFP_COLD |
Операция выделения памяти должна использовать страницы памяти, содержимое которых не находится в кэше процессора (cache cold) |
__GFP_NOWARN |
Операция выделения памяти не будет печатать сообщения об ошибках |
__GFP_REPEAT |
Операция выделения памяти повторит попытку выделения в случае ошибки |
__GFP_NOFAIL |
Операция выделения памяти будет повторять попытки выделения неопределенное количество раз |
__GFP_NORETRY |
Операция выделения памяти никогда не будет повторять попытку выделения памяти |
__GFP_NO_GROW |
Используется внутри слябового распределителя памяти (slab layer) |
__GFP_COMP |
Добавить метаданные составной (compound) страницы памяти. Используется кодом поддержки больших страниц памяти (hugetlb) |
Описанные модификаторы можно указывать вместе, как показано в следующем примере.
ptr = kmalloc(size, __GFP_WAIT | __GFP_IO | __GFP_FS);
Этот код дает инструкцию ядру (а именно функции alloc_pages()
), что операция выделения памяти может быть блокирующей, выполнять операции ввода-вывода и операции файловой системы, если это необходимо. В данном случае ядру предоставляется большая свобода в отношении того, где оно будет искать необходимую память, чтобы удовлетворить запрос.
Большинство запросов на выделение памяти указывают эти модификаторы, но это делается косвенным образом с помощью флагов типа, которые скоро будут рассмотрены. Не нужно волноваться, у вас не будет необходимости каждый раз разбираться, какие из этих ужасных флагов использовать при выделении памяти!
Модификаторы зоны
Модификаторы зоны указывают, из какой зоны должна выделяться память. Обычно выделение может происходить из любой зоны. Однако ядро предпочитает зону ZONE_NORMAL
, чтобы гарантировать, что в других зонах, когда это необходимо, есть свободные страницы.
Существует всего два модификатора зоны, поскольку, кроме зоны ZONE_NORMAL
(из которой по умолчанию идет выделение памяти), существует всего две зоны. В табл. 11.4 приведен список модификаторов зоны.
Таблица 11.4. Модификаторы зоны
Флаг | Описание |
---|---|
__GFP_DMA |
Выделять память только из зоны ZONE_DMA |
__GFP_HIGHMEM |
Выделять память только из зон ZONE_HIGHMEM и ZONE_NORMAL |
Указание одного из этих флагов изменяет зону, из которой ядро пытается выделить память. Флаг __GFP_DMA
требует, чтобы ядро выделило память только из зоны ZONE_DMA
Этот флаг эквивалентен следующему высказыванию в форме жесткого требования: "Мне абсолютно необходима память, в которой можно выполнять операции прямого доступа к памяти". Флаг __GFP_HIGHMEM
, наоборот, требует, чтобы выделение памяти было из зон ZONE_NORMAL
и ZONE_HIGHMEM
(вторая более предпочтительна). Этот флаг эквивалентен запросу: "Я могу использовать верхнюю память, но мне на самом деле, все равно, и делайте, что хотите, обычная память тоже подойдет".
Если не указан ни один из флагов, то ядро пытается выделять память из зон ZONE_NORMAL
и ZONE_DMA
, отдавая значительное предпочтение зоне ZONE_NORMAL
.
Флаг __GFP_HIGHMEM
нельзя укалывать при вызове функций __get_free_pages()
или kmalloc()
. Это связано с тем, что они возвращают логический адрес, а не структуру page, и появляется возможность, что эти функции выделят память, которая в данный момент не отображается в виртуальное адресное пространство ядра и поэтому не имеет логического адреса. Только функция alloc_pages()
может выделять страницы в верхней памяти. Однако в большинстве случаев в запросах на выделение памяти не нужно указывать модификаторы зоны, так как достаточно того, что используется зона ZONE_NORMAL
.
Флаги типов
Флаги типов указывают модификаторы операций и зон, которые необходимы для выполнения запросов определенных типов. В связи с этим, в коде ядра стараются использовать правильный флаг типа и не использовать больших наборов модификаторов. Это одновременно и проще и при этом меньше шансов ошибиться. В табл. 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) или выделение выполняется в более поздний или более ранний момент времени |
- Флаги форматирования строк .NET
- 10.6.3. Управление маской сигналов: sigprocmask() и др.
- 9.10.5 CANSetMask
- 11.2. Layer Mask (Маска слоя)
- Флаги областей VMA
- Управляющие флаги дескриптора безопасности
- Флаги Международного свода сигналов
- 10.1.6. Флаги
- Элемент MaskedTextBox
- 6.6.2 Signal masks
- Значения umask
- 16.5.6. Управляющие флаги