Ví dụ runnable tối thiểu
Để điều này có ý nghĩa, bạn phải hiểu những điều cơ bản về phân trang: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work và đặc biệt là HĐH có thể phân bổ bộ nhớ ảo thông qua các bảng trang / lưu giữ bộ nhớ trong của nó (bộ nhớ ảo VSZ) trước khi nó thực sự có bộ lưu trữ dự phòng trên RAM hoặc đĩa (bộ nhớ lưu trữ RSS).
Bây giờ để quan sát điều này trong thực tế, hãy tạo một chương trình:
C chính
#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 ngược dòng .
Biên dịch và chạy:
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
Ở đâu:
Đầu ra chương trình:
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
Trạng thái thoát:
137
mà theo quy tắc số tín hiệu 128 + có nghĩa là chúng ta có số tín hiệu 9
, có nghĩa man 7 signal
là SIGKILL , được gửi bởi kẻ giết người hết bộ nhớ Linux .
Giải thích đầu ra:
- Bộ nhớ ảo VSZ không đổi tại
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
( ps
giá trị tính bằng KiB) sau mmap.
- RSS "sử dụng bộ nhớ thực" chỉ tăng một cách lười biếng khi chúng ta chạm vào các trang. Ví dụ:
- trên bản in đầu tiên, chúng tôi có
extra_memory_committed 0
, có nghĩa là chúng tôi chưa chạm vào bất kỳ trang nào. RSS là một phần nhỏ 1648 KiB
đã được phân bổ cho khởi động chương trình bình thường như vùng văn bản, toàn cầu, v.v.
- trên bản in thứ hai, chúng tôi đã viết lên
8388608 KiB == 8GiB
giá trị của các trang. Do đó, RSS tăng chính xác 8GIB lên8390256 KiB == 8388608 KiB + 1648 KiB
- RSS tiếp tục tăng theo gia số 8GiB. Bản in cuối cùng cho thấy khoảng 24 GiB bộ nhớ và trước khi có thể in 32 GiB, kẻ giết người OOM đã giết quá trình
Xem thêm: Cần giải thích về Kích thước cài đặt thường trú / Kích thước ảo
Nhật ký sát thủ OOM
Các dmesg
lệnh của chúng tôi đã hiển thị nhật ký sát thủ OOM.
Một cách giải thích chính xác của những người đã được hỏi tại:
Dòng đầu tiên của nhật ký là:
[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Vì vậy, chúng ta thấy điều thú vị đó là daemon MongoDB luôn chạy trong máy tính xách tay của tôi trên nền tảng đầu tiên kích hoạt kẻ giết người OOM, có lẽ là khi người nghèo đang cố phân bổ bộ nhớ.
Tuy nhiên, kẻ giết người OOM không nhất thiết phải giết người đánh thức nó.
Sau khi gọi, kernel in một bảng hoặc các quy trình bao gồm 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
và xa hơn nữa chúng ta thấy rằng chính chúng ta main.out
đã bị giết trong lần gọi trước:
[ 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
Nhật ký này đề cập đến score 865
quá trình đó, có lẽ là điểm giết người OOM cao nhất (tệ nhất) như đã đề cập tại: Làm thế nào để kẻ giết người OOM quyết định quá trình nào sẽ giết trước?
Cũng thú vị, mọi thứ rõ ràng đã xảy ra nhanh đến mức trước khi bộ nhớ được giải phóng, quá trình oom
lại được đánh thức lại DeadlineMonitor
:
[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
và lần này đã giết một số quy trình Chromium, thường là máy tính của tôi bộ nhớ bình thường:
[ 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
Đã thử nghiệm trong Ubuntu 19.04, nhân Linux 5.0.0.