Potrzebujesz wyjaśnienia na temat rozmiaru zestawu rezydentnego / rozmiaru wirtualnego

61

Odkryłem, że pidstatbyłoby to dobre narzędzie do monitorowania procesów. Chcę obliczyć średnie zużycie pamięci dla określonego procesu. Oto kilka przykładowych danych wyjściowych:

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

(Jest to część wyniku z pidstat -r -p 7276.)

Czy powinienem użyć informacji o rozmiarze zestawu rezydentnego (RSS) lub rozmiarze wirtualnym (VSZ) do obliczenia średniego zużycia pamięci? Przeczytałem kilka rzeczy na Wikipedii i na forach, ale nie jestem pewien, czy w pełni rozumiem różnice. Ponadto wydaje się, że żaden z nich nie jest niezawodny. Jak więc mogę monitorować proces, aby uzyskać zużycie pamięci?

Przydałaby się wszelka pomoc w tej sprawie.

Flanfl
źródło

Odpowiedzi:

63

RSS to ilość pamięci, jaką proces ten ma obecnie w pamięci głównej (RAM). VSZ to, ile pamięci wirtualnej ma cały proces. Obejmuje to wszystkie rodzaje pamięci, zarówno w pamięci RAM, jak i zamienione. Liczby te mogą zostać zniekształcone, ponieważ obejmują również biblioteki współdzielone i inne typy pamięci. Możesz mieć pięćset wystąpień bash, a całkowity rozmiar ich pamięci nie będzie sumą ich wartości RSS lub VSZ.

Jeśli chcesz uzyskać bardziej szczegółowe pojęcie na temat śladu pamięci procesu, masz kilka opcji. Możesz przejść /proc/$PID/mapi odrzucić rzeczy, których nie lubisz. Jeśli są to biblioteki współdzielone, obliczenia mogą się skomplikować w zależności od potrzeb (które, jak sądzę, pamiętam).

Jeśli zależy Ci tylko na wielkości sterty procesu, zawsze możesz po prostu przeanalizować [heap]wpis w mappliku. Rozmiar przydzielony przez jądro dla sterty procesów może, ale nie musi, odzwierciedlać dokładną liczbę bajtów, o które proces poprosił o przydzielenie. Są drobne szczegóły, wewnętrzne elementy jądra i optymalizacje, które mogą to zrzucić. W idealnym świecie będzie to tyle, ile potrzebuje Twój proces, zaokrąglone w górę do najbliższej wielokrotności rozmiaru strony systemowej ( getconf PAGESIZEpowie ci, co to jest - na komputerach PC to prawdopodobnie 4096 bajtów).

Jeśli chcesz zobaczyć, ile pamięci przydzielił proces , jednym z najlepszych sposobów jest rezygnacja z pomiarów po stronie jądra. Zamiast tego instrumentu sterty pamięć C biblioteki (de) funkcjonuje alokacji z LD_PRELOADmechanizmu. Osobiście lekko nadużywam, valgrindaby uzyskać informacje na temat tego rodzaju rzeczy. (Należy pamiętać, że zastosowanie oprzyrządowania będzie wymagało ponownego uruchomienia procesu).

Uwaga: ponieważ możesz być również środowiskiem testowym, valgrindspowoduje to, że twoje programy będą nieco wolniejsze (ale prawdopodobnie w granicach tolerancji).

Alexios
źródło
Wielkie dzięki! Zamierzam zbadać różne opcje. Jesteś więcej niż pomocny! :)
Flanfl
„Możesz uruchomić pięćset wystąpień bash, a całkowity rozmiar ich pamięci nie będzie sumą ich wartości RSS lub VSZ”. Ale czy suma ich wartości RSS będzie dobrym przybliżeniem? Jak suma rezydentnej kolumny z statm, nie potrzebuję super wiarygodnej dokładnej wartości, ale muszę wiedzieć, ile pamięci zajmują moje procesy Java
iloveretards
3
W Ubuntu /proc/$PID/mapsjest to różnica w literówce lub dystrybucji?
dolzenko
1

Minimalny możliwy do uruchomienia przykład

Aby to miało sens, musisz zrozumieć podstawy stronicowania: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work, aw szczególności, że system operacyjny może przydzielić pamięć wirtualną za pomocą tabel stron / prowadzenie wewnętrznej księgi pamięci (pamięć wirtualna VSZ), zanim faktycznie będzie miała pamięć zapasową na RAM lub dysku (pamięć rezydentna RSS).

Teraz, aby to zaobserwować w akcji, stwórzmy program, który:

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 w górę .

Skompiluj i uruchom:

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

gdzie:

Wyjście programu:

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

Status wyjścia:

137

co według reguły liczby 128 + oznacza, że ​​otrzymaliśmy numer sygnału 9, który man 7 signalmówi , że to SIGKILL , który jest wysyłany przez zabójcę braku pamięci w systemie Linux .

Interpretacja wyjściowa:

  • Pamięć wirtualna VSZ pozostaje stała w printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( pswartości w KiB) po mmap.
  • „Rzeczywiste użycie pamięci” RSS rośnie leniwie tylko wtedy, gdy dotykamy stron. Na przykład:
    • przy pierwszym wydruku mamy extra_memory_committed 0, co oznacza, że ​​jeszcze nie dotknęliśmy żadnych stron. RSS to mały plik 1648 KiBprzeznaczony do normalnego uruchamiania programu, takiego jak obszar tekstowy, globały itp.
    • na drugim wydruku napisaliśmy do 8388608 KiB == 8GiBwartości stron. W rezultacie RSS wzrosła dokładnie o 8 GIB do8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS stale rośnie o 8GiB. Ostatni wydruk pokazuje około 24 GiB pamięci, a zanim wydrukowano 32 GiB, zabójca OOM zabił proces

Zobacz także: Potrzebujesz wyjaśnienia na temat rozmiaru zestawu rezydentnego / rozmiaru wirtualnego

Dzienniki zabójców OOM

Nasze dmesgpolecenia pokazały dzienniki zabójców OOM.

Dokładna ich interpretacja została zapytana na:

Pierwszą linią dziennika było:

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

Widzimy więc, co ciekawe, że to demon MongoDB zawsze działa w moim laptopie w tle, który jako pierwszy wyzwalał zabójcę OOM, prawdopodobnie gdy biedak próbował przydzielić trochę pamięci.

Jednak zabójca OOM niekoniecznie zabija tego, który go obudził.

Po wywołaniu jądro drukuje tabelę lub procesy, w tym 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

a dalej widzimy, że nasze własne małe main.outtak naprawdę zginęło przy poprzednim wywołaniu:

[ 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

Dziennik ten wspomina, score 865który proces miał, prawdopodobnie najwyższy (najgorszy) wynik zabójcy OOM, jak wspomniano w: Jak zabójca OOM decyduje, który proces zabić jako pierwszy?

Co ciekawe, wszystko najwyraźniej wydarzyło się tak szybko, że zanim uwolniona pamięć została rozliczona, proces oomzostał ponownie obudzony DeadlineMonitor:

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

i tym razem zabił proces Chromium, który zwykle jest normalnym zapisem pamięci mojego komputera:

[ 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

Testowany w Ubuntu 19.04, jądro Linuksa 5.0.0.

Ciro Santilli
źródło