RSS gibt an, wie viel Speicher dieser Prozess derzeit im Hauptspeicher (RAM) hat. VSZ gibt an, wie viel virtuellen Speicher der Prozess insgesamt hat. Dies umfasst alle Arten von Speicher, sowohl im RAM als auch ausgelagert. Diese Zahlen können verzerrt werden, da sie auch gemeinsam genutzte Bibliotheken und andere Arten von Speicher enthalten. Sie können fünfhundert Instanzen von bash
haben laufen, und die Gesamtgröße ihres Speicherbedarfs ist nicht die Summe ihrer RSS- oder VSZ-Werte.
Wenn Sie eine detailliertere Vorstellung vom Speicherbedarf eines Prozesses erhalten möchten, haben Sie einige Optionen. Sie können /proc/$PID/map
durchlaufen und sortiere die Sachen aus, die du nicht magst. Wenn es sich um gemeinsam genutzte Bibliotheken handelt, kann die Berechnung abhängig von Ihren Anforderungen komplex werden (an die ich mich zu erinnern glaube).
Wenn Sie sich nur für die Heap-Größe des Prozesses interessieren, können Sie immer nur [heap]
parsen Eintrag im map
Datei. Die Größe, die der Kernel für den Prozess-Heap zugewiesen hat, kann die genaue Anzahl der Bytes widerspiegeln, die der Prozess gefragt hat, muss es aber nicht zuzuordnen. Es gibt winzige Details, Kernel-Interna und Optimierungen, die dies stören können. In einer idealen Welt ist es so viel, wie Ihr Prozess benötigt, aufgerundet auf das nächste Vielfache der Systemseitengröße (getconf PAGESIZE
wird Ihnen sagen, was es ist – auf PCs sind es wahrscheinlich 4.096 Bytes).
Wenn Sie sehen möchten, wie viel Speicher ein Prozess zugewiesen hat , ist eine der besten Möglichkeiten, auf die Kernel-seitigen Metriken zu verzichten. Stattdessen instrumentieren Sie die Heap-Speicher-(De-)Allokationsfunktionen der C-Bibliothek mit LD_PRELOAD
Mechanismus. Ich persönlich missbrauche valgrind
leicht um Informationen über diese Art von Dingen zu erhalten. (Beachten Sie, dass das Anwenden der Instrumentierung einen Neustart des Prozesses erfordert.)
Bitte beachten Sie, da Sie möglicherweise auch Benchmarking-Laufzeiten durchführen, dass valgrind
wird Ihre Programme etwas langsamer machen (aber wahrscheinlich innerhalb Ihrer Toleranzen).
Minimales lauffähiges Beispiel
Damit dies sinnvoll ist, müssen Sie die Grundlagen des Paging verstehen:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work und insbesondere, dass das Betriebssystem virtuellen Speicher über Seitentabellen zuweisen kann / seine interne Speicherbuchführung (virtueller VSZ-Speicher), bevor er tatsächlich einen Sicherungsspeicher auf RAM oder Festplatte (residenter RSS-Speicher) hat.
Um dies nun in Aktion zu beobachten, erstellen wir ein Programm, das:
- weist mit
mmap
mehr RAM zu als unser physikalischer Speicher - schreibt ein Byte auf jede Seite, um sicherzustellen, dass jede dieser Seiten vom virtuellen Nur-Speicher (VSZ) zum tatsächlich genutzten Speicher (RSS) geht
- überprüft die Speichernutzung des Prozesses mit einer der unter:https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c genannten Methoden
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-Upstream.
Kompilieren und ausführen:
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
wo:
- 0x1000000000 ==64 GiB:2x der physische Arbeitsspeicher meines Computers von 32 GiB
- 0x200000000 ==8GiB:Speicher alle 8GiB drucken, also sollten wir vor dem Absturz 4 Ausdrucke bei etwa 32GiB erhalten
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
:erforderlich für Linux, damit wir einen mmap-Aufruf tätigen können, der größer als der physische RAM ist:https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432
Programmausgabe:
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
Ausgangsstatus:
137
was nach der 128 + Signalnummernregel bedeutet, dass wir die Signalnummer 9
erhalten haben , welche man 7 signal
sagt, ist SIGKILL, das vom Linux-Out-of-Memory-Killer gesendet wird.
Ausgabeinterpretation:
- VSZ virtueller Speicher bleibt konstant bei
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
(ps
Werte sind in KiB) nach dem mmap. - RSS "reale Speicherauslastung" steigt nur langsam an, wenn wir die Seiten berühren. Zum Beispiel:
- Auf dem ersten Druck haben wir
extra_memory_committed 0
, was bedeutet, dass wir noch keine Seiten berührt haben. RSS ist ein kleines1648 KiB
die für den normalen Programmstart zugewiesen wurden, wie Textbereich, Globals usw. - beim zweiten Druck haben wir an
8388608 KiB == 8GiB
geschrieben Wert von Seiten. Dadurch stieg RSS um genau 8 GIB auf8390256 KiB == 8388608 KiB + 1648 KiB
- RSS nimmt weiterhin in 8-GiB-Schritten zu. Der letzte Druck zeigt etwa 24 GiB Speicher, und bevor 32 GiB gedruckt werden konnten, hat der OOM-Killer den Prozess abgebrochen
- Auf dem ersten Druck haben wir
Siehe auch:Benötigen Sie eine Erklärung zur Größe des residenten Satzes/virtuellen Größe
OOM-Killerprotokolle
Unser dmesg
Befehle haben die OOM-Killer-Protokolle angezeigt.
Eine genaue Interpretation dieser wurde erbeten unter:
- https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs, aber lasst uns einen kurzen Blick hier werfen.
- https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages
Die allererste Zeile des Protokolls war:
[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Wir sehen also, dass es interessanterweise der MongoDB-Daemon war, der immer auf meinem Laptop im Hintergrund läuft, der zuerst den OOM-Killer auslöste, vermutlich als das arme Ding versuchte, etwas Speicher zuzuweisen.
Der OOM-Killer tötet jedoch nicht unbedingt denjenigen, der ihn geweckt hat.
Nach dem Aufruf gibt der Kernel eine Tabelle oder Prozesse aus, die den oom_score
enthalten :
[ 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
und weiter vorne sehen wir unser eigenes kleines main.out
wurde tatsächlich beim vorherigen Aufruf getötet:
[ 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
Dieses Protokoll erwähnt den score 865
den dieser Prozess hatte, vermutlich die höchste (schlechteste) OOM-Killer-Punktzahl, wie erwähnt unter:Wie entscheidet der OOM-Killer, welcher Prozess zuerst beendet wird?
Interessanterweise passierte anscheinend alles so schnell, dass vor der Berücksichtigung des freigegebenen Speichers der oom
angezeigt wurde wurde durch DeadlineMonitor
wieder geweckt Prozess:
[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
und dieses Mal hat das einen Chromium-Prozess zerstört, der normalerweise der normale Speicherfresser meines Computers ist:
[ 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
Getestet in Ubuntu 19.04, Linux-Kernel 5.0.0.