GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Protokollieren der RAM-Speicherobergrenze eines Linux-Prozesses

Schauen Sie sich /proc/[pid]/status an , speziell dieser Parameter.

  • VmHWM:Peak Resident Set Size ("High Water Mark").

Alternativ können Sie /usr/bin/time -v verwenden Befehl. Hier ist ein Beispiel dafür:

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

Die RAM-High-Watermark-Informationen für einen Prozess werden bereits vom Kernel für Sie gesammelt (ab 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").
(...)

Der knifflige Teil ist, dass dieser Wert kurz vor Beendigung des Prozesses gelesen werden sollte .

Ich habe verschiedene Ansätze ausprobiert (mehr dazu am Ende der Antwort) und der, der für mich funktioniert hat, war eine Implementierung in C:

  • logmemory ruft fork() auf um einen untergeordneten Prozess zu erstellen.

  • Der untergeordnete Prozess ruft ptrace() auf sodass der übergeordnete Prozess (das ist logmemory ) wird jedes Mal benachrichtigt, wenn das Kind einen Systemaufruf ausführt.

  • Der untergeordnete Prozess verwendet execvp() um mycmd auszuführen .

  • logmemory wartet geduldig auf eine Benachrichtigung. Wenn das der Fall ist, prüft es, ob mycmd aufgerufen exit_group . Wenn dies der Fall ist, lautet es /proc/<pid>/status , kopiert die Werte nach mem.log und löst sich vom Kind. Andernfalls logmemory erlaubt mycmd um fortzufahren und wartet bis zur nächsten Benachrichtigung.

Der Nachteil ist, dass die ptrace() verlangsamt das überwachte Programm , zeige ich unten einige Vergleiche.

Diese Version von logmemory protokolliert nicht nur VmHWM sondern auch:

  • VmPeak (Spitzengröße des virtuellen Speichers, die alle Code-, Daten- und gemeinsam genutzten Bibliotheken sowie Seiten enthält, die ausgelagert wurden, und Seiten, die zugeordnet, aber nicht verwendet wurden)

  • ein Zeitstempel

  • den Befehlsnamen und die Argumente

Dies ist der Code, der sicherlich verbessert werden kann - ich kenne mich mit C nicht aus. Er funktioniert jedoch wie vorgesehen (getestet auf einem 32-Bit-Ubuntu 12.04 und einem 64-Bit-SuSE Linux Enterprise Server 10 SP4):

// 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;
}

Speichern Sie es als logmemory.c und so kompilieren:

$ gcc logmemory.c -o logmemory

Führen Sie es so aus:

$ ./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

Ich habe dieses C-Programm geschrieben, um logmemory zu testen Genauigkeit von :

// 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;
}

Kompilieren Sie wie gewohnt und führen Sie es in logmemory aus :

$ 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

die korrekt 200 MB belegt meldet.

Nebenbei bemerkt:time (zumindest unter Ubuntu 12.04) gibt überraschenderweise einen Wert aus, der sich stark von dem unterscheidet, was der Kernel meldet:

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

wobei M (ab man time ):

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

Wie oben erwähnt, hat dies seinen Preis, denn logmemory verlangsamt die Ausführung des überwachten Programms, zum Beispiel:

$ 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

Andere Ansätze, die ich (erfolglos) ausprobiert habe, waren:

  • Ein Shell-Skript, das einen Hintergrundprozess zum Lesen von /proc/<pid>/status erstellt während mycmd läuft.

  • Ein C-Programm, das mycmd forkt und ausführt aber pausiert, bis das Kind ein Zombie ist, wodurch ptrace vermieden wird und der damit verbundene Overhead. Gute Idee, dachte ich, leider VmHWM und VmPeak sind ab /proc/<pid>/status nicht mehr erhältlich für einen Zombie.


Auch wenn das Thema schon ziemlich alt ist, möchte ich ein anderes Projekt teilen, das aus der cgroups-Linux-Kernel-Funktion hervorgegangen ist.

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

cgmemtime misst die Hochwasser-RSS+CACHE-Speichernutzung eines Prozesses und seiner untergeordneten Prozesse.

Um dies tun zu können, legt es den Prozess in seine eigene Kontrollgruppe.

Zum Beispiel weist Prozess A 10 MiB zu und verzweigt ein Kind B, das 20 MiB zuweist, und ein Kind C, das 30 MiB zuweist. Alle drei Prozesse teilen sich ein Zeitfenster, in dem ihre Zuordnungen zu einer entsprechenden RSS-Speichernutzung (resident set size) führen.

Die Frage ist nun:Wie viel Speicher wird tatsächlich verbraucht, wenn A ausgeführt wird?

Antwort:60 MiB

cgmemtime ist das Werkzeug, um solche Fragen zu beantworten.


Linux
  1. So überprüfen Sie die RAM-Größe in der Linux-Befehlszeile in GB

  2. Linux – Speichernutzung für einen einzelnen Linux-Prozess begrenzen?

  3. Tool, das die Protokollierung der Speichernutzung ermöglicht?

  4. Gibt es eine Möglichkeit, die Größe von L1-, L2-, L3-Cache und RAM in Linux zu ermitteln?

  5. So überprüfen Sie die gesamte RAM-Größe und Speichernutzung in Linux

Installation des RAM-Speichertesttools Memtest+ auf Redhat 7 Linux

Swapiness unter Linux:Alles, was Sie wissen müssen

Finden Sie die RAM-Größe in Linux

Maximale Anzahl von Threads pro Prozess in Linux?

Begrenzen Sie die Speichernutzung für einen einzelnen Linux-Prozess

Wie wird die Speichernutzung in Linux gemeldet?