Study · 운영체제 · OOM · 메모리
OOM Killer 가 보는 것은 달라고 한 양이 아니라 실제로 붙여 둔 양이다
장면
메모리가 정말 없을 때 커널은 무엇을 기준으로 죽일 프로세스를 고를까?
$ dmesg | grep -i "killed process"
[12345.678] Out of memory: Killed process 4242 (java) total-vm:...
$ cat /proc/<pid>/oom_score
# oom_score 가 높은 프로세스부터 죽는다. <pid> 자리에는 볼 프로그램 번호를 넣는다
아래 재생기의 ▶를 누르면 한 스텝씩 진행된다. 금색 테두리가 지금 이야기하는 곳이고, 캡션이 무슨 일이 일어났는지 말해 준다. 굵게 밑줄 친 말은 누르면 뜻이 나온다. 빨간 딱지는 그때 벌어지는 일의 이름이고, 캡션 바로 위 회색 글자는 지금 스텝이 속한 묶음의 이름이다.
카드 아래 회색 줄은 그 일이 실제로 적혀 있는 소스의 파일 이름 : 줄 번호와 함수 이름이다. 글 안에 (:669)처럼 줄 번호만 적힌 것은 바로 앞에 적은 파일의 그 줄이라는 뜻이다. 캡션 한가운데 박힌 긴 영어 이름과 괄호 속 파일 · 줄 번호도 같다 — 건너뛰고 한글만 읽어도 뜻이 통하게 써 두었다.
장면 · 붙일 것이 없을 때 하나를 고른다
0 / 11
▶ 를 누르면 물리 메모리가 정말 없을 때 커널이 무엇을 보고 죽일 프로세스를 고르는지 본다. 격자 32칸이 이 기계의 물리 메모리 전체이고, 누구에게 몇 칸씩 주었는지는 그림을 위한 배치다.
키보드 · ← → 한 스텝 · Space 재생/멈춤 · Home 처음
OOM Killer 는 가장 많이 달라고 한 것을 죽이지 않는다. 실제로 붙여 둔 양으로 점수를 매겨 고르고, 죽인 뒤 그 점수의 재료를 기록에 남긴다.
어떻게 확인했나
소스 어디를 보고 적었는지와 무엇을 돌려서 쟀는지를 남긴 자리다. 처음 읽을 때는 건너뛰어도 된다.
- oom_badness(mm/oom_kill.c:199)가 더하는 것은 셋뿐이다 — 지금 붙어 있는 페이지 수(RSS) + 스왑으로 나간 페이지 수 + 페이지 표가 먹은 바이트를 페이지 크기로 나눈 값(:228-229). mm->total_vm(VmSize)은 이 함수에 나오지 않는다. 그래서 「가장 많이 쓴 것이 죽는다」는 「가장 많이 붙여 둔 것이 죽는다」로 읽어야 맞다 — 달라고만 해 둔 양은 점수가 아니다.
- oom_score_adj 는 adj *= totalpages / 1000; points += adj;(mm/oom_kill.c:233-234)로 얹힌다. 1000 이면 전체 페이지 수만큼을 점수에 더하는 것이고, 최솟값 -1000(OOM_SCORE_ADJ_MIN, include/uapi/linux/oom.h:9)이면 점수를 재지 않고 LONG_MIN 을 돌려줘 후보에서 빠진다(:217-221).
- 죽이기는 마지막 수단이다 — __alloc_pages_direct_reclaim(mm/page_alloc.c:4939)이 실패하고 재시도까지 다 떨어진 뒤에야 「Reclaim has failed us, start killing things」 주석 아래(:5029)에서 __alloc_pages_may_oom(:4044) → out_of_memory(mm/oom_kill.c:1103)로 간다.
- 고르는 방식은 전수 조사다 — select_bad_process(mm/oom_kill.c:362)가 for_each_process 로 전부 돌며 oom_evaluate_task(:306)를 부르고, 지금까지의 최고점보다 낮으면 버린다(:339). 죽일 때는 SIGKILL(:943)을 보내고 dmesg 한 줄(:945)에 total-vm · anon-rss · pgtables · oom_score_adj 를 함께 찍는다.
- /proc/<pid>/oom_score 는 같은 oom_badness 를 그 자리에서 돌려(fs/proc/base.c:590) 0~2000 범위로 늘인 값이다(:596). 커널이 고를 때 쓰는 함수와 내가 읽는 숫자가 같은 출처다.
- 메모리 2GiB 로 한도를 건 컨테이너에서 계속 먹였더니 1,984MB 를 지나 죽었고 끝난 코드가 137(= 128 + SIGKILL 9)이었다. cgroup 의 memory.events 에 oom 1 · oom_kill 1 이 찍혔고, dmesg 가 남긴 표가 oom_badness 의 재료를 그대로 보여 준다 — total_vm 524991 · rss 523040 · pgtables_bytes 4243456 · oom_score_adj 0. 점수에 들어가는 것은 rss 와 pgtables 이고 total_vm 은 아니다.
항목 지도목차 ›
- 계정과 권한
- 1 로그인 — 로그인하면 무슨 일이 일어나나
- 2 계정 만들기 — 계정을 만들면 어디에 무엇이 쓰이나
- 3 권한 주기 — chmod 가 바꾸는 것은 어디에 있나
- 4 권한 검사 — 파일을 열 때 권한을 언제 보나
- 파일과 디렉터리
- 5 파일 만들기 — 파일을 만들면 무엇이 생기나
- 6 디렉터리 — 디렉터리를 만들면 무엇이 생기나
- 7 파일 읽기 — 파일을 읽으면 어디를 거치나
- 8 파일 지우기 — 파일을 지우면 정말 사라지나
- 프로세스
- 9 명령 실행 — 명령 하나를 치면 무슨 일이 벌어지나
- 10 파일 디스크립터 — 프로세스가 파일을 열면 무엇이 늘어나나
- 11 CPU 나누기 — CPU 하나를 여럿이 어떻게 나눠 쓰나
- 메모리
- 12 메모리 요청 — 메모리를 달라고 하면 바로 주나
- 13 페이지 폴트 — 처음 건드릴 때 무슨 일이 일어나나
- 14 OOM — 메모리가 모자라면 누가 죽나
- 네트워크
- 15 소켓 만들기 — 소켓을 만들면 무엇을 받나
- 16 연결 받기 — 연결이 들어오면 누가 먼저 받나
- 17 주고받기 — 데이터가 오갈 때 무엇이 움직이나