Minimum çalıştırılabilir örnek
Bunun anlaşılması için, çağrı yapmanın temellerini anlamanız gerekir: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work ve özellikle işletim sisteminin sanal tabloları sayfa tabloları aracılığıyla tahsis edebilmesi / RAM veya diskte (RSS yerleşik bellek) bir yedekleme deposuna sahip olmadan önce dahili bellek defteri (VSZ sanal belleği) tutması.
Şimdi bu eylemi gözlemlemek için, hadi bir program yaratalım:
main.c
#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
typedef struct {
unsigned long size,resident,share,text,lib,data,dt;
} ProcStatm;
/* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c/7212248#7212248 */
void ProcStat_init(ProcStatm *result) {
const char* statm_path = "/proc/self/statm";
FILE *f = fopen(statm_path, "r");
if(!f) {
perror(statm_path);
abort();
}
if(7 != fscanf(
f,
"%lu %lu %lu %lu %lu %lu %lu",
&(result->size),
&(result->resident),
&(result->share),
&(result->text),
&(result->lib),
&(result->data),
&(result->dt)
)) {
perror(statm_path);
abort();
}
fclose(f);
}
int main(int argc, char **argv) {
ProcStatm proc_statm;
char *base, *p;
char system_cmd[1024];
long page_size;
size_t i, nbytes, print_interval, bytes_since_last_print;
int snprintf_return;
/* Decide how many ints to allocate. */
if (argc < 2) {
nbytes = 0x10000;
} else {
nbytes = strtoull(argv[1], NULL, 0);
}
if (argc < 3) {
print_interval = 0x1000;
} else {
print_interval = strtoull(argv[2], NULL, 0);
}
page_size = sysconf(_SC_PAGESIZE);
/* Allocate the memory. */
base = mmap(
NULL,
nbytes,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1,
0
);
if (base == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
/* Write to all the allocated pages. */
i = 0;
p = base;
bytes_since_last_print = 0;
/* Produce the ps command that lists only our VSZ and RSS. */
snprintf_return = snprintf(
system_cmd,
sizeof(system_cmd),
"ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == \"%ju\") print}'",
(uintmax_t)getpid()
);
assert(snprintf_return >= 0);
assert((size_t)snprintf_return < sizeof(system_cmd));
bytes_since_last_print = print_interval;
do {
/* Modify a byte in the page. */
*p = i;
p += page_size;
bytes_since_last_print += page_size;
/* Print process memory usage every print_interval bytes.
* We count memory using a few techniques from:
* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c */
if (bytes_since_last_print > print_interval) {
bytes_since_last_print -= print_interval;
printf("extra_memory_committed %lu KiB\n", (i * page_size) / 1024);
ProcStat_init(&proc_statm);
/* Check /proc/self/statm */
printf(
"/proc/self/statm size resident %lu %lu KiB\n",
(proc_statm.size * page_size) / 1024,
(proc_statm.resident * page_size) / 1024
);
/* Check ps. */
puts(system_cmd);
system(system_cmd);
puts("");
}
i++;
} while (p < base + nbytes);
/* Cleanup. */
munmap(base, nbytes);
return EXIT_SUCCESS;
}
GitHub yukarı akış .
Derleyin ve çalıştırın:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
sudo dmesg -c
./main.out 0x1000000000 0x200000000
echo $?
sudo dmesg
nerede:
Program çıktısı:
extra_memory_committed 0 KiB
/proc/self/statm size resident 67111332 768 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
PID VSZ RSS
29827 67111332 1648
extra_memory_committed 8388608 KiB
/proc/self/statm size resident 67111332 8390244 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
PID VSZ RSS
29827 67111332 8390256
extra_memory_committed 16777216 KiB
/proc/self/statm size resident 67111332 16778852 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
PID VSZ RSS
29827 67111332 16778864
extra_memory_committed 25165824 KiB
/proc/self/statm size resident 67111332 25167460 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
PID VSZ RSS
29827 67111332 25167472
Killed
Çıkış durumu:
137
hangi 128 + sinyal numarası kuralı biz sinyal numarası var demektir 9
, man 7 signal
diyor sigkill Linux tarafından gönderilen, dışı belleğe katil .
Çıktı yorumu:
- VSZ sanal belleği mmap'tan sonra
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
( ps
değerler KiB cinsindendir) sabit kalır .
- RSS "gerçek bellek kullanımı" yalnızca sayfalara dokunduğumuzda tembel bir şekilde artmaktadır. Örneğin:
- İlk baskıda biz var
extra_memory_committed 0
, bu da henüz hiçbir sayfaya dokunmadığımız anlamına geliyor. RSS, 1648 KiB
metin alanı, globals, vb. Normal program başlangıcı için tahsis edilmiş küçük bir sayıdır.
- İkinci baskıda,
8388608 KiB == 8GiB
sayfalara değecek kadar yazdık. Sonuç olarak, RSS tam olarak 8GIB arttı8390256 KiB == 8388608 KiB + 1648 KiB
- RSS 8GiB artışlarla artmaya devam ediyor. Son baskı yaklaşık 24 GiB hafıza gösteriyor ve 32 GiB basılmadan önce OOM katili işlemi öldürdü
Ayrıca bakınız: Resident Set Size / Virtual Size hakkında bir açıklama yapmalısınız
OOM katil günlükleri
Bizim dmesg
komutları OOM katil günlükleri göstermiştir.
Bunların tam olarak yorumlanması istendi:
Kütüğün ilk satırı şuydu:
[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Bu yüzden ilginç bir şekilde, dizüstü bilgisayarımda her zaman arka planda çalışan OOM katilini tetikleyen MongoDB arka plan programı olduğunu görüyoruz, muhtemelen kötü bir şey biraz bellek ayırmaya çalışırken.
Ancak, OOM katilinin onu uyandıran kişiyi öldürmesi şart değildir.
Çağrının ardından, çekirdek aşağıdakileri içeren bir tablo veya işlemleri yazdırır oom_score
:
[ 7283.479292] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
[ 7283.479303] [ 496] 0 496 16126 6 172032 484 0 systemd-journal
[ 7283.479306] [ 505] 0 505 1309 0 45056 52 0 blkmapd
[ 7283.479309] [ 513] 0 513 19757 0 57344 55 0 lvmetad
[ 7283.479312] [ 516] 0 516 4681 1 61440 444 -1000 systemd-udevd
ve daha ileride görüyoruz ki main.out
, önceki istilada kendi küçüklerimiz gerçekten öldürüldü:
[ 7283.479871] Out of memory: Kill process 15665 (main.out) score 865 or sacrifice child
[ 7283.479879] Killed process 15665 (main.out) total-vm:67111332kB, anon-rss:92kB, file-rss:4kB, shmem-rss:30080832kB
[ 7283.479951] oom_reaper: reaped process 15665 (main.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:30080832kB
Bu kayıtta, score 865
bu işlemin hangisi olduğu, muhtemelen en yüksek (en kötü) OOM katili puanının belirtildiği gibi olduğu belirtilmektedir: OOM katili hangi süreci ilk öldüreceğine nasıl karar verir?
Ayrıca ilginç olarak, her şey görünüşte o kadar hızlı gerçekleşti ki, serbest kalan bellek hesaba katılmadan önce oom
, DeadlineMonitor
süreç tekrar uyandı :
[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
ve genellikle bilgisayarlarımın normal hafızası olan bazı Chromium işlemlerini öldürdüm:
[ 7283.481773] Out of memory: Kill process 11786 (chromium-browse) score 306 or sacrifice child
[ 7283.481833] Killed process 11786 (chromium-browse) total-vm:1813576kB, anon-rss:208804kB, file-rss:0kB, shmem-rss:8380kB
[ 7283.497847] oom_reaper: reaped process 11786 (chromium-browse), now anon-rss:0kB, file-rss:0kB, shmem-rss:8044kB
Ubuntu 19.04, Linux çekirdeği 5.0.0'da test edilmiştir.