Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра 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 раза в ту или иную сторону, и могут быть только грубыми ориентирами порядка величины.


Предыдущий раздел: Оглавление Следующий раздел:
Механизмы управление памятью   Распределители памяти