Введение
Управление памятью (memory management) является важной подсистемой операционной системы Linux, которая обеспечивает эффективное использование ресурсов физической и виртуальной памяти. В Linux управление памятью в основном подразумевает обработку запросов к памяти от процессов, выделение и освобождение блоков памяти, а также обеспечение ее эффективного использования.
Ключевые понятия управления памятью в Linux:
Виртуальная память (Virtual Memory)
Linux использует концепцию виртуальной памяти, которая создает иллюзию наличия у каждого процесса своего личного пространства памяти. Виртуальная память позволяет системе исполнять код приложений, используя больший объем памяти, чем физически доступно. Это достигается путем сброса неиспользуемых блоков памяти приложений на дискСистема страница (Paging)
Физическая и виртуальная память разделены на блоки фиксированного размера, которые называются страницами. Система страниц позволяет эффективно управлять памятью и активирует механизм обмена данными между ОЗУ и диском (swap)Выделение памяти (Memory Allocation)
При выполнении команд процессам требуется память. За выделение процессам подходящих блоков памяти отвечает соответствующий диспетчер. Память выделяется из свободной физической памяти. При необходимости физическая память освобождается сбросом неактивных страниц на дискПространство ядра и пользовательское пространство (Kernel Space and User Space)
Память в Linux подразделяется на пространство ядра и пользовательское пространство. Пространство ядра зарезервировано для исполнения кода ядра, расширений ядра и большинства драйверов устройств. Пользовательское пространство – это область памяти, с которой работают все пользовательские приложенияКеширование (Caching)
Linux использует несколько механизмов кеширования для улучшения производительности системы. Так, например, кеш страниц (page cache) используется для кеширования файлов, читаемых с диска, а кеш буфера (buffer cache) используется для управления операциями записи на дискЧрезмерное выделение памяти (Memory Overcommit)
Linux позволяет выделить процессам больше памяти, чем реально доступно. Эта концепция известна как memory overcommit. Она позволяет большему количество процессов выполняться одновременно при условии, что процессы не используют всю выделенную им память
Как работает виртуальная память
Сегментация памяти (Memory Segmentation)
Когда приложение запускается, ему выделяется диапазон адресов виртуальной памяти. Эта память разделена на блоки (chunks), называемые страницами. Например, если наше приложение запускается и ему выделен диапазон адресов от0x0000
до0xFFFF
, а размер страницы равен0x1000
(значение по умолчанию – 4096 байт), то приложение получает 16 страниц (0x0000–0x0FFF, 0x1000–0x1FFF, …, 0xF000–0xFFFF
)Таблица страниц (Page Tables)
Операционная система сохраняет для каждого процесса структуру данных, которая называется таблицей страниц. Таблица страниц сопоставляет адреса страниц виртуальной памяти с адресами страниц физической памяти. Например, первая страница нашего приложения0x0000–0x0FFF
может быть сопоставлена с физической страницей0x2000–0x2FFF
, вторая страница0x1000–0x1FFF
сопоставлена с физической страницей0x5000–0x5FFF
и так далееДоступ к памяти (Memory Access)
Когда приложение выполняет запрос на чтение или запись к памяти, ЦПУ использует таблицу страниц для трансляции адреса виртуальной памяти в адрес физической. Например, если наше приложение хочет прочитать адрес виртуальной памяти0x1500
, то происходит запрос ко второй странице виртуальной памяти, которая сопоставляется с физической страницей0x5000–0x5FFF
. Таким образом, ЦПУ нужно прочитать реальный адрес физической памяти0x5500
Подкачка и запрос страниц (Swapping and Demand Paging)
Если вся физическая память использована, а приложению требуется загрузить новую страницу в нее, то ОС может выбрать страницу физической памяти для сброса-«подкачки» (swap out) на диск. В таблицу страниц вносится пометка, что данная страница отсутствует в физической памяти. Если позднее приложение попытается получить доступ к адресу выгруженной на диск страницы, то это приведет к ошибке, и операционная система загрузит-«подкачает» (swap in) страницу с диска в физическую память (возможно сбросив при этом на диск другую страницу), после чего приложение сможет получить к ней доступ. Механизм загрузки страниц в физическую память при обращении к ним называется demand paging и позволяет общему объему виртуальной памяти для всех процессов превышать объем фактически доступной физической памяти
Virtual Memory Page Table Physical Memory
┌───────────────┐ ┌────────────────┐ ┌────────────────┐
│ Page 1 │ │ Page 1 │ │ Page 4 │
│ 0x0000-0x0FFF ├───────► │ 0x2000-0x2FFF ├─────────► │ 0x2000-0x2FFF │
├───────────────┤ ├────────────────┤ ├────────────────┤
│ Page 2 │ │ Page 2 │ │ Page 1 │
│ 0x1000-0x1FFF ├───────► │ 0x5000-0x5FFF ├─────────► │ 0x5000-0x5FFF │
├───────────────┤ ├────────────────┤ ├────────────────┤
│ ... │ │ ... │ │ ... │
│ │ │ │ │ │
└───────────────┘ └────────────────┘ └────────────────┘
Translation Lookaside Buffer
Виртуальная память создает иллюзию наличия большего объема памяти, чем физически доступно в вашей системе. Действительно, каждому процессу выделяется значительный объем этой виртуальной памяти. Физическая и виртуальная память разделены на сегменты фиксированной длины, называемые страницами.
Несколько различных процессов с помощью своих таблиц страниц могут сопоставить несколько виртуальных страниц с одной физической страницей. Это наглядно демонстрирует суть управления памятью – требуется эффективно создавать каждому процессу иллюзию присутствия его страницы в ОЗУ, оптимально используя доступное пространство.
Рассмотрим упрощенный пример:
Запускается первый процесс и загружает необходимую программу на страницу в физической памяти. В таблице страниц процесса создается запись, которая сопоставляет адрес программы в виртуальной памяти с ее фактическим адресом в физической памяти
Запускается второй процесс и пытается загрузить ту же программу. Ядро Linux распознает, что программа уже загружена в физическую память первым процессом
Ядро Linux не загружает еще одну копию программы в физическую память. Вместо этого оно создает запись в таблице страниц второго процесса, которая сопоставляет адрес программы в виртуальной памяти с адресом программы в физической памяти, по которому программа уже была загружена
Всякий раз, когда ЦПУ необходимо получить доступ к виртуальной странице процесса, он должен транслировать виртуальный адрес в соответствующий физический адрес. Эта операция может быть многоуровневой и, следовательно, потенциально медленной. Для ускорения работы в современных процессорных архитектурах реализована встроенная функцию поиска – Translation Lookaside Buffer (TLB). TLB по сути является небольшим кешем. В случае отсутствия в нем информации для трансляции адреса виртуального адреса в физический, ЦПУ обращается к таблице страниц процесса и получает физический адрес страницы, после чего добавляет в TLB эту информацию
Давайте рассмотрим пример того, как работает TLB:
Процесс запрашивает доступ к данным, расположенным по определенному адресу виртуальной памяти, например
0x1111
ЦПУ необходимо транслировать этот виртуальный адрес в физический. В первую очередь он проверит наличие информации для трансляции в TLB
Предположим, что в TLB уже есть запись, которая сопоставляет
0x1111
в адрес физической памяти0x2222
ЦПУ может напрямую использовать физический адрес
0x2222
для доступа к данным в физической памяти, минуя обращение к таблице страницДанные в адресе физической памяти
0x2222
могут быть использованы ЦПУ
Исторически Linux работал с размером страниц по умолчанию в 4 КБ. Однако начиная с версии ядра 2.6.3 была внедрена поддержка огромных страниц (huge pages), чтобы эффективнее адаптироваться к современным архитектурным решениям и рабочим нагрузкам.
Так, 64-разрядная версия Linux позволяет использовать до 128 ТБ виртуальной памяти для каждого процесса. Это теоретический максимум объема виртуальных адресов памяти, к которым можно получить доступ. Для физической памяти, которая соответствует установленной ОЗУ в вашем компьютере, 64-разрядный Linux может обработать в общей сложности около 64 ТБ.
В 64-разрядной системе теоретически можно выделить под адресацию 2⁶⁴ байт памяти. Однако в Linux для адресации используются только 48 бит, поэтому максимум составляет 2⁴⁸ байт, или 256 ТБ адресуемой памяти. Память разделена на пространство ядра и пользовательское пространство, причем пользовательское пространство занимает половину объема, что приводит к максимуму в 128 ТБ памяти на каждый процесс.
Ограничение физической памяти в 64 ТБ является результатом не теоретических, а архитектурных и практических ограничений. 64-разрядная адресация теоретически может обрабатывать экзабайты физической памяти, однако текущие возможности аппаратного обеспечения и реализация управления памятью Linux устанавливают практический предел в 64 ТБ.
Команды для анализа использования памяти
free
Команда free – это самая простая и понятная в использовании команда для проверки использования памяти в Linux:
$ free -h
total used free shared buff/cache available
Mem: 15G 4.0G 7.8G 276M 3.2G 10G
Swap: 2.0G 0B 2.0G
top
Команда top показывает потребление ресурсов в режиме реального времени. Она может отображать сводную информацию о системе, а также список задач, которыми в данный момент управляет ядро Linux:
$ top
top - 13:11:34 up 10 days, 21:54, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 193 total, 1 running, 192 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.6 us, 0.2 sy, 0.0 ni, 99.1 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16223716 total, 7942444 free, 4375632 used, 3961640 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 11412212 avail Mem
htop
Команда htop похожа на top, но позволяет прокручивать вывод по вертикали и горизонтали, что обеспечивает удобный просмотр всех процессов, запущенных в системе, вместе с полными строками их команд.
vmstat
Команда vmstat показывает информацию о процессах, памяти, операциям ввода-вывода, работе диска и ЦПУ:
$ vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 7980008 103292 3814528 0 0 34 30 115 228 4 1 95 0 0
/proc/meminfo
Файл /proc/meminfo хранит статистику об использовании памяти в Linux системах:
$ cat /proc/meminfo
MemTotal: 16223716 kB
MemFree: 7940732 kB
MemAvailable: 11412272 kB
Buffers: 103292 kB
Cached: 2817920 kB
SwapCached: 0 kB
Active: 3200428 kB
Inactive: 2256692 kB
В случае обнаружения неточностей в переводе напишите, пожалуйста, в комментарии или мне в личные сообщения – исправим :)