Necesita explicación sobre el tamaño del conjunto residente/tamaño virtual

Necesita explicación sobre el tamaño del conjunto residente/tamaño virtual

Descubrí que pidstatsería una buena herramienta para monitorear procesos. Quiero calcular el uso promedio de memoria de un proceso en particular. A continuación se muestra un resultado de ejemplo:

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

(Esto es parte del resultado de pidstat -r -p 7276).

¿Debo utilizar la información del tamaño del conjunto residente (RSS) o del tamaño virtual (VSZ) para calcular el consumo promedio de memoria? He leído algunas cosas en Wikipedia y en foros, pero no estoy seguro de entender completamente las diferencias. Además, parece que ninguno de ellos es fiable. Entonces, ¿cómo puedo monitorear un proceso para conocer su uso de memoria?

Cualquier ayuda sobre este asunto sería útil.

Respuesta1

RSS es la cantidad de memoria que tiene actualmente este proceso en la memoria principal (RAM). VSZ es la cantidad de memoria virtual que tiene el proceso en total. Esto incluye todo tipo de memoria, tanto en RAM como intercambiada. Estos números pueden estar sesgados porque también incluyen bibliotecas compartidas y otros tipos de memoria. Puede tener quinientas instancias en bashejecución y el tamaño total de su huella de memoria no será la suma de sus valores RSS o VSZ.

Si necesita tener una idea más detallada sobre la huella de memoria de un proceso, tiene algunas opciones. Puedes revisar /proc/$PID/mapy eliminar las cosas que no te gustan. Si se trata de bibliotecas compartidas, el cálculo podría volverse complejo según sus necesidades (que creo recordar).

Si solo le importa el tamaño del montón del proceso, siempre puede analizar la [heap]entrada en el maparchivo. El tamaño que el kernel ha asignado para el montón del proceso puede reflejar o no el número exacto de bytes que tiene el proceso.preguntópara ser asignado. Hay detalles minuciosos, aspectos internos del kernel y optimizaciones que pueden alterar esto. En un mundo ideal, será tanto como su proceso necesite, redondeado al múltiplo más cercano del tamaño de la página del sistema ( getconf PAGESIZEle dirá cuál es; en las PC, probablemente sea 4096 bytes).

Si quieres ver cuánta memoria tiene un procesoasignado, una de las mejores maneras es renunciar a las métricas del lado del kernel. En su lugar, instrumenta las funciones de (des)asignación de memoria dinámica de la biblioteca C con el LD_PRELOADmecanismo. Personalmente, abuso un poco valgrindpara obtener información sobre este tipo de cosas. (Tenga en cuenta que para aplicar la instrumentación será necesario reiniciar el proceso).

Tenga en cuenta que, dado que es posible que también esté comparando tiempos de ejecución, eso valgrindhará que sus programas sean un poco más lentos (pero probablemente dentro de sus tolerancias).

Respuesta2

Ejemplo mínimo ejecutable

Para que esto tenga sentido, debes comprender los conceptos básicos de la paginación:https://stackoverflow.com/questions/18431261/how-does-x86-paging-worky, en particular, que el sistema operativo puede asignar memoria virtual a través de tablas de páginas/su contabilidad de memoria interna (memoria virtual VSZ) antes de tener realmente un almacenamiento de respaldo en RAM o disco (memoria residente RSS).

Ahora, para observar esto en acción, creemos un programa que:

C Principal

#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 ascendente.

Compilar y ejecutar:

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

dónde:

Salida del programa:

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

Estado de salida:

137

que por el128 + regla del número de señalsignifica que tenemos el número de señal 9, que man 7 signaldice esSIGKILL, que es enviado por Linuxasesino sin memoria.

Interpretación de salida:

  • La memoria virtual VSZ permanece constante en printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( pslos valores están en KiB) después del mmap.
  • El "uso de memoria real" de RSS aumenta perezosamente sólo cuando tocamos las páginas. Por ejemplo:
    • en la primera impresión tenemos extra_memory_committed 0, lo que significa que aún no hemos tocado ninguna página. RSS es un pequeño 1648 KiBque ha sido asignado para el inicio normal de programas como área de texto, globales, etc.
    • en la segunda impresión, hemos escrito un 8388608 KiB == 8GiBtotal de páginas. Como resultado, RSS aumentó exactamente 8GIB para8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS continúa aumentando en incrementos de 8GiB. La última impresión muestra aproximadamente 24 GiB de memoria, y antes de que se pudieran imprimir 32 GiB, el asesino de OOM finalizó el proceso.

Ver también:Necesita explicación sobre el tamaño del conjunto residente/tamaño virtual

Registros asesinos de OOM

Nuestros dmesgcomandos han mostrado los registros asesinos de OOM.

Se ha solicitado una interpretación exacta de ellos en:

La primera línea del registro fue:

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

Entonces vemos que, curiosamente, fue el demonio MongoDB que siempre se ejecuta en mi computadora portátil en segundo plano el que activó por primera vez el asesino OOM, presumiblemente cuando el pobre estaba tratando de asignar algo de memoria.

Sin embargo, el asesino de OOM no necesariamente mata a quien lo despertó.

Después de la invocación, el kernel imprime una tabla o procesos que incluyen 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

y más adelante vemos que nuestro pequeño main.outmurió en la invocación anterior:

[ 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

Este registro menciona que score 865tuvo ese proceso, presumiblemente el puntaje más alto (peor) de OOM Killer como se menciona en:¿Cómo decide el asesino OOM qué proceso matar primero?

También es interesante que aparentemente todo sucedió tan rápido que antes de que se contabilizara la memoria liberada, oomel proceso la despertó nuevamente DeadlineMonitor:

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

y esta vez eso eliminó algún proceso de Chromium, que generalmente es el que acapara la memoria normal de mi computadora:

[ 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

Probado en Ubuntu 19.04, kernel de Linux 5.0.0.

información relacionada