Компакция памяти в ядре Linux - это механизм, который перемещает страницы памяти для создания непрерывных блоков свободной памяти. Когда система работает длительное время, память становится фрагментированной: свободные страницы разбросаны по всей адресной зоне, и хотя их общее количество может быть достаточным, они не образуют непрерывного блока нужного размера. Компакция перемещает занятые страницы, чтобы освободить непрерывные области памяти для важных выделений больших блоков, таких как огромные страницы или страницы для устройств DMA.
Разберём архитектуру компакции, механизмы её запуска, влияние на производительность и практические приёмы настройки.
Что такое компакция памяти и зачем она нужна
Бадди-аллокатор ядра Linux выделяет страницы блоками размером 2^n страниц (порядки от 0 до MAX_ORDER). Для обычных страниц размером 4 КБ это не проблема, так как даже отдельные страницы подходят. Но для огромных страниц (huge pages) размером 2 МБ или 1 ГБ требуются непрерывные блоки из нескольких страниц. Когда память фрагментирована, большие блоки недоступны, даже если общее количество свободной памяти велико.
Компакция решает эту проблему: она перемещает выделенные страницы по адресному пространству, чтобы освободить непрерывные области памяти. Это позволяет ядру выделять огромные страницы и страницы для устройств DMA, которым требуются непрерывные физические адреса, даже когда общая свободная память фрагментирована.
Компакция работает в фоновом режиме и по требованию. Когда выделение большого блока не удаётся из-за фрагментации, ядро может запустить компакцию, чтобы освободить нужный блок. Также ядро может запускать компакцию проактивно, чтобы поддерживать запас свободных крупных блоков.
Как работает компакция памяти
Механизм компакции работает по следующему принципу:
Сначала ядро выбирает целевую зону памяти и порядок (размер блока) для компакции, затем сканирует зону в поисках подходящих страниц для перемещения. Найденные страницы перемещаются в другие свободные места, освобождая целевые блоки, после чего обновляются таблицы страниц и счётчики, чтобы отразить новые местоположения страниц.
Компакция работает с определенными типами страниц. Не все страницы могут быть перемещены: некоторые страницы зафиксированы в памяти (например, страницы ядра, страницы с закрепленными страницами, страницы устройств DMA), и их нельзя переместить. Ядро выбирает для перемещения только те страницы, которые можно безопасно переместить.
Процесс компакции может быть дорогостоящим с точки зрения производительности, так как он требует копирования страниц и обновления таблиц страниц. Поэтому ядро использует различные стратегии для минимизации влияния на производительность, включая ограничение количества одновременно компактируемых страниц и приоритизацию важных зон памяти.
Типы страниц и миграция
Ядро Linux классифицирует страницы по типам миграции, которые определяют, как они могут быть перемещены при компакции:
Неподвижные страницы (unmovable) не могут быть перемещены: страницы ядра, страницы с закреплённой памятью, страницы устройств DMA. Восстанавливаемые страницы (reclaimable) могут быть освобождены и пересозданы: кэш страниц, буферы ядра. Перемещаемые страницы (movable) свободно перемещаются: страницы пользовательского пространства, анонимные страницы.
При компакции ядро сначала пытается переместить подвижные страницы, затем восстанавливаемые страницы, и только в крайнем случае неподвижные страницы. Это минимизирует влияние на производительность и снижает риск потери данных.
Когда происходит компакция
Компакция может происходить в нескольких ситуациях:
При выделении больших блоков, когда запрос не удаётся из-за фрагментации, ядро запускает компакцию, чтобы освободить нужный блок. Проактивная компакция запускается ядром для поддержания запаса свободных крупных блоков и управляется параметром vm.compaction_proactiveness. При нехватке памяти ядро запускает компакцию, чтобы освободить больше свободных блоков для выделения. Администратор может вручную запустить компакцию через запись в файл /proc/sys/vm/compact_memory.
Компакция может быть дорогостоящей с точки зрения производительности, поэтому ядро использует различные стратегии для минимизации её влияния, включая ограничение количества одновременно компактируемых страниц и приоритизацию важных зон памяти.
Настройка компакции через sysfs
Ядро предоставляет несколько параметров для настройки компакции через sysfs:
Параметр vm.compaction_proactiveness управляет агрессивностью проактивной компакции: значение от 0 до 100, где 0 означает отключение проактивной компакции, а 100 - максимальную агрессивность, со значением по умолчанию 20. Запись единицы в файл /proc/sys/vm/compact_memory запускает ручную компакцию всех зон памяти.
Пример настройки проактивной компакции: