Besoin d'explications sur la taille de l'ensemble de résidents / la taille virtuelle

61

J'ai trouvé que ce pidstatserait un bon outil pour surveiller les processus. Je veux calculer l'utilisation moyenne de la mémoire d'un processus particulier. Voici un exemple de sortie:

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

(Cela fait partie de la sortie de pidstat -r -p 7276.)

Devrais-je utiliser les informations RSS (Resident Set Size) ou Virtual Size (VSZ) pour calculer la consommation moyenne de mémoire? J'ai lu quelques informations sur Wikipedia et sur des forums, mais je ne suis pas sûr de bien comprendre les différences. De plus, il semble qu'aucun d'entre eux ne soit fiable. Alors, comment puis-je surveiller un processus pour obtenir son utilisation de la mémoire?

Toute aide à ce sujet serait utile.

Flanfl
la source

Réponses:

63

RSS correspond à la quantité de mémoire actuellement occupée par ce processus dans la mémoire principale (RAM). VSZ est la quantité de mémoire virtuelle totale du processus. Cela inclut tous les types de mémoire, à la fois en RAM et en permutation. Ces chiffres peuvent être biaisés car ils incluent également des bibliothèques partagées et d'autres types de mémoire. Vous pouvez avoir cinq cents instances en bashcours d' exécution, et la taille totale de leur empreinte mémoire ne sera pas la somme de leurs valeurs RSS ou VSZ.

Si vous devez vous faire une idée plus détaillée de l'encombrement mémoire d'un processus, vous avez quelques options. Vous pouvez parcourir /proc/$PID/mapet éliminer les choses que vous n'aimez pas. Si ce sont des bibliothèques partagées, le calcul pourrait devenir complexe en fonction de vos besoins (ce dont je pense me souvenir).

Si vous ne vous souciez que de la taille du tas du processus, vous pouvez toujours simplement analyser l' [heap]entrée dans le mapfichier. La taille allouée par le noyau pour le tas de processus peut ou non refléter le nombre exact d'octets que le processus a demandé d'allouer. Il y a des détails minutieux, des éléments internes du noyau et des optimisations qui peuvent en être la cause. Dans un monde idéal, vos processus seront au maximum, arrondis au multiple le plus proche de la taille de la page système ( getconf PAGESIZEvous en dira plus sur les PC, probablement 4 096 octets).

Si vous voulez voir la quantité de mémoire allouée par un processus , l'un des meilleurs moyens consiste à renoncer aux métriques côté noyau. Au lieu de cela, vous instrumentez les fonctions d'allocation (de) mémoire de tas de la bibliothèque C avec le LD_PRELOADmécanisme. Personnellement, j'abuse légèrement valgrindpour obtenir des informations sur ce genre de chose. (Notez que l'application de l'instrumentation nécessitera le redémarrage du processus.)

Notez que, étant donné que vous pouvez également effectuer des analyses comparatives des temps d'exécution, valgrindvos programmes seront légèrement plus lents (mais probablement dans les limites de vos tolérances).

Alexios
la source
Merci beaucoup! Je vais étudier les différentes options. Vous avez été plus que serviable! :)
Flanfl
"Vous pouvez avoir cinq cents instances de bash en cours d'exécution, et la taille totale de leur empreinte mémoire ne sera pas la somme de leurs valeurs RSS ou VSZ." Mais la somme de leurs valeurs RSS sera-t-elle une bonne approximation? Comme la somme des colonnes résidentes de statm, je n'ai pas besoin d'une valeur exacte extrêmement fiable, mais j'ai besoin de connaître le niveau élevé de mémoire utilisée par mes processus Java
iloveretards.
3
Sur Ubuntu, /proc/$PID/mapss'agit-il d'une différence typo ou distro?
dolzenko
1

Exemple minimal exécutable

Pour que cela ait un sens, vous devez comprendre les bases de la pagination: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work et en particulier le fait que le système d'exploitation peut allouer de la mémoire virtuelle via des tables de pages / sa comptabilité interne (mémoire virtuelle VSZ) avant d’avoir une sauvegarde sur mémoire vive ou sur disque (mémoire résidente RSS).

Maintenant, pour observer cela en action, créons un programme qui:

principal 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 en amont .

Compiler et exécuter:

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

où:

Sortie du programme:

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

Etat de sortie:

137

ce qui, selon la règle des 128 + signaux, signifie que nous avons obtenu le numéro du signal 9, qui man 7 signalest SIGKILL , qui est envoyé par le tueur de mémoire insuffisante de Linux .

Interprétation de sortie:

  • La mémoire virtuelle VSZ reste constante à printf '0x%X\n' 0x40009A4 KiB ~= 64GiB(les psvaleurs sont en Ko) après le mmap.
  • RSS "utilisation réelle de la mémoire" augmente paresseusement que lorsque nous touchons les pages. Par exemple:
    • nous en avons sur le premier tirage, ce extra_memory_committed 0qui signifie que nous n’avons encore touché aucune page. RSS est un petit 1648 KiBfichier qui a été alloué au démarrage normal du programme, comme la zone de texte, les globaux, etc.
    • sur le deuxième tirage, nous avons écrit pour une 8388608 KiB == 8GiBvaleur de pages. En conséquence, RSS a augmenté d’exactement 8 GIB pour atteindre8390256 KiB == 8388608 KiB + 1648 KiB
    • Le flux RSS continue d'augmenter par incréments de 8 Go. La dernière impression indique environ 24 Go de mémoire et avant que 32 Go puissent être imprimés, le tueur de MOO a mis fin au processus.

Voir aussi: Besoin d'explications sur la taille de l'ensemble résident / la taille virtuelle

Les journaux tueurs de MOO

Nos dmesgcommandes ont montré les journaux tueurs de MOO.

Une interprétation exacte de ceux-ci a été posée à l'adresse suivante:

La toute première ligne du journal était:

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

Nous constatons donc que c’est intéressant, c’est le démon MongoDB qui s’exécute toujours sur l’arrière-plan de mon ordinateur portable qui a d’abord déclenché le tueur de MOO, probablement lorsque le pauvre essaie d’allouer de la mémoire.

Cependant, le tueur de MOO ne tue pas nécessairement celui qui l'a réveillé.

Après l’appel, le noyau imprime une table ou des processus comprenant 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

et plus loin nous voyons que notre propre petit main.outa été tué lors de la précédente invocation:

[ 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

Ce journal mentionne le résultat score 865le plus élevé (le plus mauvais) obtenu par ce processus, comme indiqué à: Comment le tueur OOM décide-t-il quel processus tuer en premier?

Il est également intéressant de noter que tout s’est apparemment passé si vite qu’avant la prise en compte de la mémoire libérée, oomle DeadlineMonitorprocessus l’ a réveillé :

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

et cette fois-ci qui a tué un processus de chrome, qui est généralement la mémoire normale de mon ordinateur:

[ 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

Testé sous Ubuntu 19.04, noyau Linux 5.0.0.

Ciro Santilli 改造 中心 六四 事件
la source