न्यूनतम रननीय उदाहरण
इसे समझने के लिए, आपको पेजिंग की मूल बातों को समझना होगा: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work और विशेष रूप से ओएस पेज टेबल के माध्यम से वर्चुअल मेमोरी आवंटित कर सकता है। इसकी आंतरिक मेमोरी बुक कीपिंग (VSZ वर्चुअल मेमोरी) इससे पहले कि वास्तव में रैम या डिस्क (RSS निवासी मेमोरी) पर बैकिंग स्टोरेज हो।
अब इस क्रिया को देखने के लिए, आइए एक कार्यक्रम बनाएं:
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;
}
गिटहब ऊपर ।
संकलित करें और चलाएं:
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
कहाँ पे:
कार्यक्रम का उत्पादन:
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
बाहर निकलने की स्थिति:
137
जिसमें 128 + सिग्नल नंबर नियम का मतलब है कि हमें सिग्नल नंबर मिला है 9
, जो man 7 signal
कहता है कि SIGKILL है , जो लिनक्स आउट-ऑफ-मेमोरी किलर द्वारा भेजा जाता है ।
आउटपुट व्याख्या:
- VSZ वर्चुअल मेमोरी mmap के बाद
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
( ps
मान KiB में हैं) पर स्थिर रहती है ।
- RSS "वास्तविक मेमोरी उपयोग" केवल पृष्ठों को छूने पर ही आलस्य बढ़ता है। उदाहरण के लिए:
- पहले प्रिंट पर, हमारे पास है
extra_memory_committed 0
, जिसका अर्थ है कि हमने अभी तक किसी भी पृष्ठ को नहीं छुआ है। RSS एक छोटा सा हिस्सा है 1648 KiB
जिसे सामान्य प्रोग्राम स्टार्टअप जैसे टेक्स्ट एरिया, ग्लोबल्स आदि के लिए आवंटित किया गया है।
- दूसरे प्रिंट पर, हमने
8388608 KiB == 8GiB
पृष्ठों के लायक लिखा है । नतीजतन, RSS ठीक 8GIB तक बढ़ गया8390256 KiB == 8388608 KiB + 1648 KiB
- RSS 8GiB वेतन वृद्धि में वृद्धि जारी है। अंतिम प्रिंट में 24 गिबी मेमोरी के बारे में दिखाया गया है, और 32 जीबीबी को मुद्रित करने से पहले, ओओएम हत्यारे ने इस प्रक्रिया को मार दिया
इसे भी देखें: निवासी सेट आकार / आभासी आकार पर स्पष्टीकरण की आवश्यकता है
OOM हत्यारा लॉग
हमारे dmesg
आदेशों ने OOM हत्यारे लॉग दिखाए हैं।
उन लोगों की सटीक व्याख्या की गई है:
लॉग की पहली पंक्ति थी:
[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
तो हम देखते हैं कि दिलचस्प बात यह थी कि मोंगोबडी डेमॉन हमेशा मेरे लैपटॉप में चलता है जो पहले ओओएम किलर को ट्रिगर करता था, शायद तब जब खराब चीज कुछ मेमोरी आवंटित करने की कोशिश कर रही थी।
हालांकि, ओओएम किलर जरूरी नहीं कि जो इसे जगाए उसे मार डाले।
मंगलाचरण के बाद, कर्नेल एक तालिका या प्रक्रियाओं को शामिल करता है 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
और आगे हम देखते हैं कि हमारे छोटे main.out
वास्तव में पिछले आह्वान पर मारे गए:
[ 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
इस लॉग में score 865
उस प्रक्रिया का उल्लेख है, जो संभवतः उच्चतम (सबसे खराब) OOM किलर स्कोर है जैसा कि उल्लेख किया गया है: OOM किलर किस प्रक्रिया को पहले मारने का निर्णय लेता है?
इसके अलावा दिलचस्प रूप से, सब कुछ स्पष्ट रूप से इतनी तेजी से हुआ कि मुक्त की गई मेमोरी का हिसाब करने से पहले प्रक्रिया oom
द्वारा फिर से जागृत किया गया DeadlineMonitor
:
[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
और इस बार कुछ क्रोमियम प्रक्रिया को मार दिया, जो आमतौर पर मेरे कंप्यूटर की सामान्य मेमोरी हॉग है:
[ 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
उबंटू 19.04, लिनक्स कर्नेल 5.0.0 में परीक्षण किया गया।