Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внутренние механизмы ядра | Вперед |
Динамическое выделение участка
Динамическое выделение участка памяти размером size байт производится вызовом:
#include <linux/slab.h> void *kmalloc( size_t size, int flags );
Выделенная таким вызовом область памяти является непрерывной в физической памяти. Только некоторые (наиболее используемые флаги):
- GFP_KERNEL - выделение производится от имени процесса, который выполняет системный запрос в пространстве ядра — такой запрос может быть временно переводиться в пассивное состояние (блокирован).
- GFP_ATOMIC - выделения памяти в обработчиках прерываний, тасклетах, таймерах ядра и другом коде, выполняющемся вне контекста процесса — такой не может быть блокирован (нет процесса, который активировать после блокирования). Но это означает, что в случаях, когда память могла бы быть выделена после некоторого блокирования, в данном случае будет сразу возвращаться ошибка.
Эти флаги могут быть совместно (по «или») определены с большим числом других, например таким как:
- GFP_DMA - выделение памяти должно произойти в DMA-совместимой зоне памяти.
Выделенный в результате блок может быть больше размером (что никогда не создаёт проблем пользователю), и ни при каких обстоятельствах не может быть меньше. В зависимости от размера страницы архитектуры, минимальный размер возвращаемого блока может быть 32 или 64 байта, максимальный размер зависит от архитектуры, но если рассчитывать на переносимость, то, утверждается в литературе, это не должно быть больше 128 Кб; но даже уже при размерах больших 1-й страницы (несколько килобайт, для x86 — 4 Кб), есть лучше способы, чем получение памяти чем kmalloc().
После использования всякого блока памяти он должен быть освобождён. Это касается вообще любого способа выделения блока памяти, которые ещё будут рассматриваться. Важно, чтобы освобождение памяти выполнялось вызовом, соответствующим тому способу, которым она выделялась. Для kmalloc() это:
void kfree( const void *ptr );
Повторное освобождение, или освобождение не размещённого блока приводит к тяжёлым последствиям, но kfree( NULL ) проверяется и является совершенно допустимым.
Примечание: Требование освобождения блока памяти после использования — в ядре становится заметно актуальнее, чем в программировании пользовательских процессов: после завершения пользовательского процесса, некорректно распоряжающегося памятью, вместе с завершением процесса системе будут возвращены и все ресурсы, выделенные процессу, в том числе и область для динамического выделения памяти. Память, выделенная модулю ядра и не возвращённая явно им при выгрузке явно, никогда больше не возвратиться под управление системы.
Альтернативным kmalloc() способом выделения блока памяти, но не обязательно в непрерывной области в физической памяти, является вызов:
#include <linux/vmalloc.h> void *vmalloc( unsigned long size ); void vfree( void *addr );
Распределение vmalloc() менее производительно, чем kmalloc(), но может стать предпочтительнее при выделении больших блоков памяти, когда kmalloc() вообще не сможет выделить блок требуемого размера и завершится аварийно. Отображение страниц физической памяти в непрерывную логическую область, возвращаемую vmalloc(), обеспечивает MMU (аппаратная реализация управления таблицами страниц), и для пользователя разрывность физических адресов обычно незаметна и не составляет проблемы (за исключением случаев аппаратного взаимодействия с памятью, самым явным из которых является обмен по DMA).
Ещё одним (итого три) принципиально иным способом выделения памяти будут те вызовы API ялра, которые выделяют память в размере целого числа физических страниц, управляемых MMU: __get_free_pages() и подобные (они все имеют в своих именах суффикс *page*). Такие механизмы будут детально рассмотрены ниже.
Вопрос сравнения возможностей по выделению памяти различными способами актуален, но весьма запутан (по литературным источникам), так как радикально зависит от используемой архитектруры процессора, физических ресурсов оборудования (объём реальной RAM, число процессоров SMP, ...), версии ядра Linux и других факторов. Этот вопрос настолько важен, и заслуживает обстоятельного тестирования, что такие оценки были проделаны для нескольких конфигураций, в виду объёмности сам тест (архив mtest.tgz) и результаты снесены в отдельно приложение, а здесь приведём только сводную таблицу:
Архитектура |
Максимальный выделенный блок* (байт) |
||
---|---|---|---|
kmalloc() |
__get_free_pages() |
vmalloc() |
|
Celeron (Coppermine) - 534 MHz RAM 255600 kB kernel 2.6.18.i686 |
131072 |
4194304 |
134217728 |
Genuine Intel(R), core 2 - 1.66GHz kernel 2.6.32.i686 RAM 2053828 kB |
4194304 |
4194304 |
33554432 |
Intel(R) Core(TM)2 Quad - 2.33GHz kernel 2.6.35.x86_64 RAM 4047192 kB |
4194304 |
4194304 |
2147483648 |
* - приведен размер не максимально возможного для размещения блока в системе, а размер максимального блока в конкретном описываемом тесте: блок вдвое большего размера выделить уже не удалось.
Из таблицы следует, по крайней мере, что в основе каждого из сравниваемых методов выделения памяти лежит свой отдельный механизм (особенно это актуально в отношении kmalloc() и __get_free_pages()), отличающийся от всех других.
Ещё одно сравнение (описано полностью там же, в отдельном приложении) — сравнение по затратам процессорных актов на одно выполнение запроса на выделение:
Размер блока (байт) |
Затраты (число процессорных тактов** , 1.6Ghz) |
||
---|---|---|---|
kmalloc() |
__get_free_pages() |
vmalloc() |
|
5* |
143 |
890 |
152552 |
1000* |
146 |
438 |
210210 |
4096 |
181 |
877 |
59626 |
65536 |
1157 |
940 |
84129 |
262144 |
2151 |
2382 |
52026 |
262000* |
8674 |
4730 |
55612 |
* - не кратно PAGE_SIZE
** - оценки времени, связанные с диспетчированием в системе, могут отличаться в 2-3 раза в ту или иную сторону, и могут быть только грубыми ориентирами порядка величины.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Механизмы управление памятью | Распределители памяти |