Necesita explicación sobre Tamaño de conjunto residente / Tamaño virtual

61

Descubrí que pidstatsería una buena herramienta para monitorear procesos. Quiero calcular el uso promedio de memoria de un proceso en particular. Aquí hay un ejemplo de salida:

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 de la salida de pidstat -r -p 7276).

¿Debo usar la información de Tamaño de conjunto residente (RSS) o 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 confiable. Entonces, ¿cómo puedo monitorear un proceso para obtener su uso de memoria?

Cualquier ayuda sobre este asunto sería útil.

Flanfl
fuente

Respuestas:

63

RSS es la cantidad de memoria que este proceso tiene actualmente en la memoria principal (RAM). VSZ es la cantidad de memoria virtual que tiene el proceso en total. Esto incluye todos los tipos de memoria, tanto en RAM como intercambiados. Estos números pueden sesgarse porque también incluyen bibliotecas compartidas y otros tipos de memoria. Puede tener quinientas instancias de 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 dependiendo de sus necesidades (que creo recordar).

Si solo le importa el tamaño de almacenamiento dinámico del proceso, siempre puede analizar la [heap]entrada en el maparchivo. El tamaño que el núcleo ha asignado para el montón del proceso puede o no reflejar el número exacto de bytes que el proceso ha pedido que se asigne. Hay detalles minuciosos, elementos internos del núcleo y optimizaciones que pueden estropear esto. En un mundo ideal, será todo lo que su proceso necesite, redondeado al múltiplo más cercano del tamaño de página del sistema ( getconf PAGESIZEle dirá cuál es: en las PC, probablemente sea de 4.096 bytes).

Si desea ver cuánta memoria ha asignado un proceso , una de las mejores maneras es renunciar a las métricas del lado del kernel. En su lugar, se instrumentan las funciones de asignación (des) de memoria de montón 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 la aplicación de la instrumentación requerirá reiniciar el proceso).

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

Alexios
fuente
¡Muchas gracias! Voy a investigar las diferentes opciones. ¡Has sido más que útil! :)
Flanfl
"Puede tener quinientas instancias de bash ejecutándose, y el tamaño total de su huella de memoria no será la suma de sus valores RSS o VSZ". Pero, ¿será la suma de sus valores RSS una buena aproximación? Como suma de la columna residente de statm, no necesito un valor exacto súper fiable, pero necesito saber alto nivel la cantidad de memoria mis procesos Java están utilizando
iloveretards
3
En Ubuntu, ¿ /proc/$PID/mapses la diferencia de error tipográfico o de distribución?
dolzenko
1

Ejemplo ejecutable mínimo

Para que esto tenga sentido, debe comprender los conceptos básicos de la paginación: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work y, en particular, que el sistema operativo puede asignar memoria virtual a través de tablas de páginas / su contabilidad interna (memoria virtual VSZ) antes de que realmente tenga 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 aguas arriba .

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 según la regla del número de señal 128+ significa que obtuvimos el número de señal 9, que man 7 signaldice que es SIGKILL , que es enviado por el asesino de memoria insuficiente de Linux .

Interpretación de salida:

  • La memoria virtual VSZ permanece constante en printf '0x%X\n' 0x40009A4 KiB ~= 64GiB(los psvalores están en KiB) después del mmap.
  • El "uso de memoria real" de RSS aumenta perezosamente solo 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 se ha asignado para el inicio normal del programa como área de texto, globales, etc.
    • En la segunda impresión, hemos escrito 8388608 KiB == 8GiBpáginas por valor. Como resultado, RSS aumentó exactamente 8GIB a8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS continúa aumentando en incrementos de 8GiB. La última impresión muestra unos 24 GiB de memoria, y antes de que se pudieran imprimir 32 GiB, el asesino de OOM eliminó el proceso

Ver también: Necesito explicación sobre Tamaño de conjunto residente / Tamaño virtual

OOM troncos asesinos

Nuestros dmesgcomandos han mostrado los registros asesinos de OOM.

Se ha pedido una interpretación exacta de estos 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

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

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

Después de la invocación, el núcleo 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 propio pequeño main.outfue asesinado 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 lo score 865que ese proceso tuvo, presumiblemente el puntaje más alto (peor) del asesino OOM 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 contara la memoria liberada, el proceso oomdespertó nuevamente DeadlineMonitor:

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

y esta vez que acabó con el proceso de Chromium, que suele ser 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.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente