GNU/Linux >> Linux Esercitazione >  >> Linux

Hai bisogno di spiegazioni sulla dimensione del set residente/dimensione virtuale

RSS è la quantità di memoria che questo processo ha attualmente nella memoria principale (RAM). VSZ è la quantità di memoria virtuale che il processo ha in totale. Ciò include tutti i tipi di memoria, sia nella RAM che scambiata. Questi numeri possono essere distorti perché includono anche librerie condivise e altri tipi di memoria. Puoi avere cinquecento istanze di bash in esecuzione e la dimensione totale del loro footprint di memoria non sarà la somma dei loro valori RSS o VSZ.

Se hai bisogno di avere un'idea più dettagliata sull'impronta di memoria di un processo, hai alcune opzioni. Puoi passare attraverso /proc/$PID/map ed elimina le cose che non ti piacciono. Se si tratta di librerie condivise, il calcolo potrebbe diventare complesso a seconda delle tue esigenze (che penso di ricordare).

Se ti interessa solo la dimensione dell'heap del processo, puoi sempre analizzare il [heap] voce nel map file. La dimensione che il kernel ha allocato per l'heap del processo può riflettere o meno il numero esatto di byte che il processo ha chiesto da assegnare. Ci sono minimi dettagli, interni del kernel e ottimizzazioni che possono eliminare tutto questo. In un mondo ideale, sarà tanto quanto il tuo processo richiede, arrotondato al multiplo più vicino della dimensione della pagina di sistema (getconf PAGESIZE ti dirà di cosa si tratta — sui PC è probabilmente di 4.096 byte).

Se vuoi vedere quanta memoria ha allocato un processo , uno dei modi migliori è rinunciare alle metriche lato kernel. Invece, strumentizzi le funzioni di (de)allocazione della memoria heap della libreria C con LD_PRELOAD meccanismo. Personalmente, abuso leggermente di valgrind per ottenere informazioni su questo genere di cose. (Si noti che l'applicazione della strumentazione richiederà il riavvio del processo.)

Tieni presente che, poiché potresti anche eseguire il benchmarking dei tempi di esecuzione, valgrind renderà i tuoi programmi leggermente più lenti (ma probabilmente entro le tue tolleranze).


Esempio eseguibile minimo

Affinché ciò abbia senso, devi comprendere le basi del paging:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work e in particolare che il sistema operativo può allocare memoria virtuale tramite le tabelle delle pagine / il suo libro di memoria interno (memoria virtuale VSZ) prima che abbia effettivamente uno spazio di archiviazione su RAM o disco (memoria residente RSS).

Ora per osservarlo in azione, creiamo un programma che:

  • alloca più RAM della nostra memoria fisica con mmap
  • scrive un byte su ogni pagina per garantire che ciascuna di queste pagine passi dalla memoria solo virtuale (VSZ) alla memoria effettivamente utilizzata (RSS)
  • controlla l'utilizzo della memoria del processo con uno dei metodi menzionati in: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 a monte.

Compila ed esegui:

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

dove:

  • 0x1000000000 ==64GiB:2 volte la RAM fisica del mio computer di 32GiB
  • 0x200000000 ==8GiB:stampa la memoria ogni 8GiB, quindi dovremmo ottenere 4 stampe prima del crash a circa 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory :richiesto per Linux per consentirci di effettuare una chiamata mmap più grande della RAM fisica:https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

Uscita del programma:

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

Stato di uscita:

137

che per la regola del numero di segnale 128 + significa che abbiamo ottenuto il numero di segnale 9 , che man 7 signal dice è SIGKILL, che viene inviato dal killer di memoria esaurita di Linux.

Interpretazione dell'output:

  • La memoria virtuale VSZ rimane costante a printf '0x%X\n' 0x40009A4 KiB ~= 64GiB (ps i valori sono in KiB) dopo mmap.
  • Il "vero utilizzo della memoria" RSS aumenta lentamente solo quando tocchiamo le pagine. Ad esempio:
    • sulla prima stampa, abbiamo extra_memory_committed 0 , il che significa che non abbiamo ancora toccato nessuna pagina. RSS è un piccolo 1648 KiB che è stato assegnato per il normale avvio del programma come area di testo, globali, ecc.
    • nella seconda stampa, abbiamo scritto a 8388608 KiB == 8GiB valore di pagine. Di conseguenza, RSS è aumentato esattamente di 8 GIB a 8390256 KiB == 8388608 KiB + 1648 KiB
    • L'RSS continua ad aumentare con incrementi di 8GiB. L'ultima stampa mostra circa 24 GiB di memoria e prima che potessero essere stampati 32 GiB, il killer OOM ha interrotto il processo

Vedi anche:Hai bisogno di spiegazioni su Resident Set Size/Virtual Size

Registri OOM killer

Il nostro dmesg i comandi hanno mostrato i log killer di OOM.

Un'esatta interpretazione di questi è stata chiesta a:

  • https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs ma diamo una rapida occhiata qui.
  • https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages

La primissima riga del registro era:

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

Quindi vediamo che è stato interessante notare che è stato il demone MongoDB che viene sempre eseguito sul mio laptop in background a attivare per primo il killer OOM, presumibilmente quando il poveretto stava cercando di allocare un po' di memoria.

Tuttavia, il killer OOM non uccide necessariamente colui che lo ha svegliato.

Dopo l'invocazione, il kernel stampa una tabella o processi che includono 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

e più avanti vediamo che il nostro piccolo main.out effettivamente è stato ucciso durante l'invocazione precedente:

[ 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

Questo registro menziona il score 865 che quel processo aveva, presumibilmente il più alto (peggiore) punteggio di OOM killer come menzionato in:In che modo l'OOM killer decide quale processo uccidere per primo?

Inoltre, è interessante notare che apparentemente tutto è accaduto così in fretta che prima che la memoria liberata fosse contabilizzata, il oom è stato nuovamente svegliato dal DeadlineMonitor processo:

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

e questa volta questo ha ucciso un processo Chromium, che di solito è il normale consumo di memoria del mio computer:

[ 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

Testato in Ubuntu 19.04, kernel Linux 5.0.0.


Linux
  1. Comandi Linux:esplorare la memoria virtuale con vmstat

  2. Aumenta la dimensione del disco virtuale di Windows10 VM su QEMU-KVM

  3. Avvia Zathura a schermo intero / Ricorda le dimensioni della finestra?

  4. Picco di utilizzo della memoria di un processo Linux/Unix

  5. malloc restituisce la memoria o lo spazio degli indirizzi virtuali

Swappiness in Linux:tutto ciò che devi sapere

Come estendere la dimensione del disco della macchina virtuale KVM in Linux

Come configurare gli host virtuali Apache su Ubuntu 18.04

Come configurare gli host virtuali Apache su Ubuntu 20.04

Gestione della memoria Linux:memoria virtuale e paginazione della domanda

Trova la dimensione della RAM in Linux