- Как анализировать вывод /proc/meminfo в Linux
- Основные показатели
- Прочие показатели
- Статистика памяти
- Память процесса Linux
- Виртуальная память
- Виртуальная и резидентная память процесса
- Отображение файлов в память
- Карта памяти процесса
- Системные вызовы mmap/munmap и brk— выделение и высвобождение памяти
- Потребление памяти
- Распределение памяти по назначению
- Статистика использования памяти
- Потребление памяти процессами
Как анализировать вывод /proc/meminfo в Linux
Файловая система /proc — это виртуальная файловая система. Она не хранится на диске, а создается в памяти и используется для предоставления информации о системе (первоначально о процессах, отсюда и название).
Из /proc/meminfo можно получить информацию о свободной памяти, об используемой (и физической, и swap), а также о разделяемой (shared memory) и буферах.
Подробно файловая система /proc описана в man.
Сначала посмотрим показатели, на которые следует обращать внимание в первую очередь.
Основные показатели
MemTotal : доступный объем оперативной памяти (физическая память за вычетом нескольких зарезервированных битов и бинарного кода ядра).
MemFree : Сумма LowFree и HighFree.
MemShared : 0; приведен здесь из соображений совместимости и всегда равен нулю.
Buffers : память в буферном кеше. В настоящее время бесполезен в качестве метрики. Это временное хранилище для страниц, ожидающих записи на диск. Не должно быть слишком большим (обычно около 20 МБ).
Cached : память в кэше страниц за вычетом SwapCache.
SwapCache: память, которая когда-то была выгружена в своп, но потом загружена обратно и все еще находится в файле подкачки. Если будет необходимость в выделении памяти, то эту память не нужно будет выгружать повторно, так как она уже находится в свопе. Это экономит операции ввода-вывода.
Прочие показатели
Cтраницы кэша делятся на «активные» и «неактивные». Идея заключается в том, что если вам нужна память и ее можно взять из кэша, то она будет забрана из неактивных страниц, поскольку ожидается, что она больше не будет использоваться. Подсистема виртуальной памяти постоянно отслеживает, какая память используется, и отмечает это в таблице страниц (pagetable) специальным битом.
Страницы также могут перемещаться обратно в активные. Активные страницы упорядочены в порядке «не использовалась дольше всех» (хотя это очень грубо и в реальности все немного сложнее). Давно не использованные страницы можно переместить в неактивные. В приведенном выше примере неактивная память разделена на две части. Иногда она разбивается на три.
Active : память, которая использовалась совсем недавно. Обычно не освобождается без крайней необходимости.
Inact_dirty : «грязная» означает, что «необходима запись на диск или в своп». Для ее освобождения требуется больше работы. Здесь могут быть файлы, которые еще не записаны на диск. Операции записи на диск происходят не сразу, чтобы не снижать производительность ввода-вывода. Например, если вы пишете логи, то, возможно, лучше подождать, пока у вас будет готова полная запись лога, прежде чем отправлять ее на диск.
Inact_clean: память, которую можно легко освободить. Ядро пытается сохранить немного чистых страниц, чтобы было проще «дышать».
Inact_target : целевая метрика, которую ядро использует, чтобы убедиться, в достаточном количестве неактивных страниц. При превышении этого значения ядро не будет перемещать страницы из активного состояния в неактивное. Страница может стать неактивной разными способами. Например, если вы выполняете длительный последовательный ввод-вывод, то ядро предполагает, что вы не собираетесь использовать эту память, и делает ее неактивной превентивно. Таким образом, вы можете получить больше неактивных страниц, чем целевое значение, потому что ядро помечает некоторый кэш как «скорее всего, никогда не будет использоваться» и позволяет обмануть порядок «использовался последним».
Статистика памяти
HighTotal : размер области верхней памяти. Область верхней памяти (highmem) — это вся память, выше (приблизительно) 860 МБ физической ОЗУ. Доступ к этой памяти осуществляется через косвенные механизмы доступа. Здесь может находиться кэш данных.
LowTotal: общий объем памяти без highmem-памяти.
LowFree : объем свободной памяти в нижней области памяти. Это память, к которой ядро может обращаться напрямую. Все структуры данных ядра должны находиться этой области.
SwapTotal : общий физический размер свопинга.
SwapFree : количество свободной памяти в свопинге. Swap — память, которая была выгружена из ОЗУ и временно находится на диске.
Dirty : память, ожидающая записи на диск.
Writeback: память, которая в настоящий момент записывается на диск.
Mapped : отображаемые в память файлы с помощью mmaped, например, библиотеки.
Slab : кеш внутренних структур ядра.
Committed_AS: оценка объема оперативной памяти, необходимой для 99,99% гарантии того, что для текущей нагрузки системы не будет OOM (out of memory, нехватки памяти). Обычно ядро позволяет выделять больше памяти, чем доступно в системе (overcommit). Например, при выделении 1 ГБ памяти через malloc, на самом деле ничего не происходит. Только когда вы начнете ИСПОЛЬЗОВАТЬ эту память, вам выделят столько памяти, сколько вы будете использовать. То есть вы берете ипотеку и надеетесь, что банк не разорится. Также могут быть случаи, когда вы используете mmap файл, который используется только при записи в него, и вы получаете приватную копию этих данных. Хотя обычно они разделяются между процессами. Committed_AS — это приблизительная оценка того, сколько памяти / свопинга вам понадобится в худшем случае.
PageTables : объем памяти, выделенный для таблиц страниц.
ReverseMaps : количество выполненных обратных отображений.
VmallocTotal : общий размер области памяти vmalloc.
VmallocUsed : используемая память vmalloc.
VmallocChunk : самый большой свободный непрерывный блок в области vmalloc.
Оценка использования ресурсов, особенно потребления памяти, намного сложнее, чем может показаться на первый взгляд. Неиспользуемый ресурс — это ресурс, потраченный впустую. Поэтому ядро старается использовать столько оперативной памяти, сколько может для кэширования информации с локальных и удаленных файловых систем и дисков. Кэш наполняется постепенно по мере выполнения операций чтения и записи, пытаясь сохранить данные, хранящиеся в ОЗУ, как можно более актуальными для процессов, запущенных в системе. При наличии свободной оперативной памяти будет выполняться больше кэширования и, следовательно, будет «потребляться» больше памяти. Однако на самом деле это не считается использованием ресурсов, поскольку эта память с кэшем доступна для процессов в любой момент. Кэш освобождается не при завершении процесса (возможно, появится другой процесс, которому нужны те же данные), а по запросу.
При запуске процесса, требующему для работы много памяти, ядро ОС освобождает память, в которой хранились кэшированные данные, и отдает ее новому процессу.
Обычно системной информации о памяти можно доверять, но даже она может быть легко истолкована неверно. Например, посмотрим на top.
Что может сбить с толку, так это буфер и кэш:
Можно увидеть, что используется 16124948K, но почти все эти 15 ГБ используются под кэш и буфер, и в случае, если память потребуется другому процессу, то она будет немедленно освобождена.
Система использует 2448184 КБ (= 2 ГБ) для приложений / процессов, при этом потенциально свободно 13 ГБ, используемой для буферизации и кэширования. Для оценки потребления памяти процессом, вы должны использовать команду free до, во время и после запуска процесса, проделывая это несколько раз, чтобы получить среднее значение оцениваемого показателя.
Для получения информации о потреблении памяти конкретного процесса можно использовать следующие команды:
Всех, кто хочет с нуля научиться управлять серверами и развиваться как системный / инфраструктурный инженер или DevOps-инженер приглашаем на страницу курса для более подробного ознакомления с программой и процессом обучения.
Источник
Память процесса Linux
Еще одним ресурсом, подлежащим распределению между процессами, является оперативная память. В Linux, как и во многих других современных операционных системах, для управления памятью используют механизм страничного отображения, реализуемого ядром операционной системы при помощи устройства управления памятью — W:[MMU]. При этом процессы работают с виртуальными адресами (virtual address) «воображаемой» памяти, отображаемыми устройством MMU на физические адреса (physical address) настоящей оперативной памяти.
Для отображения вся оперативная память (RAM) условно разбивается на «гранулы» — страничные кадры размером в 4 Кбайт, которые затем выделяются процессам. Таким образом, память процесса условно состоит из страниц (page), которым в специальных таблицах страниц (page table) сопоставлены выделенные страничные кадры (page frame).
При выполнении процесса преобразование его виртуальных адресов в физические выполняется устройством MMU «на лету» при помощи его. индивидуальной таблицы страниц.
Именно механизм страничного отображения позволяет эффективно распределять память между процессами путем размещения страниц процессов в произвольные свободные страничные кадры. Кроме этого, механизм страничного отображения позволяет выделять процессам память по требованию, добавляя дополнительные страницы и отображая их на свободные страничные кадры. Аналогично, ненужные процессу страницы могут быть удалены, а соответствующие страничные кадры высвобождены для использования другими процессами.
Виртуальная память
Помимо задачи распределения памяти между процессами, механизм страничного отображения используется ядром операционной системы и для решения задачи нехватки оперативной памяти. При определенных обстоятельствах имеющиеся в распоряжении свободные страничные кадры оперативной памяти могут быть исчерпаны. Одновременно с этим оказывается, что большую часть времени процессы используют лишь малую часть выделенной им памяти, а находясь в состоянии сна, не используют память вовсе.
Увеличить коэффициент полезного использования памяти позволяет еще одна простая идея — высвобождать страничные кадры при помощи выгрузки (page out) неиспользуемых страниц процессов во вторичную память (в специальную область «подкачки» SWAP, например, на диске), а при обращении к выгруженной странице — загружать (page in) ее обратно перед использованием.
За счет такого страничного обмена (paging или page swapping) организуется W:[виртуальная память], т. е. видимость большего количества (оперативной) памяти для размещения процессов, чем есть на самом деле.
В примере из листинга ниже в столбцах VSZ и RSS вывода команды ps показано потребление памяти процессами (в килобайтах). В столбце VSZ (virtual size) указывается суммарный объем всех страниц процесса (в том числе и выгруженных), а в столбце RSS (resident set size) — суммарный объем всех его страничных кадров в оперативной памяти, т. е. ее реальное потребление процессом.
Виртуальная и резидентная память процесса
$ ps fu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
fitz 18690 0.0 0.0 10308 5432 pts/1 Ss 20:51 0:00 bash
fitz 19393 2.8 1.7 609744 143316 pts/1 Sl 21:27 0:11 \_ /usr/…/firefox
fitz 19526 0.0 0.0 6104 700 pts/1 R+ 21:33 0:00 \_ ps fu
Отображение файлов в память
Страничный обмен, помимо организации виртуальной памяти, имеет еще одно важнейшее применение. Именно на его основе реализуется незаменимый механизм отображения файлов в память процесса, доступный при помощи системных вызовов mmap/munmap (и дополнительных mlock, mprotect, msync, madvise и др.).
Для отображения файла в память процесса ему выделяют страницы в необходимом количестве, но не страничные кадры. Вместо этого, в таблице страниц формируются такие записи, как будто эти страницы уже были выгружены ранее в отображаемый файл.
При последующем обращении (on demand) процесса к какой-либо странице отображенной памяти, под нее выделяют страничный кадр и заполняют (read) соответствующим содержимым файла. Любые последующие изменения, сделанные процессом в отображенных страницах, сохраняются обратно (write back) в файл, если отображение выполнено «разделяемым» (shared) способом. Для страниц, отображенных «частным» (private) способом, используется принцип COW (copy-on-write), согласно которому любые попытки их изменения (write) приводят к созданию их копий (сору), куда и попадают изменения.
Таким образом, страницы отображенного файла, которые никогда не были востребованы процессом, не будут вовсе занимать оперативной памяти. Это обстоятельство широко используется для «загрузки» в процесс его программы и библиотек. В листинге ниже при помощи команды pmap показана карта (отображения файлов) памяти процесса командного интерпретатора bash.
Карта памяти процесса
$ ps fu
PID TTY STAT TIME COMMAND
26958 pts/0 Ss 0:00 bash
28540 pts/0 R+ 0:00 \_ ps f
[email protected]:
$ which bash
$ readelf -l /bin/bash
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0X000000 0X08048000 0x08048000 0xdb0c8 0xdb0c8 R E 0x1000
LOAD 0x0dbf04 0x08124f04 0x08124f04 0x04870 0x09820 RW 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x0dbf04 0x08124f04 0x08124f04 0x000fc 0x000fc R 0 0x1
[email protected]:
$ pmap -d 26958
26958: bash
Address Kbytes Mode Offset Device Mapping
08048000 880 r-x— 0000000000000000 0fc:00000 bash
08124000 4 r—- 00000000000db000 0fc:00000 bash
08125000 20 rw— 00000000000dc000 0fc:00000 bash
0812a000 20 rw— 0000000000000000 000:00000 [ anon ]
086b9000 3676 rw— 0000000000000000 000:00000 [ anon ]
b753be000 1676 r-x— 0000000000000000 0fc:00000 libc-2.15.so
b76de000 8 r—- 00000000001a3000 0fc:00000 libc-2.15.so
b76e0000 4 rw— 00000000001a5000 0fc:00000 libc-2.15.so
b76e1000 16 rw— 0000000000000000 000:00000 [ anon ]
bfe78000 132 rw— 0000000000000000 000:00000 [ stack ]
mapped: 10312K writeable/private: 3908K shared: 240K
В память процесса интерпретатора отображен исполняемый ELF-файл его программы и ELF-файлы всех библиотек, от которых она зависит. Отображение ELF-файлов выполняется частями — сегментами (при помощи readelf можно получить их список), в зависимости от их назначения. Так, например, сегмент программного кода отображен в страницы, доступные на чтение r и выполнение x, сегмент данных отображен в страницы, доступные на чтение r и запись w, и т. д.
Более того, выделение страниц памяти по требованию в процессе работы процесса реализуется при помощи «воображаемого» отображения некоторого несуществующего, «анонимного файла» [anon] на страничные кадры. Необходимо отметить, что механизм виртуальной памяти при освобождении неиспользуемых страниц выгружает в специальную область подкачки SWAP только «анонимные» страничные кадры и «анонимизированные», полученные копированием при изменении (согласно принципу COW).
Неанонимные измененные кадры выгружаются непосредственно в соответствующие им файлы, а неизмененные освобождаются вовсе без выгрузки, т. к. уже «заранее выгружены».
В примере из листинга ниже иллюстрируются два способа выделения памяти по требованию: явный — при помощи системного вызова mmap с аргументом MAP_ANONYMOUS, и неявный (Доставшийся в наследство от классической ОС UNIX) — при помощи системного вызова brk. Явный способ позволяет выделять новые сегменты памяти процесса, тогда как неявный способ изменяет размер предопределенного «сегмента данных» процесса, позволяя увеличивать и уменьшать его по желанию, перемещая так называемый «break» — адрес конца этого сегмента.
Системные вызовы mmap/munmap и brk— выделение и высвобождение памяти
$ ldd hostname
libnsl.so.1 => /lib/i386-linux /llb/i386-linux-gnu/libc.so.6 (0xb7526000)
/lib/ld-linux. so.2 (0xb770a000)
$ strace hostname
execve(«/bin/hostname», [«hostname»], [/* 43 vans */]) = 0
access(«/etc/ld.,so.nohwcap», F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYNOMOUS, -1, 0) = 0xb770e000
access( «/etc/ld. so. preload», R_OK) = -1 ENOENT (No such file or directory)
open(«/etc/ld.so.cache», O_RDONLY|O_CLOEXEC) = 3
mmap2(NULL, 122191, PROT_READ, MAP_PRIVATE, 3, 0) = 0bdo76f000
access(«/etc/ld.so.nowcap», F_OK) = -1 ENOENT (No such Ale or directory)
open(«/lib/1386-linux 6, base_addr:0xb752b6c0, limit: 1048575, seg_32bit:1, contents: 0, read_exec_only: 0, limit_in_pages: 1, seg_not_present: 0, useable: 1>) = 0
mprotect(oxb76d0000, 8192, PROT_READ) = 0
mprotect(0xb76eo000, 4096, PROT_READ) = 0
mprobect(0x804b000, 4096, PROT_READ) = 0
mprotect(0xb7731000, 4096, PROT_READ) = 0
munmap(0xb76f0000, 122101) = = 0 ↑ /lib/ld-linux.so.2
brk(0) = 0x9e62000 ↓ /bin/hostname
mmap2(NULL, 4096, PROT_READ| PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb770d000 wrlte(1, «ubuntu\n», 7ubuntu
Трасса команды hostname, показанная в листинге выше, поясняет работу загрузчика и компоновщика, (loader, ld) динамических библиотек ld-linux.
Системный вызов exec отображает для запуска в память процесса не только заданный ELF-файл /bin/hostname, но и (указанный в этом ELF-файле) загрузчик библиотек /lib/ld-linux.so.2, которому и передается управление до самой программы.
Загрузчик библиотек, в свою очередь, отображает в процесс , свой «конфигурационный» файл /etc/ld.so.cache, а затем посегментно отображает файлы всех библиотек и выделяет им требуемую дополнительную память.
Загруженные библиотеки присоединяются (линкуются или же компонуются, linking), к программе /bin/hostname, после чего страницам их отображенных сегментов назначается ©Ф соответствующий режим доступа системным вызовом mprotect. По завершении компоновки отображение конифгурационного файла /etc/ld.so.cache снимается при помощи munmap, а управление передается исходной программе.
Потребление памяти
Суммарное распределение страниц памяти по сегментам процесса можно получить при помощи третьего набора столбцов (активировав его клавишами № команды top, как показано в листинге ниже. В столбце VIRT изображается суммарный объем (в килобайтах) всех страниц процесса, а в столбце RES — объем резидентных «страниц (находящихся в страничных кадрах оперативной памяти). В столбце SWAP указывается объем всех страниц, находящихся во вторичной памяти — как «анонимных» страниц, выгруженных в специальную область подкачки, так и «файловых» страниц, возможно, никогда не загружавшихся в оперативную память.
Столбцы CODE и DATA показывают объемы (в килобайтах) памяти,, выделенной, под сегменты кода и данных, а столбец SHR — объем резидентных страниц, которые используются (или могут быть использованы) совместно с другими процессами.
Распределение памяти по назначению
$ top -р 26958
top — 11:05:01 up 15 days, 47 min, 8 users, load average: 0.27, 0.51, 0.51
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
Cpu(s): 8.6%us, 2.4%sy, 0.0%ni, 88.6%id, 0.4%wa, 0.0%hi, 0.0%si, 0.0%si
Mem: 8192144k total, 7037720k used, 1154424k free, 238984k buffers
Swap: 4104188k total, 35376k used, 4068812k free, 4356372k cached
PID %МЕМ VIRT SWAP RES CODE DATA SHR nFLT nDRT S PR NI %CPU COMMAND
26958 0.1 10288 4936 5352 880 3852 1544 0 0 S 20 0 0 bash
Механизм отображения считывает содержимое файла в страничные кадры только один раз, вне зависимости от количества процессов, отображающих этот файл в свою память. В случае отображения одного файла разными процессами их страницы совместно отображаются на одни и те же страничные кадры, за исключением страниц, скопированных (согласно принципу COW) при изменении.
Такое поведение механизма отображения позволяет эффективно использовать оперативную память за счет использования разными программами одинаковых разделяемых библиотек. Так как ELF-файлы библиотек «загружаются* в память процессов при помощи отображения mmap, то в результате каждая библиотека размещается в оперативной памяти лишь единожды, вне зависимости от количества ее «использований».
Интегральная статистика по использованию виртуальной памяти может быть получена при помощи команды free, как показано в листинге ниже.
Статистика использования памяти
$ free -m
total used free shared buffers cached
Mem: 8000 5523 2476 0 219 3430
-/+ buffers/cache: 1873 6126
Swap: 4007 17 3990
Строка Mem: содержит статистику использования оперативной памяти, а строка Swap: — статистику специальной области подкачки. В столбце total указан суммарный объем всех доступных страничных кадров, а в столбцах used и free — суммарные объемы использованных и свободных страничных кадров, соответственно.
В столбце cached указан объем страничного кэша (page cache), т. е. суммарный объем страничных кадров оперативной памяти, использованных под отображение файлов в память. Аналогично, в столбце buffers указывается объем буферного кэша, т. е. суммарный объем памяти, использованной ядром для кэширования «не-отображаемых» сущностей: метаданных файлов, дисковых блоков при прямом вводе-выводе на устройства и пр.
В столбцах used и free строки -/+ buffers/cache указываются объемы использованной и свободной памяти «за вычетом» страничного и буферного кэшей, т. е. «чистая» память, выделенная процессам по требованию под данные, сгенерированные в процессе работы.
В листинге ниже показан пример потребления памяти процессом текстового редактора vi при попытке редактирования громадного файла в 1 Гбайт. Процесс загружает файл целиком, в результате чего он целиком оказывается в резидентных страницах сегмента данных процесса, что естественным образом увеличивает «чистый» расход оперативной памяти системы. После принудительного завершения процесса при помощи команды kill память естественным образом высвобождается.
Потребление памяти процессами
pts/0
$ dd if=/dev/urandom of=big bs=4069 count=262144
262144+0 записей получено
262144+0 записей отправлено
скопировано 1073741824 байта (1,1 GВ), 148,956 с, 7,2 МВ/с
$ ls -lh big
-rw-r—r— 1 fitz fttz 1,0G дек. 3 12:26 big
$ free -m
total used free shared buffers cached
Mem: 8000 3947 4052 0 42 2712
-/+ buffers/cache: 1192 6807
Swap: 4007 0 4007
$ vi big
pts/1
$ ps f
PID TTY STAT TIME COMMAND
20595 pts/1 S 0:00 -bash
21087 pts/1 R+ 0:00 \_ ps f
20437 pts/0 S 0:00 -bash
21085 pts/0 Rl+ 0:08 \_ vi big
$ ps up 21685
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
fitz 21085 96.4 21.8 1826416 1786444 pts/0 Sl+ 21:14 0:18 vi big
$ free -m
total used free shared buffers cached
Mem: 8000 5687 2312 0 42 2709
-/+ buffers/cache: 2935 5065
Swap: 4007 0 4007
$ top -b -n1 -p 21085
top — 21:14:43 up 3:56, 3 users, load average: 0.53, 0.40, 0.47
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
Cpu(s): 6.0%us, 5.89%sy, 0.2%ni, 83.7%id, 4.3%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 8192144k total, 5826856k used, 2365288k free, 43932k buffers
Swap: 4104188k total, 0k used, 4104188k free, 2777504k cached
PID %МЕМ VIRT SWAP RES CODE DATA SHR nFLT nDRT S PR NI %CPU COMMAND
21085 21.8 1783m 39m 1.7g 2148 1.7g 5676 0 0 S 20 0 0 vi
$ kill 21085
$ free -m
total used free shared buffers cached
Mem: 8000 3945 4054 0 42 2709
-/+ buffers/cache: 1193 6806
Swap: 4007 0 4007
Источник