Не могу понять, почему поле RES в htop / top не соответствует количеству использованной памяти
С помощью системного вызова mmap я попросил ОС выделить мне 4096 * 100000 байт памяти (то есть 100 тыс. страниц). Это 390.625 MiB. htop показывает в столбце VIRT 390M.
Вроде похоже на правду. Я заполняю 8192 байт (2 страницы) первых элементов выделенной мне памяти и RES равен 4. И top и htop показывают одинаковое значение 4 KiB. Как такое возможно? Я заполнил массив на 8 KiB, а получил 4 KiB физической памяти?
Возможно, я как-то неправильно интерпретирую значение RES или неверно пользуюсь mmap?
Вот исходный код:
section .data
SYS_read equ 0
SYS_mmap equ 9
SYS_exit equ 60
PROT_READ equ 0x1
PROT_WRITE equ 0x2
MAP_ANONYMOUS equ 0x0020
MAP_PRIVATE equ 0x0002
EXIT_SUCCESS equ 0
STDIN equ 0
NULL equ 0
PAGE_SIZE equ 4096
section .text
global _start
_start:
; allocate ~390MB of heap memory
mov rax, SYS_mmap
mov rdi, NULL
mov rsi, PAGE_SIZE * 100000
mov rdx, PROT_READ | PROT_WRITE
mov r10, MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1
mov r9, 0
syscall
; fill 2 pages with values
mov rbx, rax
xor rcx, rcx
lbl:
mov byte[rbx + rcx], cl
inc rcx
cmp rcx, 8192
jl lbl
; wait until key pressed
mov rax, SYS_read
mov rdi, STDIN
lea rsi, byte [rbx]
mov rdx, 1
syscall
; exit
mov rax, SYS_exit
mov rdi, EXIT_SUCCESS
syscall
Собираю бинарник так: > yasm -f elf64 sample.asm > ld sample.o
Прошу помочь разобраться! Как минимум я ожидал увидеть значение 8 KiB в столбце RES. Но ведь и сам код занимает место! Значит должно быть больше 8?
UPDATE: в комментариях попросили показать содержимое /proc/$pid/smaps
00400000-00401000 r--p 00000000 08:12 11284375 /home/alex/dev/src/asm/sandbox/a.out
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd mr mw me dw sd
00401000-00402000 r-xp 00001000 08:12 11284375 /home/alex/dev/src/asm/sandbox/a.out
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 4 kB
Private_Dirty: 0 kB
Referenced: 4 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd ex mr mw me dw sd
7fa00207d000-7fa01a71d000 rw-p 00000000 00:00 0
Size: 400000 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 8 kB
Pss: 8 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 8 kB
Referenced: 8 kB
Anonymous: 8 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd wr mr mw me ac sd
7ffeaf308000-7ffeaf329000 rw-p 00000000 00:00 0 [stack]
Size: 132 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 8 kB
Pss: 8 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 8 kB
Referenced: 8 kB
Anonymous: 8 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd wr mr mw me gd ac
7ffeaf3e3000-7ffeaf3e7000 r--p 00000000 00:00 0 [vvar]
Size: 16 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd mr pf io de dd sd
7ffeaf3e7000-7ffeaf3e9000 r-xp 00000000 00:00 0 [vdso]
Size: 8 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd ex mr mw me de sd
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: ex
Ответы (2 шт):
ps/top берут данные из /proc/[pid]/stat (смотрите man ):
Status information about the process. This is used by ps(1).
По полю rss там написано: This value is inaccurate; see /proc/[pid]/statm below
А там пишут:
Some of these values are inaccurate because of a kernel- internal scalability optimization. If accurate values are required, use /proc/[pid]/smaps or /proc/[pid]/smaps_rollup instead, which are much slower but provide accurate, detailed information.
Очевидно точность в top принесена в жертву скорости.
ps/top/htop просто показывают данные из файлов /proc/PID/stat, statm и status. В них показывается информация, собираемая ядром. Однако в linux начиная с 2.6.34 значения RSS для всего процесса обновляется не при каждом запросе/освобождении памяти, а только когда количество событий (в основном страничных прерываний) для данного потока превысит определённый предел (конкретно говоря — 64). Как подмечено в документации ядра:
For making accounting scalable, RSS related information are handled in an asynchronous manner and the value may not be very precise. To see a precise snapshot of a moment, you can see /proc//smaps file and scan page table. It’s slow but very precise.
Вольный перевод:
Для того, чтобы сделать подсчёты более масштабируемыми, информация, связанная с RSS обрабатывается асинхронно, так что эти значения могут быть не очень точны. Чтобы получить точные значения, вы можете обратиться к файлу
/proc/<pid>/smapsи произвести подсчёт по таблице страниц. Это медленно, зато очень точно.
Как утверждается в оригинальном коммите, такой подход уменьшает количество кеш-промахов на страничное прерывание при агрессивном обращении к памяти из двух потоков с 4.5 до 4.0.
Таким образом, максимальная ошибка, которую может показывать значение RSS в /proc/PID/stat (а также в ps/top/htop) составляет до 256К (64 страницы * 4K) на каждый поток.