Память — это один из самых важных ресурсов компьютера, который необходим для хранения и обработки данных. При работе с операционной системой Linux мы часто сталкиваемся с различными аспектами управления памятью, такими как выделение и освобождение памяти для процессов, подкачка и кэширование данных из файловой системы, мониторинг и оптимизация использования памяти. В этой статье мы рассмотрим основные механизмы и алгоритмы, которые использует Linux для эффективного управления памятью, а также инструменты и команды, которые позволяют нам работать с памятью в Linux.

Виды памяти

Память можно разделить на несколько видов в зависимости от её характеристик и функций. Самый основной вид памяти — это физическая память, которая представляет собой реальные чипы памяти, установленные на материнской плате компьютера. Физическая память имеет ограниченный размер и скорость доступа, поэтому для увеличения объёма доступной памяти и ускорения работы с данными используется другой вид памяти — виртуальная память.

Виртуальная память — это абстракция, которая создаётся операционной системой для того, чтобы каждый процесс имел своё собственное адресное пространство, которое не зависит от физического расположения данных в памяти. Виртуальная память позволяет запускать процессы, размер которых превышает размер физической памяти, а также защищать процессы от взаимного влияния и нежелательного доступа к данным других процессов. Виртуальная память реализуется с помощью механизма подкачки (swapping), который заключается в том, что часть данных из виртуальной памяти временно переносится на диск в специальный файл или раздел, называемый своп (swap). Своп служит как дополнительная память для системы, но имеет гораздо меньшую скорость доступа, чем физическая память.

Основные механизмы управления памятью в Linux

Linux использует несколько механизмов для управления памятью, которые мы рассмотрим далее.

Сегментация адресного пространства процесса

Каждый процесс в Linux имеет своё собственное адресное пространство, которое состоит из нескольких сегментов. Сегменты — это логические части адресного пространства, которые имеют разные свойства и функции. Основные сегменты адресного пространства процесса в Linux — это:

Сегмент текста (text segment) — содержит исполняемый код программы, который обычно располагается в начале адресного пространства. Сегмент текста является доступным только для чтения и может быть разделяемым между несколькими процессами, если они запускают одну и ту же программу.

Сегмент данных (data segment) — содержит глобальные и статические переменные программы, которые инициализируются при запуске программы. Сегмент данных является доступным для чтения и записи и может быть разделяемым между несколькими процессами, если они используют одну и ту же библиотеку или разделяемую память.

Сегмент стека (stack segment) — содержит локальные переменные и параметры функций, а также адреса возврата из функций. Сегмент стека является доступным для чтения и записи и растёт в сторону уменьшения адресов. Каждый процесс имеет свой собственный сегмент стека, который не может быть разделяемым с другими процессами.

Сегмент кучи (heap segment) — содержит динамически выделенную память для программы, которая запрашивается с помощью функций, таких как malloc, calloc, realloc и free. Сегмент кучи является доступным для чтения и записи и растёт в сторону увеличения адресов. Каждый процесс имеет свой собственный сегмент кучи, который не может быть разделяемым с другими процессами.

Страничная подкачка по требованию

Страничная подкачка по требованию (demand paging) — это механизм, который позволяет загружать и выгружать страницы памяти из физической памяти в своп-файл или своп-раздел по мере необходимости. Страница памяти — это блок данных, обычно размером в 4 КБ, который является единицей управления памятью в Linux. Страничная подкачка по требованию работает следующим образом:

Когда процесс обращается к адресу в своём адресном пространстве, операционная система проверяет, есть ли соответствующая страница памяти в физической памяти. Если есть, то происходит обычное чтение или запись данных. Если нет, то возникает исключение, называемое промахом страницы (page fault).

При промахе страницы операционная система ищет нужную страницу памяти в своп-файле или своп-разделе. Если находит, то загружает её в физическую память и обновляет таблицу страниц (page table), которая хранит соответствие между виртуальными и физическими адресами. Если не находит, то значит, что страница памяти ещё не была создана или инициализирована, и тогда операционная система выделяет новую страницу памяти в физической памяти и заполняет её нулями или данными из исполняемого файла или библиотеки.

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

Страничная подкачка по требованию имеет ряд преимуществ, таких как:

Экономия физической памяти: процессы используют только те страницы памяти, которые им действительно нужны в данный момент, а не все сразу.

Защита данных: процессы не могут читать или записывать данные других процессов, так как каждый процесс имеет своё собственное адресное пространство.
Переносимость: процессы могут работать на разных архитектурах и конфигурациях компьютеров, так как операционная система абстрагирует отличия физической памяти.

Страничный кэш

Страничный кэш (page cache) — это механизм, который позволяет хранить часто используемые данные из файловой системы в оперативной памяти для ускорения доступа к ним. Страничный кэш работает следующим образом:

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

Когда процесс закрывает файл или завершается, операционная система не выгружает страницы данных из страничного кэша, а оставляет их в оперативной памяти для возможного повторного использования другими процессами. Если страницы данных были изменены процессом, то операционная система отмечает их как грязные (dirty) и периодически записывает их на диск для синхронизации с файловой системой.

Когда оперативная память заполняется, операционная система освобождает часть памяти, выгружая наименее нужные страницы данных из страничного кэша в своп-файл или своп-раздел или просто удаляя их, если они не были изменены.

Страничный кэш имеет ряд преимуществ, таких как:

Ускорение работы с данными: процессы могут читать и записывать данные из файловой системы гораздо быстрее, чем если бы они обращались к диску каждый раз.
Экономия дискового пространства: процессы могут работать с файлами, размер которых превышает размер своп-файла или своп-раздела, так как данные хранятся в оперативной памяти, а не на диске.

Унификация управления памятью: операционная система использует одинаковые механизмы для управления памятью для процессов и файловой системы, что упрощает реализацию и оптимизацию.

Определение наиболее и наименее нужных страниц памяти — это механизм, который позволяет операционной системе решать, какие страницы памяти выгружать из физической памяти в своп-файл или своп-раздел, когда необходимо освободить место для новых страниц памяти. 

Определение наиболее и наименее нужных страниц памяти работает следующим образом:

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

Для выбора страницы памяти для выгрузки операционная система использует некоторый алгоритм замещения страниц (page replacement algorithm), который опирается на два основных критерия: частоту использования страницы памяти и время последнего использования страницы памяти. Чем чаще и позже используется страница памяти, тем больше вероятность, что она будет использована снова в ближайшем будущем, и тем меньше желательно её выгружать из физической памяти.

Один из самых распространённых алгоритмов замещения страниц — это алгоритм LRU (Least Recently Used), который выбирает для выгрузки ту страницу памяти, которая была использована дольше всех остальных. Алгоритм LRU имеет хорошую производительность, но требует дополнительной памяти для хранения информации о времени последнего использования каждой страницы памяти. Поэтому Linux использует несколько модификаций алгоритма LRU, таких как алгоритм NRU (Not Recently Used), который разделяет страницы памяти на четыре класса в зависимости от того, были ли они изменены (dirty) или использованы (referenced) с момента последней очистки этих битов. Алгоритм NRU выбирает для выгрузки случайную страницу памяти из наименее приоритетного класса.

Освобождение памяти при нехватке свободной памяти

Освобождение памяти при нехватке свободной памяти (memory reclaim) — это механизм, который позволяет операционной системе предотвратить ситуацию, когда вся физическая память и своп-файл или своп-раздел заполнены и невозможно выделить новую страницу памяти для процесса. 

Освобождение памяти при нехватке свободной памяти работает следующим образом:

Когда операционная система обнаруживает, что количество свободной физической памяти или своп-файла или своп-раздела опускается ниже определённого порога, она активирует процесс освобождения памяти, который начинает выгружать наименее нужные страницы данных из страничного кэша в своп-файл или своп-раздел или просто удалять их, если они не были изменены. Процесс освобождения памяти продолжает работать, пока количество свободной памяти не достигнет другого, более высокого порога.

Если процесс освобождения памяти не справляется с освобождением достаточного количества памяти, то операционная система прибегает к крайней мере — запускает алгоритм OOM killer (Out Of Memory killer), который выбирает и убивает один или несколько процессов, которые потребляют больше всего памяти. Алгоритм OOM killer основывается на оценке важности и влияния каждого процесса на систему, которая выражается в виде числового приоритета (oom_score). Чем выше приоритет процесса, тем больше вероятность, что он будет убит алгоритмом OOM killer. Приоритет процесса зависит от нескольких факторов, таких как количество потребляемой памяти, время жизни процесса, принадлежность к определённой группе процессов и другие.

Инструменты и команды для работы с памятью в Linux

Для того, чтобы работать с памятью в Linux, существует множество инструментов и команд, которые позволяют просматривать и изменять параметры управления памятью в Linux. Вот некоторые из них:

Команда free — показывает общее количество физической памяти и своп-файла или своп-раздела, а также их использование и доступность. Команда free имеет несколько опций, таких как -h для вывода данных в человекочитаемом формате, -m для вывода данных в мегабайтах, -g для вывода данных в гигабайтах и другие.

Команда top — показывает информацию о текущих процессах в системе, включая их потребление памяти. Команда top имеет много опций и интерактивных команд, которые позволяют сортировать, фильтровать, убивать и изменять приоритет процессов. Например, для сортировки процессов по потреблению памяти можно нажать Shift+M, для убийства процесса можно нажать k и ввести его PID (идентификатор процесса), для изменения приоритета процесса можно нажать r и ввести его PID и новый приоритет (niceness) и так далее.

Команда vmstat — показывает статистику по использованию памяти и своп-файла или своп-раздела, а также по активности процессора и диска. Команда vmstat имеет несколько опций, таких как -s для вывода общей статистики по системе, -a для вывода статистики по активной и неактивной памяти, -S для выбора единицы измерения памяти (как в команде free) и другие.

Команда ps — показывает информацию о выбранных процессах в системе, включая их потребление памяти. Команда ps имеет много опций, которые позволяют выбирать, какие процессы отображать (например, -e для всех процессов, -u для процессов определённого пользователя и т.д.), какую информацию отображать (например, -о для указания списка полей, которые нужно показать, -f для вывода полного формата и т.д.), и как сортировать результаты (например, --sort для указания поля, по которому нужно сортировать). Например, для того, чтобы показать все процессы в системе с их потреблением памяти в процентах и сортировать их по убыванию этого значения, можно использовать команду ps -e -o pid,comm,%mem --sort=-%mem.

Команда /proc/meminfo — показывает подробную информацию о состоянии памяти в системе, такую как общее количество физической памяти и своп-файла или своп-раздела, количество свободной, занятой, активной, неактивной, кэшированной и других видов памяти. Команда /proc/meminfo не имеет опций, а просто выводит содержимое специального файла /proc/meminfo, который является виртуальным файлом, создаваемым операционной системой.

Команда /proc/sys/vm/ — позволяет просматривать и изменять различные параметры управления памятью в Linux, такие как размер страничного кэша, пороги активации процесса освобождения памяти и алгоритма OOM killer, частота очистки битов изменения и использования страниц памяти и другие. Команда /proc/sys/vm/ не является настоящей командой, а просто обозначает каталог /proc/sys/vm/, в котором находятся виртуальные файлы, соответствующие различным параметрам управления памятью. Для того, чтобы просмотреть значение параметра, нужно прочитать содержимое соответствующего файла. Например, для того, чтобы узнать размер страничного кэша в мегабайтах, нужно выполнить команду cat /proc/sys/vm/pagecache_limit_mb. Для того, чтобы изменить значение параметра, нужно записать новое значение в соответствующий файл. Например, для того, чтобы установить размер страничного кэша в 1024 мегабайтах, нужно выполнить команду echo 1024 > /proc/sys/vm/pagecache_limit_mb.

Команда sysctl — позволяет просматривать и изменять различные параметры ядра Linux, включая параметры управления памятью. Команда sysctl имеет несколько опций, таких как -a для вывода всех параметров ядра, -w для записи нового значения параметра ядра и другие. Например, для того, чтобы просмотреть значение параметра управления памятью vm.pagecache_limit_mb (то же самое, что и /proc/sys/vm/pagecache_limit_mb), можно использовать команду sysctl vm.pagecache_limit_mb. Для того, чтобы изменить значение этого параметра на 1024 мегабайтах (то же самое, что и echo 1024 > /proc/sys/vm/pagecache_limit_mb), можно использовать команду sysctl -w vm.pagecache_limit_mb=1024.

Команды swapon/swapoff — позволяют включать и выключать использование своп-файла или своп-раздела в системе. Команды swapon/swapoff имеют несколько опций, таких как -a для включения или выключения всех своп-файлов или своп-разделов, указанных в файле /etc/fstab, -s для вывода статистики по использованию своп-файла или своп-раздела и другие. Например, для того, чтобы включить использование своп-файла /swapfile, нужно выполнить команду swapon /swapfile. Для того, чтобы выключить использование этого своп-файла, нужно выполнить команду swapoff /swapfile.

Заключение

В этой статье мы рассмотрели основные механизмы и алгоритмы, которые использует Linux для эффективного управления памятью, а также инструменты и команды, которые позволяют нам работать с памятью в Linux. Мы увидели, что Linux использует такие механизмы, как сегментация адресного пространства процесса, страничная подкачка по требованию, страничный кэш, определение наиболее и наименее нужных страниц памяти и освобождение памяти при нехватке свободной памяти. Мы также узнали о таких инструментах и командах, как free, top, vmstat, ps, /proc/meminfo, /proc/sys/vm/, sysctl, swapon/swapoff и другие.