суббота, 5 мая 2018 г.

Работа с памятью в Linux

Одной из наиболее важных и базовых подсистем в любой операционной системе является подсистема работы с оперативной памятью (MMU — Memory Management Unit). Конструкция данной подсистемы является очень сложной, поэтому разработчикам программного обеспечения должны очень хорошо понимать общее устройство как ядра Linux, так и то, как функционируют подобные подсистемы, к счастью все это отлично документировано в исходных кодах ядра.

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

Операционная система занимается задачей распределения доступной памяти для приложений и является некой абстракцией для приложений, так как было бы неудобно каждому приложению обращаться напрямую к адресам памяти. В свою очередь операционная система оперирует понятиями страницы памяти, так как работать напрямую с каждым адресом не удобно. По умолчанию размер страницы в Linux равен 4KB, это сделано из-за того, что очень сложно обращаться за каждым байтом к памяти.
Процесс операционной системы работает с виртуальной памятью, это та память, которая выделяется процессу операционной системой.
Операционная система следит за тем, чтобы выделенные для одного процесса страницы памяти не пересекались со страницами памяти которые были выделены для другого процесса.

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

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

Память можно разделить на две большие группы: файловая и анонимная.
Какой-либо файл или часть файловой системы соответствует понятию файловой памяти. Именно в файлах располагается исполняемый код приложения. С помощью системного вызова mmap приложение обращается к виртуальному адресному пространству память.

Если же памяти не соответствует никакой файл, то такая память называется анонимной. Для выделения анонимной памяти используется функция malloc.

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

В случаях, когда память невозможно вытеснить для работы процесса используется OOM killer. Данных механизм выбирает процесс (как правило, самый объемный) и освобождает его память для работы других процессов, по умолчанию он включен в Linux. Если же отключить OOM killer, то в случае заполнения памяти остается только физически перезагрузить компьютер.

Для изоляции процессов в рамках определенной памяти существует механизм cgroups, который разделяет процессы на логические группы и выделяет им назначенный объем оперативной памяти. Появление технологии cgroups в ядре Linux сыграло большую роль в распространении контейнерных технологий, таких как Docker и OpenVZ.

Ядро Linux также умеет работать с многопроцессорными системами (NUMA — Non-Uniform Memory Access). То есть в случае если процессор физические располагается ближе к оперативной памяти, то в первую очередь он будет обращаться именно к этой части RAM.

Рассмотрим типичный вывод системной информации об оперативной памяти в Linux:
$ sudo cat /proc/meminfo
MemTotal: 3789684 kB
MemFree: 1765616 kB
MemAvailable: 3051416 kB
Buffers: 134112 kB
Cached: 990372 kB
SwapCached: 0 kB
Active: 1190556 kB
Inactive: 579688 kB
Active(anon): 646244 kB
Inactive(anon): 7252 kB
Active(file): 544312 kB
Inactive(file): 572436 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 1936 kB
Writeback: 0 kB
AnonPages: 645756 kB
Mapped: 141264 kB
Shmem: 7740 kB
Slab: 214240 kB
SReclaimable: 197816 kB
SUnreclaim: 16424 kB
KernelStack: 2960 kB
PageTables: 17536 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1894840 kB
Committed_AS: 1601240 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 9668 kB
VmallocChunk: 34359720912 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 79820 kB
DirectMap2M: 2803712 kB
DirectMap1G: 1048576 kB


MemTotal отображает доступный объем оперативной памяти
MemFree указывает объем неиспользуемый объем оперативной памяти
Buffers — область памяти, в которой хранятся данные ожидающие записи на диск, буфер позволяет процессам продолжать работу не дожидаясь физической записи на диск
Cached отображает объем памяти занятый под кэш (файлы, каталоги, файлы блочных устройств и т.д)
SwapCached указывает на объем памяти, который был помещен в swap, а затем перенесен обратно в RAM, при этом данные все еще присутствуют в swap
Active отображает объем памяти, который наиболее часто используется страницами памяти, данные страницы памяти освобождаются довольно редко
Inactive в свою очередь не используются в данный момент, данные страницы являются первоочередными кандидатами для выгрузки в swap в случае необходимости
Unevictable -- страницы памяти, которые по каким-либо причинам не могут быть помещены в swap
Mlocked отображает память, внесенную в адресное пространство с помощью системного вызова mlock
SwapTotal и SwapFree — общий объем и свободный объем swap
Dirty — измененные страницы памяти, которые все еще находятся в RAM, но еще не сброшены на диск
Writeback это память которая в данный момент сбрасывается на диск
AnonPages — объем анонимной памяти
Mapped отображает объем памяти, внесенный в адресное пространство с помощью mmap
Slab — объем памяти выделенный под структуры ядра небольшого объема
PageTables — память выделенная под страничную таблицу
NFS_Unstable указывает на объем памяти используемый для передачи данных по NFS v3+
Bounce — память используемая блочным устройством
CommitLimit — based on the overcommit ratio (vm.overcommit_ratio), this is the total amount of memory currently available to be allocated on the system. This limit is only adhered to if strict overcommit accounting is enabled (mode 2 in vm.overcommit_memory)
Committed_AS — the amount of memory presently allocated on the system. The committed memory is a sum of all of the memory which has been allocated by processes, even if it has not been "used" by them as of yet
VmallocTotal — виртуальное пространство, доступное для vmalloc
VmallocUsed — объем использованного пространства vmalloc
VmallocChunk — наибольший свободный блок внутри vmalloc
HugePages_Total — количество huge страниц выделенных ядром
HugePages_Free — количество huge страниц, которые не выделяются
HugePages_Rsvd — количество huge страниц, которые должны были выделиться из пула, но еще этого не сделали
Hugepagesize — количество huge страниц
DirectMap1G — память выделенная под huge страницы размером 1GB

RHEL 6/7 only:
Shmem указывает объем виртуальной памяти, используемой несколькими процессами
SReclaimable является частью памяти которая может быть reclaimed
SUnreclaim — память которая не может быть reclaimed
KernelStack — память используемая стеком ядра
WritebackTmp объем памяти используемой FUSE для временной записи буферов
HardwareCorrupted является объемом памяти, которую ядро обозначил нерабочей
AnonHugePages — объем памяти занимаемой огромными (huge) анонимными страницами
HugePages_Surp — количество huge страниц в пуле, значение которых выше vm.nr_hugepages
DirectMap4k — память выделенная под стандартные страницы размером 4KB
DirectMap2M — память выделенная под huge страницы размером 2MB

С помощью таких утилит как ps, top, atop, htop и прочих можно проанализировать потребление памяти каждым процессом в системе.
Очистку кэша памяти можно произвести командой:
# free && sync && echo 3 > /proc/sys/vm/drop_caches && free

Комментариев нет: