GNU/Linux >> Tutoriels Linux >  >> Linux

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

RSS est la quantité de mémoire dont ce processus dispose actuellement dans la mémoire principale (RAM). VSZ est la quantité totale de mémoire virtuelle dont dispose le processus. Cela inclut tous les types de mémoire, à la fois en RAM et en échange. Ces chiffres peuvent être faussés car ils incluent également des bibliothèques partagées et d'autres types de mémoire. 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.

Si vous avez besoin d'avoir une idée plus détaillée de l'empreinte mémoire d'un processus, vous avez quelques options. Vous pouvez passer par /proc/$PID/map et éliminez les choses que vous n'aimez pas. S'il s'agit de bibliothèques partagées, le calcul peut 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 le [heap] entrée dans le map dossier. La taille que le noyau a allouée au tas du processus peut ou non refléter le nombre exact d'octets que le processus a demandé à allouer. Il y a des détails infimes, des éléments internes du noyau et des optimisations qui peuvent perturber cela. Dans un monde idéal, ce sera autant que votre processus a besoin, arrondi au multiple le plus proche de la taille de la page système (getconf PAGESIZE vous dira ce que c'est - sur les PC, c'est probablement 4 096 octets).

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

Veuillez noter que, puisque vous pouvez également comparer les runtimes, que valgrind rendra vos programmes très légèrement plus lents (mais probablement dans vos tolérances).


Exemple exécutable minimal

Pour que cela ait du sens, il faut comprendre les bases de la pagination :https://stackoverflow.com/questions/18431261/how-does-x86-paging-work et notamment que l'OS peut allouer de la mémoire virtuelle via des tables de pages/ sa mémoire interne (mémoire virtuelle VSZ) avant qu'il ne dispose d'un stockage de sauvegarde sur RAM ou sur disque (mémoire résidente RSS).

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

  • alloue plus de RAM que notre mémoire physique avec mmap
  • écrit un octet sur chaque page pour s'assurer que chacune de ces pages passe de la mémoire virtuelle uniquement (VSZ) à la mémoire réellement utilisée (RSS)
  • vérifie l'utilisation de la mémoire du processus avec l'une des méthodes mentionnées à :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 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ù :

  • 0x1000000000 ==64 Gio :2 x la RAM physique de 32 Gio de mon ordinateur
  • 0x200000000 ==8GiB :imprime la mémoire tous les 8GiB, nous devrions donc obtenir 4 impressions avant le crash à environ 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory :requis pour Linux pour nous permettre de faire un appel mmap plus grand que la RAM physique :https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

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

Statut de sortie :

137

ce qui, par la règle du numéro de signal 128 +, signifie que nous avons le numéro de signal 9 , dont man 7 signal dit est 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 (ps les valeurs sont en KiB) après le mmap.
  • L'"utilisation réelle de la mémoire RSS" n'augmente paresseusement que lorsque nous touchons les pages. Par exemple :
    • sur la première impression, nous avons extra_memory_committed 0 , ce qui signifie que nous n'avons encore touché aucune page. RSS est un petit 1648 KiB qui a été alloué pour le démarrage normal du programme comme la zone de texte, les variables globales, etc.
    • sur la deuxième impression, nous avons écrit à 8388608 KiB == 8GiB valeur de pages. En conséquence, RSS a augmenté d'exactement 8 GIB pour atteindre 8390256 KiB == 8388608 KiB + 1648 KiB
    • Le RSS continue d'augmenter par paliers de 8 Gio. La dernière impression montre environ 24 Gio de mémoire, et avant que 32 Gio ne puissent être imprimés, le tueur de MOO a tué le processus

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

Journaux de tueur OOM

Notre dmesg commandes ont montré les journaux du tueur OOM.

Une interprétation exacte de ceux-ci a été demandée à :

  • https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs mais jetons un coup d'œil ici.
  • https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages

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 voyons donc qu'il est intéressant de noter que c'est le démon MongoDB qui s'exécute toujours sur mon ordinateur portable en arrière-plan qui a d'abord déclenché le tueur OOM, probablement lorsque le pauvre essaie d'allouer de la mémoire.

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

Après l'invocation, le noyau imprime une table ou des processus comprenant le 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 on voit que notre petit main.out a effectivement été tué lors de l'invocation précédente :

[ 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 log mentionne le score 865 lequel ce processus avait, vraisemblablement le score de tueur de MOO le plus élevé (le pire), comme mentionné à :Comment le tueur de MOO décide-t-il quel processus tuer en premier ?

Fait également intéressant, tout s'est apparemment passé si vite qu'avant que la mémoire libérée ne soit prise en compte, le oom a été réveillé à nouveau par le DeadlineMonitor processus :

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

et cette fois, cela a tué un processus Chromium, qui est généralement le porc normal de la mémoire 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é dans Ubuntu 19.04, noyau Linux 5.0.0.


Linux
  1. Comment vérifier la taille de la RAM dans la ligne de commande Linux en Go

  2. Commandes Linux :explorer la mémoire virtuelle avec vmstat

  3. Augmenter la taille du disque virtuel de la machine virtuelle Windows10 sur QEMU-KVM

  4. Linux - Besoin d'explications sur la taille de l'ensemble résident/taille virtuelle ?

  5. Comment définir la limite de taille de téléchargement de fichier dans Apache ?

Swappiness sous Linux :tout ce que vous devez savoir

Comment étendre la taille du disque de la machine virtuelle KVM sous Linux

Comment configurer les hôtes virtuels Apache sur Ubuntu 18.04

Comment configurer des hôtes virtuels Apache sur Ubuntu 20.04

Gestion de la mémoire Linux - Mémoire virtuelle et pagination à la demande

Trouver la taille de la RAM sous Linux