상주 세트 크기/가상 크기에 대한 설명이 필요합니다.

상주 세트 크기/가상 크기에 대한 설명이 필요합니다.

나는 이것이 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 PAGESIZE이 값을 통해 알 수 있습니다. PC에서는 아마도 4,096바이트일 것입니다).

프로세스의 메모리 양을 확인하려면할당된, 가장 좋은 접근 방식 중 하나는 커널 측 측정 항목을 포기하는 것입니다. 대신 이 메커니즘을 사용하여 C 라이브러리의 힙 메모리(사용 가능) 할당 기능을 감지할 수 있습니다 LD_PRELOAD. 개인적으로 저는 valgrind이런 것에 대한 정보를 얻는 것을 일종의 남용합니다 . (앱을 감지하려면 프로세스를 다시 시작해야 합니다.)

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

답변2

최소한의 실행 가능한 예

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

이제 이것이 실제로 작동하는 것을 보기 위해 프로그램을 만들어 보겠습니다.

메인 프로그램

#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에서 전송됨메모리 부족 킬러.

출력 설명:

  • mmap 이후 VSZ 가상 메모리는 일정하게 유지됩니다 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 킬러가 프로세스를 종료했습니다.

또한보십시오:상주 세트 크기/가상 크기에 대한 설명이 필요합니다.

OOM 킬러 로그

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

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

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

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

그래서 흥미롭게도 우리가 발견한 것은 내 노트북의 백그라운드에서 항상 실행되고 있던 MongoDB 데몬이 아마도 메모리를 할당하려고 할 때 OOM 킬러를 먼저 트리거했다는 것입니다.

하지만 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에서 테스트되었습니다.

관련 정보