GNU/Linux >> Belajar Linux >  >> Linux

mencatat memori RAM tanda air tinggi dari proses Linux

Lihatlah /proc/[pid]/status , khususnya parameter ini.

  • VmHWM:Ukuran set residen puncak ("tanda air tinggi").

Sebagai alternatif, Anda dapat menggunakan /usr/bin/time -v memerintah. Berikut ini contoh keluarnya:

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

Informasi tanda air tinggi RAM untuk suatu proses sudah dikumpulkan untuk Anda oleh kernel (dari 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").
(...)

Bagian rumitnya adalah nilai ini harus dibaca sesaat sebelum proses berakhir .

Saya mencoba pendekatan yang berbeda (lebih lanjut tentang itu di akhir jawaban) dan yang berhasil bagi saya adalah implementasi di C:

  • logmemory memanggil fork() untuk membuat proses anak.

  • Proses anak memanggil ptrace() sehingga proses induk (yaitu logmemory ) diberi tahu setiap kali anak menjalankan panggilan sistem.

  • Proses anak menggunakan execvp() untuk menjalankan mycmd .

  • logmemory sabar menunggu notifikasi. Jika demikian, ia akan memeriksa apakah mycmd memanggil exit_group . Jika demikian, terbaca /proc/<pid>/status , salin nilainya ke mem.log dan melepaskan diri dari anak. Jika tidak, logmemory memungkinkan mycmd untuk melanjutkan dan menunggu hingga notifikasi berikutnya.

Kelemahannya adalah ptrace() memperlambat program yang dipantau , saya tunjukkan beberapa perbandingan di bawah ini.

Versi logmemory ini tidak hanya mencatat VmHWM tetapi juga:

  • VmPeak (ukuran memori virtual puncak, yang mencakup semua kode, data, dan pustaka bersama ditambah halaman yang telah ditukar dan halaman yang telah dipetakan tetapi tidak digunakan)

  • cap waktu

  • nama perintah dan argumen

Ini adalah kodenya, yang pasti dapat ditingkatkan - saya tidak mahir dalam C. Namun, ini berfungsi sebagaimana mestinya (diuji pada Ubuntu 12.04 32-bit dan 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;
}

Simpan sebagai logmemory.c dan kompilasi seperti ini:

$ gcc logmemory.c -o logmemory

Jalankan seperti ini:

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

Saya menulis program C ini untuk menguji logmemory Akurasi:

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

Kompilasi seperti biasa dan jalankan di dalam 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

yang melaporkan penggunaan 200 MB dengan benar.

Sebagai catatan tambahan:time (setidaknya di Ubuntu 12.04) secara mengejutkan menghasilkan nilai yang sangat berbeda dari yang dilaporkan kernel:

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

di mana M (dari man time ):

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

Seperti disebutkan di atas, ini ada harganya, karena logmemory memperlambat eksekusi program yang dipantau, misalnya:

$ 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

Pendekatan lain yang saya (tidak berhasil) coba adalah:

  • Skrip shell yang membuat proses latar belakang untuk membaca /proc/<pid>/status sedangkan mycmd berjalan.

  • Program C yang melakukan fork dan mycmd exec tapi jeda sampai anak menjadi zombie, jadi hindari ptrace dan overhead yang diciptakannya. Ide bagus, pikir saya, sayangnya VmHWM dan VmPeak tidak lagi tersedia dari /proc/<pid>/status untuk zombie.


Meskipun topiknya sudah cukup lama, saya ingin berbagi proyek lain yang muncul dari fitur kernel Linux cgroups.

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

cgmemtime mengukur penggunaan memori RSS+CACHE air tinggi dari suatu proses dan proses turunannya.

Untuk dapat melakukannya, ia memasukkan proses ke dalam grupnya sendiri.

Misalnya proses A mengalokasikan 10 MiB dan membagi anak B yang mengalokasikan 20 MiB dan membagi anak C yang mengalokasikan 30 MiB. Ketiga proses berbagi jendela waktu saat alokasinya menghasilkan penggunaan memori RSS (resident set size) yang sesuai.

Pertanyaannya sekarang adalah:Berapa banyak memori yang sebenarnya digunakan sebagai hasil dari menjalankan A?

Jawaban:60 MiB

cgmemtime adalah alat untuk menjawab pertanyaan semacam itu.


Linux
  1. Cara Memeriksa Ukuran Ram di Baris Perintah Linux di Gb

  2. Ny Cara Mengetahui Ukuran Cache L1, L2, L3 Dan Ram Di Linux?

  3. Cara Memeriksa Ukuran Total RAM dan Penggunaan Memori di Linux

  1. Ambil penggunaan CPU dan penggunaan memori dari satu proses di Linux?

  2. Linux:cari tahu proses apa yang menggunakan semua RAM?

  3. Batasi penggunaan memori untuk satu proses Linux

  1. Linux – Batasi Penggunaan Memori Untuk Proses Linux Tunggal?

  2. Grafik penggunaan memori proses di Linux

  3. Buang memori proses linux ke file