GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Benötigen Sie eine Erklärung zur Resident Set-Größe/virtuellen Größe

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 kleines 1648 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 auf 8390256 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

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.


Linux
  1. Wie stelle ich die Datei-Upload-Größenbeschränkung in Apache ein?

  2. Richten Sie virtuelle Hosts auf CentOS ein

  3. Maximale Speicherauslastung eines Linux/Unix-Prozesses

  4. Speichernutzung des aktuellen Prozesses in C

  5. malloc gibt Arbeitsspeicher oder virtuellen Adressraum zurück

Swapiness unter Linux:Alles, was Sie wissen müssen

So erweitern Sie die Festplattengröße der virtuellen KVM-Maschine unter Linux

So richten Sie Apache Virtual Hosts unter Ubuntu 18.04 ein

So richten Sie virtuelle Apache-Hosts unter Ubuntu 20.04 ein

Linux-Speicherverwaltung – Virtueller Speicher und Demand Paging

Finden Sie die RAM-Größe in Linux