GNU/Linux >> Linux の 問題 >  >> Linux

Linux プロセスの RAM メモリ最高水準点のロギング

/proc/[pid]/status を見てください 、特にこのパラメータ。

  • VmHWM:ピーク常駐セット サイズ (「最高水準点」)。

または、 /usr/bin/time -v を使用できます 指図。以下はそのアウトの例です:

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

プロセスの RAM ハイウォーター マーク情報は、カーネルによって既に収集されています (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").
(...)

注意が必要なのは、プロセスが終了する直前にこの値を読み取る必要があることです .

私はさまざまなアプローチを試しました(詳細は回答の最後にあります)が、私にとってうまくいったのはCでの実装でした:

  • logmemory fork() を呼び出す 子プロセスを作成します。

  • 子プロセスは ptrace() を呼び出します 親プロセス (これは logmemory ) は、子プロセスがシステム コールを実行するたびに通知されます。

  • 子プロセスは execvp() を使用します mycmd を実行する .

  • logmemory 気長に通知を待ちます。その場合、mycmd かどうかをチェックします。 exit_group を呼び出しました .その場合、/proc/<pid>/status と表示されます。 、値を mem.log にコピーします そして子供から離れます。それ以外の場合は logmemory mycmd を許可 続行して、次の通知まで待ちます。

欠点は、ptrace() 監視対象のプログラムを遅くします 、以下にいくつかの比較を示します。

このバージョンの logmemory VmHWM をログに記録するだけではありません だけでなく:

  • VmPeak (すべてのコード、データ、および共有ライブラリに加えて、スワップ アウトされたページとマップされたが使用されていないページを含むピーク仮想メモリ サイズ)

  • タイムスタンプ

  • コマンド名と引数

これは確実に改善できるコードです - 私は C に堪能ではありませんが、意図したとおりに動作します (32 ビット Ubuntu 12.04 および 64 ビット 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;
}

logmemory.c として保存します 次のようにコンパイルします:

$ gcc logmemory.c -o logmemory

次のように実行します:

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

logmemory をテストするためにこの C プログラムを書きました の精度:

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

通常どおりコンパイルし、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

200 MB の使用量を正しく報告しています。

補足:time (少なくとも Ubuntu 12.04 では) 驚くべきことに、カーネルが報告するものとは大きく異なる値を出力します:

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

どこで M (man time より) ):

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

前述のように、logmemory のため、これには代償が伴います。 監視対象プログラムの実行を遅くします。例:

$ 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

私が (失敗して) 試したその他のアプローチは次のとおりです。

  • /proc/<pid>/status を読み取るためのバックグラウンド プロセスを作成するシェル スクリプト mycmd の間 実行します。

  • fork して mycmd を実行する C プログラム ただし、子供がゾンビになるまで一時停止するため、ptrace を回避します そしてそれが生み出すオーバーヘッド。いいアイデアだと思いましたが、残念ながら VmHWMVmPeak /proc/<pid>/status からは利用できなくなりました


このトピックはかなり古いものですが、cgroups Linux カーネル機能から生まれた別のプロジェクトを共有したいと思います.

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

<ブロック引用>

cgmemtime は、プロセスとその子孫プロセスの最高水準の RSS+CACHE メモリ使用量を測定します。

これを可能にするために、プロセスを独自の cgroup に配置します。

たとえば、プロセス A は 10 MiB を割り当て、20 MiB を割り当てる子 B をフォークし、30 MiB を割り当てる子 C をフォークします。 3 つのプロセスはすべて、その割り当てが対応する RSS (常駐セット サイズ) のメモリ使用量になる時間枠を共有します。

問題は、A を実行した結果、実際にどれくらいのメモリが使用されるかということです。

答え:60 MiB

cgmemtime は、そのような質問に答えるツールです。


Linux
  1. Linux –単一のLinuxプロセスのメモリ使用量を制限しますか?

  2. メモリ使用量のログを記録できるツール?

  3. Linux –常駐セットサイズ/仮想サイズについての説明が必要ですか?

  1. LinuxでL1、L2、L3キャッシュ、RAMのサイズを知る方法はありますか?

  2. Linux で合計 RAM サイズとメモリ使用量を確認する方法

  3. Linux のプロセスあたりの最大スレッド数は?

  1. Linux/Unix プロセスのピーク時のメモリ使用量

  2. C での現在のプロセスのメモリ使用量

  3. 単一の Linux プロセスのメモリ使用量を制限する