4.10 Уменьшение размера памяти, занимаемого ядром

Предыдущая  Содержание  Следующая V*D*V

В этом разделе описываются методы для уменьшения размера памяти, используемого ядром. Ядро Linux не участвует в подкачке и, следовательно, всё ядро (код, данные и стек) всегда находится в оперативной памяти. Прежде чем углубляться в методы оптимизации, давайте разберёмся, как оценить объём памяти, используемый ядром. Размер статической памяти, которая будет использоваться ядром, можно определить используя команду size; эта утилита выводит список размеров различных секций и общий размер объектного файла. Ниже приведён вывод для ядра, скомпилированного для процессора MIPS.

 

bash >mips-linux-size vmlinux

text        data     bss     dec      hex    filename

 621244   44128     128848  794220   c1e6c    vmlinux

 

Показанная выше распечатка показывает, что 621 Кб памяти используется текстом ядра, 44 Кб данными и 128 Кб BSS. Обратите внимание, что BSS не является частью образа хранения ядра; код запуска выделяет память для BSS и заполняет его 0, таким образом, эффективно создавая его во время выполнения.

Следующая часть полезной информации отображается во время запуска Linux:

 

Memory: 61204k/65536k available (1347k kernel code, 4008k

reserved, 999k data, 132k init, 0k highmem)

 

Данное сообщение показывает, что из 65 536 Кб памяти, присутствующей в системе, около 4 Мб было использовано для хранения разделов текста ядра, кода и инициализации, и для создания структур данных для управления памятью. Остальные 61 Мб доступны ядру для динамического распределения памяти.

Отчёт о расходе памяти в системе во время работы можно получить через /proc/meminfo. Пример вывода для ядра версии 2.4:

 

# cat /proc/meminfo

 

        total:    used:    free:  shared: buffers:  cached:

Mem:  62894080 47947776 14946304        0  4964352 23674880

Swap:        0        0        0

MemTotal:        61420 Kb

MemFree:         14596 Kb

MemShared:           0 Kb

Buffers:          4848 Kb

Cached:          23120 Kb

SwapCached:          0 Kb

Active:          32340 Kb

ActiveAnon:      10760 Kb

ActiveCache:     21580 Kb

Inact_dirty:      6336 Kb

Inact_clean:       236 Kb

Inact_target:     7780 Kb

HighTotal:           0 Kb

HighFree:            0 Kb

LowTotal:        61420 Kb

LowFree:         14596 Kb

SwapTotal:           0 Kb

SwapFree:            0 Kb

 

Важными являются поля used и free. Остальная информация о том, как расходуют память различные кэши системы (буферы, страницы и так далее). В случае, если читателю интересно узнать эти подробности, он может обратиться к документации, доступной вместе с ядром Linux. Файл Documentation/proc.txt имеет раздел, который объясняет каждое поле, отображаемое /proc/meminfo.

Теперь давайте рассмотрим методы оптимизации использования памяти ядром.

 

Сокращение структур со статическим выделением памяти для данных: структуры со статическим выделением памяти для данных находятся либо в секции .data, либо в секции .bss. Многие из таких задействованных структур данных не имеют опции для настройки (это сделало бы процесс сборки очень сложным) и, следовательно, остаются с размером по умолчанию, который следовало бы изменить для встраиваемых систем. Некоторые из них перечислены ниже.
- Количество консолей TTY по умолчанию (MAX_NR_CONSOLES и MAX_NR_USER_CONSOLES), определённое в include/linux/tty.h как 63.
- Размер буфера журнала консоли LOG_BUF_LEN, определённый в kernel/printk.c как 16 Кб.
- Количество символьных и блочных устройств (MAX_CHRDEV и MAX_BLKDEV), определённое в включать include/linux/major.h

Файл System.map: файл System.map, сгенерированный при сборке ядра, может быть полезным источником информации в этом отношении. Этот файл содержит адреса для каждого символа; разница между последовательными адресами даст размер символа, который является либо текстом, либо данными. Все крупные структуры данных представляют собой цели для исследования. Для получения размеров различных символов  в образе ядра можно также использовать команду nm с опцией --size.

Сокращение неиспользуемого кода в ядре: код ядра может быть проверен, чтобы удалить неиспользуемые модули и функции. Обратите внимание, что методы, обсуждаемые для уменьшения ядра в разделе, посвящённом оптимизации пространства, занимаемого ядром, справедливы и здесь.

Неправильное использование kmalloc: kmalloc представляет собой универсальный распределитель памяти ядра. Драйверы устройств обычно используют kmalloc() для динамического выделения памяти. kmalloc() работает с кэшем объектов, которые кратны 32-м байтам; так что любое выделение памяти, размер которой лежит между двумя последовательными кратными 32-м байтам величинами, приведёт к внутренней фрагментации. Предположим, что субмодуль выполняет kmalloc размером 80 байт; он получает их выделенными из объекта размером 128 байт и, следовательно, 48 байт при выделении памяти тратятся впустую. Если ваш субмодуль или драйвер делает много таких выделений, то много памяти тратится впустую. Средством решения этой проблемы является создание собственных кэшей, из которых можно выделять объекты точно требуемого размера. Это делается с помощью следующих двух шагов:
- Создание кэша, связанного с объектом slab (кусок памяти), с помощью kmem_cache_create(). (Чтобы уничтожить кеш, вызовите kmem_cache_destroy().)
- Создание объекта, связанного с кэшем, с помощью kmem_cache_alloc(). (Функцией освобождения памяти является kmem_cache_free().)

Использование директивы __init: раздел .init содержит все функции, которые могут быть выброшены после инициализации ядра. Как правило, все функции инициализации находятся в секции .init. Если вы включаете в ядро свой собственный драйвер или модуль, определите разделы, которые должны быть использованы только один раз во время запуска системы и поместите их в секцию .init с помощью директивы __init. Это будет гарантировать, что когда все функции секции .init выполнены, некоторая часть памяти, занимаемая ядром, вернётся обратно в систему.

Сокращение дыр в физической памяти: дыры в физической памяти являются обычным явлением во встроенных системах. Иногда дизайн платы или процессора не позволяет всей физической памяти быть смежной, создавая тем самым дыры. Однако, большие дыры в физической памяти могут приводить к потере памяти. Так происходит потому, что каждые 4 Кб физической памяти (страница) требуют для обслуживания 60-ти байтовой структуры данных page_struct. Если есть большая дыра, то память для этих структур выделена без необходимости, и страницы, которые находятся в области дыры, помечены как неиспользуемые. Чтобы предотвратить это, может быть использована поддержка CONFIG_DISCONTIGMEM, предоставляемая ядром Linux .

XIP: XIP или "eXecute In Place, выполнение на месте" является технологией, благодаря которой программа выполняется непосредственно из флеш-памяти; нет необходимости копировать программу из флеш-памяти, чтобы выполнить её. Помимо уменьшения требований к памяти, это также уменьшает время запуска, поскольку ядро не должно быть распаковано или скопировано в оперативную память. Обратной стороной является то, что в файловой системе, где хранится ядро, не может быть использовано сжатие и, следовательно, требуется иметь много флеш-памяти. Однако, использование XIP в файловых системах имеет ограниченное применение для приложений, так как код обычно разделён и загружается по требованию (подкачка по требованию). XIP более популярен в uClinux из-за нехватки виртуальной памяти в системах на нём. XIP подробно обсуждается в Главе 10.

 

Предыдущая  Содержание  Следующая