Benötigen Sie eine Erklärung zu Resident Set Size/Virtual Size

Benötigen Sie eine Erklärung zu Resident Set Size/Virtual Size

Ich fand, dass das pidstatein gutes Tool zur Überwachung von Prozessen wäre. Ich möchte den durchschnittlichen Speicherverbrauch eines bestimmten Prozesses berechnen. Hier ist eine Beispielausgabe:

02:34:36 PM       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
02:34:37 PM      7276      2.00      0.00  349212 210176   7.14  scalpel

(Dies ist ein Teil der Ausgabe von pidstat -r -p 7276.)

Soll ich die Informationen zur Resident Set Size (RSS) oder Virtual Size (VSZ) verwenden, um den durchschnittlichen Speicherverbrauch zu berechnen? Ich habe ein paar Dinge auf Wikipedia und in Foren gelesen, bin mir aber nicht sicher, ob ich die Unterschiede vollständig verstehe. Außerdem scheint keine davon zuverlässig zu sein. Wie kann ich also einen Prozess überwachen, um seinen Speicherverbrauch zu ermitteln?

Jede Hilfe zu diesem Thema wäre hilfreich.

Antwort1

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 Speichertypen, sowohl im RAM als auch ausgelagert. Diese Zahlen können verzerrt sein, da sie auch gemeinsam genutzte Bibliotheken und andere Speichertypen enthalten. Sie können fünfhundert Instanzen ausführen bash, und die Gesamtgröße ihres Speicherbedarfs entspricht nicht der Summe ihrer RSS- oder VSZ-Werte.

Wenn Sie sich einen genaueren Überblick über den Speicherbedarf eines Prozesses verschaffen möchten, stehen Ihnen einige Optionen zur Verfügung. Sie können alles durchgehen /proc/$PID/mapund die Dinge aussortieren, die Ihnen nicht gefallen. Wenn es sich um gemeinsam genutzte Bibliotheken handelt, kann die Berechnung je nach Ihren Anforderungen (an die ich mich zu erinnern glaube) komplex werden.

Wenn Sie nur an der Heap-Größe des Prozesses interessiert sind, können Sie immer nur den [heap]Eintrag in der mapDatei analysieren. Die Größe, die der Kernel für den Prozess-Heap zugewiesen hat, spiegelt möglicherweise die genaue Anzahl der Bytes wider, die der Prozess hat, muss es aber nicht.fragtezuzuteilen. Es gibt winzige Details, Kernel-Interna und Optimierungen, die dies durcheinander bringen 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 PAGESIZEwir sagen Ihnen, was es ist – auf PCs sind es wahrscheinlich 4.096 Bytes).

Wenn Sie sehen möchten, wie viel Speicher ein Prozess hatzugeteilt, eine der besten Möglichkeiten besteht darin, auf die Kernel-seitigen Metriken zu verzichten. Stattdessen instrumentieren Sie die Heap-Speicher-(De-)Allokationsfunktionen der C-Bibliothek mit dem LD_PRELOADMechanismus. Persönlich missbrauche ich es ein wenig, valgrindum Informationen über solche Dinge zu erhalten. (Beachten Sie, dass das Anwenden der Instrumentierung einen Neustart des Prozesses erfordert.)

valgrindBitte beachten Sie, dass Ihre Programme dadurch geringfügig langsamer werden (aber wahrscheinlich innerhalb Ihrer Toleranzen), da Sie möglicherweise auch Laufzeitvergleiche durchführen .

Antwort2

Minimal ausführbares Beispiel

Damit dies Sinn ergibt, müssen Sie die Grundlagen der Paging-Funktion verstehen:https://stackoverflow.com/questions/18431261/wie-funktioniert-x86-pagingund insbesondere, dass das Betriebssystem virtuellen Speicher über Seitentabellen bzw. seine interne Speicherbuchhaltung (VSZ-virtueller Speicher) zuweisen kann, bevor es tatsächlich über einen Sicherungsspeicher im RAM oder auf der Festplatte (RSS-residenter Speicher) verfügt.

Um dies nun in Aktion zu beobachten, erstellen wir ein Programm, das:

Haupt 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:

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

Beendigungsstatus:

137

die durch die128 + Signalnummernregelbedeutet, dass wir eine Signalnummer haben 9, die man 7 signalbesagt, dassSIGKILL, die vom Linux-System gesendet wirdOut-of-Memory-Killer.

Interpretation der Ausgabe:

  • Der virtuelle VSZ-Speicher bleibt nach dem mmap konstant bei printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psWerte sind in KiB).
  • Der „tatsächliche Speicherverbrauch“ von RSS steigt nur langsam an, wenn wir die Seiten berühren. Zum Beispiel:
    • beim ersten Ausdruck haben wir extra_memory_committed 0, was bedeutet, dass wir noch keine Seiten berührt haben. RSS ist ein kleiner Bereich, 1648 KiBder für den normalen Programmstart reserviert wurde, wie Textbereich, Globals usw.
    • Beim zweiten Ausdruck haben wir 8388608 KiB == 8GiBSeiten im Wert von geschrieben. Als Ergebnis stieg RSS um genau 8 GIB auf8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS steigt weiterhin in 8-GiB-Schritten. Der letzte Ausdruck zeigt etwa 24 GiB Speicher, und bevor 32 GiB gedruckt werden konnten, beendete der OOM-Killer den Prozess

Siehe auch:Benötigen Sie eine Erklärung zu Resident Set Size/Virtual Size

OOM-Killerprotokolle

Unsere dmesgBefehle haben die OOM-Killerprotokolle angezeigt.

Um eine genaue Interpretation dieser Angaben wurde gebeten unter:

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 auf meinem Laptop immer im Hintergrund läuft, der den OOM-Killer zuerst ausgelöst hat, vermutlich als das arme Ding versucht hat, etwas Speicher zuzuordnen.

Allerdings tötet der OOM-Killer nicht unbedingt denjenigen, der ihn geweckt hat.

Nach dem Aufruf druckt der Kernel eine Tabelle oder Prozesse aus, einschließlich 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

und weiter vorne sehen wir, dass unser eigenes Kleines main.outbeim vorherigen Aufruf tatsächlich getötet wurde:

[ 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

In diesem Protokoll wird erwähnt score 865, dass dieser Prozess vermutlich den höchsten (schlechtesten) OOM-Killer-Score hatte, wie hier erwähnt:Wie entscheidet der OOM-Killer, welcher Prozess zuerst beendet wird?

Interessant ist auch, dass anscheinend alles so schnell passierte, dass der freigegebene Speicher, bevor er abgerechnet wurde, durch den Vorgang oomwieder aufgeweckt wurde :DeadlineMonitor

[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

und dieses Mal hat es einen Chromium-Prozess beendet, 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.

verwandte Informationen