Можно сказать, что свойства флага 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)или выделение выполняется в более поздний или более ранний момент времени |
Обратной к функции kmalloc()является функция kfree(), которая определена в файле следующим образом.
void kfree(const void *ptr);
Функция kfree()позволяет освободить память, ранее выделенную с помощью функции kmalloc(). Вызов этой функции для памяти, которая ранее не была выделена с помощью функции kmalloc()или уже была освобождена, приводит к очень плохим последствиям, таким как освобождение памяти, которая принадлежит другим частям ядра. Так же как и в пространстве пользователя, количество операций выделения памяти должно быть равно количеству операций освобождения, чтобы предотвратить утечку памяти и другие проблемы. Следует обратить внимание, что случай вызова kfree(NULL)специально проверяется и поэтому является безопасным.
Читать дальше