Resident Set Size/Virtual Size에 대한 설명이 필요합니다.

Resident Set Size/Virtual Size에 대한 설명이 필요합니다.

나는 이것이 pidstat프로세스를 모니터링하는 데 좋은 도구라는 것을 알았습니다. 특정 프로세스의 평균 메모리 사용량을 계산하고 싶습니다. 다음은 몇 가지 출력 예입니다.

02:34:36 PM       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
02:34:37 PM      7276      2.00      0.00  349212 210176   7.14  scalpel

(이는 의 출력의 일부입니다 pidstat -r -p 7276.)

평균 메모리 소비를 계산하려면 RSS(Resident Set Size) 또는 VSZ(Virtual Size) 정보를 사용해야 합니까? Wikipedia와 포럼에서 몇 가지 내용을 읽었지만 차이점을 완전히 이해하지 못합니다. 게다가 그 어느 것도 신뢰할 만한 것이 없는 것 같습니다. 그렇다면 메모리 사용량을 확인하기 위해 프로세스를 어떻게 모니터링할 수 있습니까?

이 문제에 대한 도움이 도움이 될 것입니다.

답변1

RSS는 이 프로세스가 현재 주 메모리(RAM)에 얼마나 많은 메모리를 갖고 있는지를 나타냅니다. VSZ는 프로세스의 총 가상 메모리 양입니다. 여기에는 RAM 및 교체된 모든 유형의 메모리가 포함됩니다. 이 수치에는 공유 라이브러리 및 기타 유형의 메모리도 포함되어 있기 때문에 왜곡될 수 있습니다. 500개의 인스턴스를 bash실행할 수 있으며 해당 메모리 공간의 총 크기는 RSS 또는 VSZ 값의 합계가 아닙니다.

프로세스의 메모리 사용량에 대해 더 자세한 정보가 필요한 경우 몇 가지 옵션이 있습니다. /proc/$PID/map마음에 들지 않는 내용을 살펴보고 제거할 수 있습니다 . 공유 라이브러리라면 필요에 따라 계산이 복잡해질 수 있습니다.

프로세스의 힙 크기에만 관심이 있다면 언제든지 파일 [heap]의 항목을 구문 분석할 수 있습니다 map. 커널이 프로세스 힙에 할당한 크기는 프로세스가 가지고 있는 정확한 바이트 수를 반영할 수도 있고 반영하지 않을 수도 있습니다.물었다할당됩니다. 이를 방해할 수 있는 미세한 세부 사항, 커널 내부 및 최적화가 있습니다. 이상적인 세계에서는 시스템 페이지 크기의 가장 가까운 배수로 반올림하여 프로세스에 필요한 만큼이 될 것입니다. getconf PAGESIZEPC에서는 아마도 4,096바이트일 것입니다.

프로세스의 메모리 양을 확인하려면할당됨, 가장 좋은 방법 중 하나는 커널 측 측정 항목을 포기하는 것입니다. 대신 메커니즘을 사용하여 C 라이브러리의 힙 메모리 할당 해제 기능을 계측합니다 LD_PRELOAD. 개인적으로 나는 valgrind이런 종류의 정보를 얻기 위해 약간 학대합니다. (계측을 적용하려면 프로세스를 다시 시작해야 합니다.)

런타임을 벤치마킹할 수도 있으므로 valgrind프로그램이 약간 느려질 수 있습니다(그러나 아마도 허용 범위 내에서).

답변2

최소한의 실행 가능한 예

이를 이해하려면 페이징의 기본 사항을 이해해야 합니다.https://stackoverflow.com/questions/18431261/how-does-x86-paging-work특히 OS는 실제로 RAM이나 디스크(RSS 상주 메모리)에 백업 저장소를 갖기 전에 페이지 테이블/내부 메모리 장부 유지(VSZ 가상 메모리)를 통해 가상 메모리를 할당할 수 있습니다.

이제 이를 실제로 관찰하기 위해 다음과 같은 프로그램을 만들어 보겠습니다.

  • 물리적 메모리보다 더 많은 RAM을 할당합니다.mmap
  • 각 페이지가 가상 전용 메모리(VSZ)에서 실제로 사용된 메모리(RSS)로 이동되도록 각 페이지에 1바이트를 씁니다.
  • 다음에 언급된 방법 중 하나를 사용하여 프로세스의 메모리 사용량을 확인합니다.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 업스트림.

컴파일하고 실행합니다:

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

어디:

프로그램 출력:

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

종료 상태:

137

어느128 + 신호 번호 규칙신호 번호를 얻었음을 의미 9합니다 man 7 signal.시그킬, 이는 Linux에서 전송됩니다.메모리 부족 킬러.

출력 해석:

  • VSZ 가상 메모리는 mmap 후에도 일정하게 유지됩니다 printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( 값은 KiB 단위임).ps
  • RSS "실제 메모리 사용량"은 페이지를 터치할 때만 느리게 증가합니다. 예를 들어:
    • 첫 번째 인쇄에는 이 있습니다 extra_memory_committed 0. 이는 아직 페이지를 건드리지 않았음을 의미합니다. RSS는 1648 KiB텍스트 영역, 전역 등과 같은 정상적인 프로그램 시작을 위해 할당된 작은 크기입니다 .
    • 8388608 KiB == 8GiB두 번째 인쇄에서는 상당한 페이지 에 글을 썼습니다 . 결과적으로 RSS는 정확히 8GIB 증가하여8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS는 8GiB 단위로 계속 증가하고 있습니다. 마지막 인쇄에는 약 24GiB의 메모리가 표시되며 32GiB가 인쇄되기 전에 OOM 킬러가 프로세스를 종료했습니다.

또한보십시오:Resident Set Size/Virtual Size에 대한 설명이 필요합니다.

OOM 킬러 로그

우리의 dmesg명령은 OOM 킬러 로그를 표시했습니다.

이에 대한 정확한 해석은 다음에서 요청되었습니다.

로그의 첫 번째 줄은 다음과 같습니다.

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

그래서 우리는 흥미롭게도 처음으로 OOM 킬러를 촉발한 것은 아마도 불량자가 메모리를 할당하려고 할 때 항상 내 노트북에서 백그라운드로 실행되는 MongoDB 데몬이었다는 것을 알 수 있습니다.

그러나 OOM 킬러가 반드시 자신을 깨운 사람을 죽이는 것은 아닙니다.

호출 후 커널은 다음을 포함하는 테이블 또는 프로세스를 인쇄합니다 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

그리고 더 나아가 우리는 main.out이전 호출에서 우리 자신의 작은 것이 실제로 죽임을 당했다는 것을 알 수 있습니다:

[ 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

이 로그에는 score 865다음에서 언급된 것처럼 해당 프로세스의 아마도 가장 높은(최악) OOM 킬러 점수가 무엇인지 언급되어 있습니다.OOM 킬러는 어떤 프로세스를 먼저 종료할지 어떻게 결정합니까?

또한 흥미롭게도 모든 일이 너무 빨리 일어나서 해제된 메모리가 계산되기 전에 프로세스 oom에 의해 다시 깨어났습니다 DeadlineMonitor.

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

이번에는 일반적으로 내 컴퓨터의 메모리를 많이 차지하는 일부 Chromium 프로세스가 종료되었습니다.

[ 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

Ubuntu 19.04, Linux 커널 5.0.0에서 테스트되었습니다.

관련 정보