Linux 메모리 관리에서 RSS 및 VSZ 란 무엇입니까? 멀티 스레드 환경에서이 두 가지를 어떻게 관리하고 추적 할 수 있습니까?
답변
RSS는 Resident Set Size이며 해당 프로세스에 할당되고 RAM에있는 메모리 양을 표시하는 데 사용됩니다. 스왑 아웃 된 메모리는 포함되지 않습니다. 해당 라이브러리의 페이지가 실제로 메모리에있는 한 공유 라이브러리의 메모리가 포함됩니다. 모든 스택 및 힙 메모리가 포함됩니다.
VSZ는 가상 메모리 크기입니다. 스왑 아웃 된 메모리, 할당되었지만 사용되지 않은 메모리 및 공유 라이브러리의 메모리를 포함하여 프로세스가 액세스 할 수있는 모든 메모리를 포함합니다.
따라서 프로세스 A에 500K 이진이 있고 2500K의 공유 라이브러리에 연결되어 있고, 200K의 스택 / 힙 할당이 100K가 실제로 메모리에 있고 (나머지 스왑 또는 사용되지 않음) 실제로 1000K의 공유 라이브러리 만로드 한 경우 그리고 자체 바이너리의 400K :
RSS: 400K + 1000K + 100K = 1500K
VSZ: 500K + 2500K + 200K = 3200K
메모리의 일부가 공유되므로 많은 프로세스가이를 사용할 수 있으므로 모든 RSS 값을 합하면 시스템보다 더 많은 공간을 쉽게 확보 할 수 있습니다.
할당 된 메모리는 프로그램에서 실제로 사용될 때까지 RSS에 없을 수도 있습니다. 따라서 프로그램이 많은 양의 메모리를 미리 할당 한 다음 시간이 지남에 따라 사용하면 RSS가 올라가고 VSZ가 동일하게 유지되는 것을 볼 수 있습니다.
PSS (비례 세트 크기)도 있습니다. 이것은 현재 프로세스에서 사용하는 비율로 공유 메모리를 추적하는 새로운 측정입니다. 따라서 이전과 동일한 공유 라이브러리를 사용하는 두 개의 프로세스가있는 경우 :
PSS: 400K + (1000K/2) + 100K = 400K + 500K + 100K = 1000K
스레드는 모두 동일한 주소 공간을 공유하므로 각 스레드의 RSS, VSZ 및 PSS는 프로세스의 다른 모든 스레드와 동일합니다. linux / unix에서이 정보를 보려면 ps 또는 top을 사용하십시오.
이것보다 더 많은 방법이 있습니다. 더 많은 것을 배우려면 다음 참조를 확인하십시오.
- http://manpages.ubuntu.com/manpages/en/man1/ps.1.html
- https://web.archive.org/web/20120520221529/http://emilics.com/blog/article/mconsumption.html
참조 :
답변
RSS는 Resident Set Size (물리적 상주 메모리-현재 머신의 실제 메모리에서 공간을 차지하고 있음)이고 VSZ는 Virtual Memory Size (주소 공간이 할당 됨-프로세스의 메모리 맵에 할당 된 주소가 있지만 반드시 필요한 것은 아닙니다. 실제 메모리 뒤에 있습니다).
요즘 일반적인 가상 머신에서는 머신 관점의 실제 메모리가 실제 실제 메모리가 아닐 수도 있습니다.
답변
최소 실행 가능 예
이를 이해하려면 페이징의 기본 사항을 이해해야합니다. x86 페이징은 어떻게 작동합니까? 특히 OS는 실제로 RAM 또는 디스크 (RSS 상주 메모리)에 백업 스토리지를 갖기 전에 페이지 테이블 / 내부 메모리 북 보관 (VSZ 가상 메모리)을 통해 가상 메모리를 할당 할 수 있습니다.
이제 이것을 실제로 관찰하기 위해 다음과 같은 프로그램을 만들어 봅시다 :
- 실제 메모리보다 많은 RAM을 할당
mmap
- 각 페이지에 1 바이트를 기록하여 각 페이지가 가상 전용 메모리 (VSZ)에서 실제로 사용 된 메모리 (RSS)로 이동하도록합니다.
- 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;
/* /programming/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:
* /programming/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;
}
컴파일하고 실행하십시오.
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
어디:
- 0x1000000000 == 64GiB : 2x 내 컴퓨터의 물리적 RAM 32GiB
- 0x200000000 == 8GiB : 8GiB마다 메모리를 인쇄하므로 32GiB 부근에서 충돌하기 전에 4 번 인쇄해야합니다.
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
: 리눅스가 물리적 RAM보다 mmap 호출을 더 크게하기 위해 필요합니다 : malloc이 할당 할 수있는 최대 메모리
프로그램 출력 :
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
말한다 SIGKILL 리눅스에서 보낸, 메모리 부족 살인자 .
출력 해석 :
- VSZ 가상 메모리는 mmap 후에도 일정하게 유지됩니다
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
(ps
값은 KiB에 있음). - RSS “실제 메모리 사용량”은 페이지를 터치 할 때만 게으르게 증가합니다. 예를 들면 다음과 같습니다.
- 첫 번째 인쇄물에는가 있습니다
extra_memory_committed 0
. 이는 아직 페이지를 건드리지 않은 것을 의미합니다. RSS는1648 KiB
텍스트 영역, 전역 등과 같은 정상적인 프로그램 시작에 할당 된 작은 크기 입니다. - 두 번째 인쇄물에서 우리는
8388608 KiB == 8GiB
가치있는 페이지를 작성했습니다 . 결과적으로 RSS는 정확히 8GIB 증가하여8390256 KiB == 8388608 KiB + 1648 KiB
- RSS는 8GiB 단위로 계속 증가합니다. 마지막 인쇄는 약 24GiB의 메모리를 표시하며 32GiB를 인쇄하기 전에 OOM 킬러가 프로세스를 종료했습니다.
- 첫 번째 인쇄물에는가 있습니다
참조 : /unix/35129/need-explanation-on-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
우리는 흥미롭게도 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
이 로그는 /unix/153585/how-does-the-oom-killer-decide-which-score 865
에서 언급 한 프로세스 중 가장 높은 (최악의) 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에서 테스트되었습니다.
답변
RSS와 VSZ에 대해서는 이미 많은 이야기가 있다고 생각합니다. 관리자 / 프로그래머 / 사용자 관점에서 응용 프로그램을 디자인 / 코딩 할 때 RSZ (레지 덴셜 메모리)에 더 관심이 있으며 점점 더 많은 변수를 계속 가져 가면 (heap)이 값이 증가하는 것을 볼 수 있습니다. malloc 기반 공간 할당을 루프로 빌드하는 간단한 프로그램을 시도하고 해당 malloc의 공간에 데이터를 채우십시오. RSS는 계속 올라갑니다. VSZ에 관해서는 Linux 가하는 가상 메모리 매핑과 기존 운영 체제 개념에서 파생 된 핵심 기능 중 하나입니다. VSZ 관리는 커널의 가상 메모리 관리에 의해 수행됩니다. VSZ에 대한 자세한 내용은 커널의 기본 task_struct 데이터 구조의 일부인 mm_struct 및 vm_struct에 대한 Robert Love의 설명을 참조하십시오.