GNU/Linux >> Znalost Linux >  >> Linux

Potřebujete vysvětlení ohledně velikosti rezidentní sady/virtuální velikosti

RSS udává, kolik paměti má tento proces aktuálně v hlavní paměti (RAM). VSZ udává, kolik virtuální paměti má proces celkem. To zahrnuje všechny typy pamětí, a to jak v RAM, tak swapované. Tato čísla mohou být zkreslená, protože zahrnují také sdílené knihovny a další typy paměti. Můžete mít pět set instancí bash běží a celková velikost jejich paměťové stopy nebude součtem jejich hodnot RSS nebo VSZ.

Pokud potřebujete získat podrobnější představu o paměťové stopě procesu, máte několik možností. Můžete projít /proc/$PID/map a vyřadit věci, které se vám nelíbí. Pokud se jedná o sdílené knihovny, výpočet by mohl být složitý v závislosti na vašich potřebách (což si myslím, že si pamatuji).

Pokud vám záleží pouze na velikosti haldy procesu, můžete vždy analyzovat [heap] záznam v map soubor. Velikost, kterou jádro alokovalo pro hromadu procesu, může, ale nemusí odrážet přesný počet bajtů, které proces požádal k přidělení. Existují drobné detaily, vnitřnosti jádra a optimalizace, které to mohou shodit. V ideálním světě to bude tolik, kolik váš proces potřebuje, zaokrouhleno nahoru na nejbližší násobek velikosti systémové stránky (getconf PAGESIZE vám řekne, co to je — na počítačích je to pravděpodobně 4 096 bajtů).

Pokud chcete vidět, kolik paměti proces přidělil , jedním z nejlepších způsobů je vzdát se metrik na straně jádra. Místo toho instrumentujete (de)alokační funkce paměti haldy C knihovny pomocí LD_PRELOAD mechanismus. Osobně valgrind mírně zneužívám získat informace o takových věcech. (Všimněte si, že použití instrumentace bude vyžadovat restartování procesu.)

Vezměte prosím na vědomí, protože můžete také srovnávat běhové prostředí, že valgrind vaše programy budou velmi mírně pomalejší (ale pravděpodobně v rámci vašich tolerancí).


Minimální spustitelný příklad

Aby to dávalo smysl, musíte pochopit základy stránkování:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work a zejména to, že OS může alokovat virtuální paměť pomocí tabulek stránek / vedení knihy vnitřní paměti (virtuální paměť VSZ) předtím, než bude mít skutečně záložní úložiště v RAM nebo na disku (RSS rezidentní paměť).

Nyní, abychom to pozorovali v akci, vytvořte program, který:

  • přiděluje více paměti RAM než naše fyzická paměť s mmap
  • na každou stránku zapíše jeden bajt, aby bylo zajištěno, že každá z těchto stránek přejde z pouze virtuální paměti (VSZ) do skutečně používané paměti (RSS)
  • kontroluje využití paměti procesem pomocí jedné z metod uvedených na adrese:https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c

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.

Kompilace a spuštění:

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

kde:

  • 0x1000000000 ==64GiB:2x fyzická RAM mého počítače 32GiB
  • 0x200000000 ==8GiB:vytiskněte paměť každých 8GiB, takže bychom před zhroucením měli získat 4 výtisky při přibližně 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory :vyžadováno pro Linux, aby nám umožnilo volání mmap větší než fyzická RAM:https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

Výstup 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

Stav ukončení:

137

což podle pravidla 128 + číslo signálu znamená, že máme číslo signálu 9 , což je man 7 signal říká, že je SIGKILL, který je odeslán linuxovým zabijákem nedostatku paměti.

Interpretace výstupu:

  • Virtuální paměť VSZ zůstává konstantní na printf '0x%X\n' 0x40009A4 KiB ~= 64GiB (ps hodnoty jsou v KiB) za mmap.
  • Skutečné využití paměti RSS se líně zvyšuje pouze tehdy, když se dotýkáme stránek. Například:
    • na prvním výtisku máme extra_memory_committed 0 , což znamená, že jsme se ještě nedotkli žádné stránky. RSS je malý 1648 KiB která byla přidělena pro normální spouštění programu, jako je textová oblast, globální hodnoty atd.
    • při druhém tisku jsme zapsali do 8388608 KiB == 8GiB v hodnotě stránek. V důsledku toho se RSS zvýšilo přesně o 8 GBP na 8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS se stále zvyšuje v 8GiB přírůstcích. Poslední tisk ukazuje asi 24 GiB paměti a než bylo možné vytisknout 32 GiB, zabiják OOM tento proces zabil

Viz také:Potřebujete vysvětlení k velikosti rezidentní sady/virtuální velikosti

Protokoly zabijáků OOM

Naše dmesg příkazy zobrazily protokoly zabijáků OOM.

Jejich přesný výklad byl požádán na adrese:

  • https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs, ale pojďme se rychle podívat sem.
  • https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages

Úplně první řádek protokolu byl:

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

Vidíme tedy, že je zajímavé, že to byl démon MongoDB, který vždy běží v mém notebooku na pozadí, který jako první spustil OOM zabijáka, pravděpodobně když se chudák snažil alokovat nějakou paměť.

OOM zabiják však nemusí nutně zabít toho, kdo ho probudil.

Po vyvolání jádro vytiskne tabulku nebo procesy včetně 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 dále vidíme naši vlastní malou main.out vlastně byl zabit při předchozím vyvolání:

[ 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

Tento protokol uvádí score 865 který tento proces měl, pravděpodobně nejvyšší (nejhorší) skóre OOM zabijáka, jak je zmíněno v:Jak se OOM zabiják rozhodne, který proces zabije jako první?

Zajímavé také je, že vše se zjevně stalo tak rychle, že než byla uvolněná paměť zaúčtována, oom byl znovu probuzen DeadlineMonitor proces:

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

a tentokrát to zabilo nějaký proces Chromium, což je obvykle normální paměťový prase mých počítačů:

[ 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

Testováno v Ubuntu 19.04, Linux kernel 5.0.0.


Linux
  1. Jak zkontrolovat velikost RAM v příkazovém řádku Linuxu v GB

  2. Linuxové příkazy:prozkoumávání virtuální paměti pomocí vmstat

  3. Zvětšete velikost virtuálního disku Windows10 VM na QEMU-KVM

  1. Linux – potřebujete vysvětlení ohledně velikosti rezidentní sady/virtuální velikosti?

  2. Spustit Zathura na celou obrazovku / Zapamatovat velikost okna?

  3. Změna velikosti okna na nastavenou velikost v Linuxu

  1. Špičkové využití paměti linuxovým/unixovým procesem

  2. Využití paměti aktuálního procesu v C

  3. jak se určuje velikost stránky ve virtuálním adresovém prostoru?