GNU/Linux >> Tutoriels Linux >  >> Linux

enregistrement de la marque d'eau haute de la mémoire RAM d'un processus Linux

Jetez un oeil à /proc/[pid]/status , en particulier ce paramètre.

  • VmHWM :Taille maximale de l'ensemble résident ("pointe haute").

Alternativement, vous pouvez utiliser /usr/bin/time -v commande. Voici un exemple de sa sortie :

Command exited with non-zero status 1
    Command being timed: "xz -9ek access_log.3 access_log.xz"
    User time (seconds): 6.96
    System time (seconds): 0.34
    Percent of CPU this job got: 99%
    Elapsed (wall clock) time (h:mm:ss or m:ss): 0:07.34
    Average shared text size (kbytes): 0
    Average unshared data size (kbytes): 0
    Average stack size (kbytes): 0
    Average total size (kbytes): 0
  **Maximum resident set size (kbytes): 383456**
    Average resident set size (kbytes): 0
    Major (requiring I/O) page faults: 0
    Minor (reclaiming a frame) page faults: 24000
    Voluntary context switches: 3
    Involuntary context switches: 225
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size (bytes): 4096
    Exit status: 1

Les informations sur la marque d'eau haute de la RAM pour un processus sont déjà collectées pour vous par le noyau (à partir de man proc ):

/proc/[pid]/status
Provides much of the information in /proc/[pid]/stat and /proc/[pid]/statm in a format that's easier for humans to parse.
(...)
* VmHWM: Peak resident set size ("high water mark").
(...)

La partie délicate est que cette valeur doit être lue un instant avant la fin du processus .

J'ai essayé différentes approches (plus à ce sujet à la fin de la réponse) et celle qui a fonctionné pour moi était une implémentation en C :

  • logmemory invoque fork() pour créer un processus enfant.

  • Le processus enfant appelle ptrace() afin que le processus parent (qui est logmemory ) est notifié chaque fois que l'enfant exécute un appel système.

  • Le processus enfant utilise execvp() pour exécuter mycmd .

  • logmemory attend patiemment une notification. Lorsque c'est le cas, il vérifie si mycmd appelé exit_group . Si c'est le cas, il lit /proc/<pid>/status , copie les valeurs dans mem.log et se détache de l'enfant. Sinon, logmemory autorise mycmd pour continuer et attend la prochaine notification.

L'inconvénient est que le ptrace() ralentit le programme surveillé , je montre quelques comparaisons ci-dessous.

Cette version de logmemory non seulement enregistre VmHWM mais aussi :

  • VmPeak (taille maximale de la mémoire virtuelle, qui inclut tout le code, les données et les bibliothèques partagées, ainsi que les pages qui ont été échangées et les pages qui ont été mappées mais non utilisées)

  • un horodatage

  • le nom de la commande et les arguments

Voici le code, qui peut sûrement être amélioré - je ne maîtrise pas le C. Il fonctionne cependant comme prévu (testé sur un Ubuntu 12.04 32 bits et un SuSE Linux Enterprise Server 10 SP4 64 bits) :

// logmemory.c
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <syscall.h>
#include <sys/reg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define STRINGLENGTH 2048

int main(int argc, char **argv)
{   
    pid_t child_pid;
    long syscall;
    int status, index;
    FILE *statusfile, *logfile;
    char opt, statusfile_path[STRINGLENGTH], line[STRINGLENGTH], command[STRINGLENGTH], logfile_path[STRINGLENGTH] = "";
    time_t now;
    extern char *optarg;
    extern int optind;

    // Error checking
    if (argc == 1) {
        printf("Error: program to execute is missing. Exiting...\n");
        return 0;
    }
    // Get options
    while ((opt = getopt (argc, argv, "+o:")) != -1)
        switch (opt) {
            case 'o':
                strncpy(logfile_path, optarg, 2048);
                break;
            case ':':
                fprintf (stderr, "Aborting: argument for option -o is missing\n");
                return 1;
            case '?':
                fprintf (stderr, "Aborting: only valid option is -o\n");
                return 1;
    }
    // More error checking
    if (!strcmp(logfile_path, "")) {
        fprintf(stderr, "Error: log filename can't be empty\n");
        return 1;
    }
    child_pid = fork();
    // The child process executes this:
    if (child_pid == 0) {
        // Trace child process:
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        // Execute command using $PATH
        execvp(argv[optind], (char * const *)(argv+optind));

    // The parent process executes this:
    } else {
        // Loop until child process terminates
        do {
            // Set ptrace to stop when syscall is executed
            ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
            wait(&status);
            // Get syscall number
            syscall = ptrace(PTRACE_PEEKUSER, child_pid,
#ifdef __i386__
                          4 * ORIG_EAX,
#else
                          8 * ORIG_RAX,
#endif
                          NULL);
        } while (syscall != SYS_exit_group);

        // Construct path to status file and check whether status and log file can be opened
        snprintf(statusfile_path, STRINGLENGTH, "/proc/%d/status", child_pid);
        if ( !(logfile = fopen(logfile_path, "a+")) || !(statusfile = fopen(statusfile_path, "r")) ) {
            ptrace(PTRACE_DETACH, child_pid, NULL, NULL);
            return 1;
        }

        // Copy timestamp and command to logfile
        now = time(NULL);
        fprintf(logfile, "Date: %sCmd: ", asctime(localtime(&now)));
        for (index = optind; index < argc; index++)
           fprintf(logfile, " %s", argv[index]);
        fprintf(logfile, "\n");

        // Read status file line by line and copy lines containing VmPeak and VmHWM to logfile
        while (fgets(line, STRINGLENGTH, statusfile)) {
            if (strstr(line,"VmPeak") || strstr(line,"VmHWM"))
                fprintf(logfile, "%s", line);
        }
        fprintf(logfile, "\n");

        // Close files
        fclose(statusfile);
        fclose(logfile);

        // Detach from child process
        ptrace(PTRACE_DETACH, child_pid, NULL, NULL);
    }
    return 0;
}

Enregistrez-le sous logmemory.c et compilez comme ceci :

$ gcc logmemory.c -o logmemory

Exécutez-le comme ceci :

$ ./logmemory 
Error: program to execute is missing. Exiting...
$ ./logmemory -o mem.log ls -l
(...)
$ ./logmemory -o mem.log free
             total       used       free     shared    buffers     cached
Mem:       1025144     760660     264484          0       6644     143980
-/+ buffers/cache:     610036     415108
Swap:      1046524     544228     502296
$ ./logmemory -o mem.log find /tmp -name \*txt
(...)
$ cat mem.log
Date: Mon Feb 11 21:17:55 2013
Cmd:  ls -l
VmPeak:     5004 kB
VmHWM:      1284 kB

Date: Mon Feb 11 21:18:01 2013
Cmd:  free
VmPeak:     2288 kB
VmHWM:       448 kB

Date: Mon Feb 11 21:18:26 2013
Cmd:  find /tmp -name *txt
VmPeak:     4700 kB
VmHWM:       908 kB

J'ai écrit ce programme C pour tester logmemory Précision :

// bigmalloc.c
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ITERATIONS 200
int main(int argc, char **argv)
{
    int i=0;
    for (i=0; i<ITERATIONS; i++) {
        void *m = malloc(1024*1024);
        memset(m,0,1024*1024);
    }
    return 0;
}

Compilez comme d'habitude et exécutez-le dans logmemory :

$ gcc bigmalloc.c -o bigmalloc
$ ./logmemory -o mem.log ./bigmalloc
$ tail mem.log

Date: Mon Feb 11 21:26:01 2013
Cmd:  ./bigmalloc
VmPeak:   207604 kB
VmHWM:    205932 kB

qui signale correctement 200 Mo utilisés.

En passant :time (au moins sur Ubuntu 12.04) génère étonnamment une valeur qui diffère largement de ce que rapporte le noyau :

$ /usr/bin/time --format %M ./bigmalloc
823872

M (à partir de man time ):

M   Maximum resident set size of the process during its lifetime, in Kilobytes.

Comme mentionné ci-dessus, cela a un prix, car logmemory ralentit l'exécution du programme surveillé, par exemple :

$ time ./logmemory -o mem.log ./bigmalloc
real    0m0.288s
user    0m0.000s
sys     0m0.004s
$ time ./bigmalloc
real    0m0.104s
user    0m0.008s
sys     0m0.092s

$ time find /var -name \*log
(...)
real    0m0.036s
user    0m0.000s
sys     0m0.032s
$ time ./logmemory -o mem.log find /var -name \*log
(...)
real    0m0.124s
user    0m0.000s
sys     0m0.052s

D'autres approches que j'ai (sans succès) essayées étaient :

  • Un script shell qui crée un processus d'arrière-plan pour lire /proc/<pid>/status tandis que mycmd court.

  • Un programme C qui bifurque et exécute le mycmd de mais s'arrête jusqu'à ce que l'enfant soit un zombie, évitant ainsi ptrace et les frais généraux qu'il crée. Bonne idée, j'ai pensé, malheureusement VmHWM et VmPeak ne sont plus disponibles à partir du /proc/<pid>/status pour un zombie.


Même si le sujet est assez ancien, je souhaite partager un autre projet qui a émergé de la fonctionnalité du noyau Linux cgroups.

https://github.com/gsauthof/cgmemtime :

cgmemtime mesure l'utilisation de la mémoire RSS+CACHE à haut débit d'un processus et de ses processus descendants.

Pour pouvoir le faire, il place le processus dans son propre groupe de contrôle.

Par exemple, le processus A alloue 10 MiB et bifurque un enfant B qui alloue 20 MiB et qui bifurque un enfant C qui alloue 30 MiB. Les trois processus partagent une fenêtre de temps où leurs allocations entraînent une utilisation correspondante de la mémoire RSS (taille de l'ensemble résident).

La question est maintenant :quelle quantité de mémoire est réellement utilisée suite à l'exécution de A ?

Réponse :60 Mio

cgmemtime est l'outil pour répondre à ces questions.


Linux
  1. Linux - Limiter l'utilisation de la mémoire pour un seul processus Linux ?

  2. Outil qui permet la journalisation de l'utilisation de la mémoire ?

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

  4. Ny Way pour connaître la taille du cache L1, L2, L3 et Ram sous Linux?

  5. Comment vérifier la taille totale de la RAM et l'utilisation de la mémoire sous Linux

Installation de l'outil de test de mémoire RAM Memtest+ sur Redhat 7 Linux

Swappiness sous Linux :tout ce que vous devez savoir

Trouver la taille de la RAM sous Linux

Utilisation de la mémoire du processus en cours en C

Comment vérifier la taille du tas pour un processus sous Linux

Comment l'utilisation de la mémoire est-elle signalée sous Linux ?