OOM Killer (Out-of-Memory Killer)
๊ฐ์ (Overview)
OOM Killer๋ Linux ์ปค๋์ด ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ชจ๋ ์์งํ๊ณ ํ์ ๊ฐ๋ฅํ ํ์ด์ง๋ ๋จ์ ์์ง ์์ ๋, ๊ฐ์ฅ ์ ํฉํ ํ๋ก์ธ์ค๋ฅผ ๊ณจ๋ผ SIGKILL๋ก ์ข
๋ฃํ์ฌ ์์คํ
์ ์ฒด๊ฐ ๋ฉ์ถ๋ ๊ฒ์ ๋ฐฉ์งํ๋ ์ตํ์ ์๋จ์
๋๋ค. __alloc_pages()์์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ์คํจํ๊ณ ๋ชจ๋ reclaim ๊ฒฝ๋ก๊ฐ ์์ง๋๋ฉด out_of_memory()๊ฐ ํธ์ถ๋๋ฉฐ, ์ด ํจ์๊ฐ OOM Killer์ ์ง์
์ ์
๋๋ค. OOM Killer๋ ๋จ์ํ "๊ฐ์ฅ ํฐ ํ๋ก์ธ์ค"๋ฅผ ์ฃฝ์ด๋ ๊ฒ์ด ์๋๋ผ, oom_badness() ํด๋ฆฌ์คํฑ์ ํตํด RSS + swap + page table ํฌ๊ธฐ์ oom_score_adj ๊ฐ์ค์น๋ฅผ ๊ฒฐํฉํ์ฌ ์ ์๋ฅผ ์ฐ์ถํ๊ณ , ๊ฐ์ฅ ๋์ ์ ์๋ฅผ ๊ฐ์ง ํ๋ก์ธ์ค๋ฅผ ํฌ์์(victim)๋ก ์ ํํฉ๋๋ค.
OOM Killer๋ ๊ธ๋ก๋ฒ OOM๊ณผ memcg(๋ฉ๋ชจ๋ฆฌ cgroup) OOM ๋ ๊ฐ์ง ๊ฒฝ๋ก๋ก ๋์ํฉ๋๋ค. ๊ธ๋ก๋ฒ OOM์ ์์คํ
์ ์ฒด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํ ๋ ๋ฐ๋ํ๊ณ , memcg OOM์ ํน์ ์ปจํ
์ด๋๋ cgroup์ ๋ฉ๋ชจ๋ฆฌ ํ๋(memory.max)๊ฐ ์ด๊ณผ๋ ๋ ํด๋น cgroup ๋ด๋ถ์์๋ง ๋์ํฉ๋๋ค. ๋ํ oom_reaper ์ปค๋ ์ค๋ ๋๊ฐ ํฌ์์์ ์ฃผ์ ๊ณต๊ฐ์ ์ฆ์ ํ์(reaping)ํ์ฌ ๋ฉ๋ชจ๋ฆฌ ํ๋ณด ์๊ฐ์ ๋จ์ถํ๋ ์ญํ ์ ํฉ๋๋ค.
์์ค ํ์ผ:
mm/oom_kill.c โ OOM Killer ํต์ฌ ๋ก์ง (1273์ค)
mm/page_alloc.c โ ํ ๋น slowpath์์ OOM ์ง์
์กฐ๊ฑด
mm/memcontrol.c โ memory.oom.group, memcg OOM ๊ทธ๋ฃน ์ฒ๋ฆฌ
include/linux/oom.h โ oom_control, oom_constraint, API ์ ์ธ
include/uapi/linux/oom.h โ OOM_SCORE_ADJ_MIN/MAX ์์
include/linux/mm_types.h โ struct mm_struct, struct page ์ ์
fs/proc/base.c โ /proc/<pid>/oom_score ํ์๊ฐ
์ผ์ ๋น์ ๋ก ๋ณด๋ฉด OOM Killer๋ ๊ฑด๋ฌผ์ ๋น์ ์ฐจ๋จ๊ธฐ์ ๊ฐ์ต๋๋ค. ๋จผ์ ๋ถํ์ํ ์ ๋ฑ์ ๋๊ณ (ํ์ด์ง ํ์), ์ฐฝ๊ณ ๋ก ์ฎ๊ธฐ๊ณ (swap/zswap), ๊ณต๊ฐ์ ๋ค์ ๋ฐฐ์นํ ๋ค(compaction)์๋ ์ ๋ ฅ์ด ๋ถ์กฑํ๋ฉด ๊ฑด๋ฌผ ์ ์ฒด ์ ์ ์ ๋ง๊ธฐ ์ํด ๊ฐ์ฅ ์ํฅ์ด ์๊ฑฐ๋ ์ ๋ ฅ์ ๋ง์ด ์ฐ๋ ๊ตฌ์ญ ํ๋๋ฅผ ๊ฐ์ ๋ก ์ฐจ๋จํฉ๋๋ค. ์ด๋ oom_score_adj๋ ๋ณ์ ์๊ธ์ค์ด๋ ๊ด์ ์ค์ฒ๋ผ ์ ๋ ๊บผ์ง๋ฉด ์ ๋๋ ๊ตฌ์ญ์ ๋ณดํธํ๊ฑฐ๋, ์์ ์์
์ฒ๋ผ ๋จผ์ ์ข
๋ฃํด๋ ๋๋ ๊ตฌ์ญ์ ์ง์ ํ๋ ์ฐ์ ์์ ๋
ธ๋ธ์
๋๋ค.
OOM Killer ํธ์ถ ํ๋ฆ
ํต์ฌ ์๋ฃ๊ตฌ์กฐ ๊ด๊ณ๋
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# 1. ์์คํ
์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ํํฉ
cat /proc/meminfo | grep -E 'MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree'
# 2. PSI ๋ฉ๋ชจ๋ฆฌ ์๋ ฅ ํ์ธ
cat /proc/pressure/memory
# 3. OOM Killer ๊ด๋ จ sysctl ์ค์ ํ์ธ
sysctl vm.panic_on_oom vm.oom_kill_allocating_task vm.oom_dump_tasks
# 4. ํ์ฌ ํ๋ก์ธ์ค์ oom_score์ oom_score_adj ํ์ธ
cat /proc/self/oom_score /proc/self/oom_adj /proc/self/oom_score_adj
# 5. ์์คํ
์ ์ฒด ํ๋ก์ธ์ค์ OOM ์ ์ ์ ๋ ฌ
for pid in /proc/[0-9]*/oom_score; do echo "$(cat $pid 2>/dev/null) $(dirname $pid | xargs basename) $(cat $(dirname $pid)/comm 2>/dev/null)"; done 2>/dev/null | sort -rn | head -10
# 6. ์ค์ ์๋น์ค ๋ณดํธ ์ค์ ๊ฐ ํ์ธ
pid=$(pidof sshd 2>/dev/null | awk '{print $1}'); [ -n "$pid" ] && cat /proc/$pid/oom_score_adj
# 7. ์ค์ ์๋น์ค OOM ๋ณดํธ ์ค์ ์์
pid=$(pidof sshd 2>/dev/null | awk '{print $1}'); [ -n "$pid" ] && echo -1000 | sudo tee /proc/$pid/oom_score_adj
# 8. dmesg์์ OOM Killer ๋ฐ๋ ์ด๋ ฅ ํ์ธ
dmesg | grep -E 'oom-kill|Out of memory|Killed process'
# 9. cgroup v2 OOM ์ด๋ฒคํธ ํ์ธ
grep -H 'oom' /sys/fs/cgroup/memory.events /sys/fs/cgroup/*/memory.events 2>/dev/null
# 10. cgroup v2 ๊ทธ๋ฃน kill ์ค์ ํ์ธ
find /sys/fs/cgroup -name memory.oom.group -exec sh -c 'for f; do printf "%s " "$f"; cat "$f"; done' sh {} + 2>/dev/null | head
# 11. ํ์/compaction/OOM ๊ด๋ จ vmstat ์นด์ดํฐ ํ์ธ
cat /proc/vmstat | grep -E 'allocstall|pgscan|pgsteal|compact|oom_kill'
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct oom_control
OOM Killer์ ์ ์ฒด ๋์ ์ํ๋ฅผ ๋ด๋ ์ ์ด ๊ตฌ์กฐ์ฒด์ ๋๋ค.
์์ค: include/linux/oom.h:24-54
/*
* OOM killer๋ฅผ ๋ฐ์์ํจ ํ์ด์ง ํ ๋น์ ์ธ๋ถ ์ ๋ณด์ด๋ฉฐ,
* ๋ฌด์์ killํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
*/
struct oom_control {
/* cpuset ํ๋จ์ ์ฌ์ฉ */
struct zonelist *zonelist;
/* mempolicy ํ๋จ์ ์ฌ์ฉ */
nodemask_t *nodemask;
/* OOM์ด ํธ์ถ๋ memory cgroup์ด๋ฉฐ, global OOM์ด๋ฉด NULL */
struct mem_cgroup *memcg;
/* cpuset๊ณผ node locality ์๊ตฌ์ฌํญ ํ๋จ์ ์ฌ์ฉ */
const gfp_t gfp_mask;
/*
* order == -1์ sysrq์ ์ํด OOM kill์ด ํ์ํ๋ค๋ ๋ป์ด๊ณ ,
* ๊ทธ ๋ฐ์ ๊ฐ์ ํ์ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉ๋๋ค.
*/
const int order;
/* OOM ๊ตฌํ์์ ์ฌ์ฉํ๋ฉฐ, ํธ์ถ์๊ฐ ์ค์ ํ์ง ์๋๋ค */
unsigned long totalpages;
struct task_struct *chosen;
long chosen_points;
/* constraint ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ ๋ฐ ์ฌ์ฉ */
enum oom_constraint constraint;
};
enum oom_constraint
ํ ๋น ์คํจ์ ์์ธ์ด ์ ์ฝ ์กฐ๊ฑด์ ์ํ ๊ฒ์ธ์ง ํ๋จํฉ๋๋ค.
์์ค: include/linux/oom.h:17-22
enum oom_constraint {
CONSTRAINT_NONE,
CONSTRAINT_CPUSET,
CONSTRAINT_MEMORY_POLICY,
CONSTRAINT_MEMCG,
};
CONSTRAINT_NONE์ ์ผ๋ฐ์ ์ธ ์์คํ
์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ, CONSTRAINT_CPUSET์ cpuset์ด ํ์ฉํ ๋
ธ๋ ๋ฒ์ ์ ํ, CONSTRAINT_MEMORY_POLICY๋ NUMA mempolicy ์ ์ฝ, CONSTRAINT_MEMCG๋ memory cgroup ํ๋ ์ด๊ณผ๋ฅผ ๋ํ๋
๋๋ค.
oom_badness() ์ ์ ๊ณ์ฐ ๊ณต์
OOM Killer๊ฐ ๊ฐ ํ๋ก์ธ์ค์ ๋ํด ์ฐ์ถํ๋ ์ ์์ ๋๋ค.
์์ค: mm/oom_kill.c:193-240
/**
* oom_badness - ์ด๋ค ํ๋ณด task๋ฅผ killํ ์ง ๊ฒฐ์ ํ๋ ํด๋ฆฌ์คํฑ ํจ์
* @p: ๊ณ์ฐํ task ๊ตฌ์กฐ์ฒด
* @totalpages: ํ์ด์ง ํ ๋น์ ํ์ฉ๋ ์ ์ฒด present RAM
*
* kill ๋์ task๋ฅผ ํ๋จํ๋ ํด๋ฆฌ์คํฑ์ ๊ฐ๋ฅํ ๋จ์ํ๊ณ ์์ธก ๊ฐ๋ฅํด์ผ ํ๋ค.
* ๋ชฉํ๋ ๊ฐ์ฅ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ๋ task์ ๊ฐ์ฅ ํฐ ๊ฐ์ ๋ฐํํ์ฌ
* ์ฐ์ OOM ์คํจ๋ฅผ ํผํ๋ ๊ฒ์ด๋ค.
*/
long oom_badness(struct task_struct *p, unsigned long totalpages)
{
long points;
long adj;
if (oom_unkillable_task(p))
return LONG_MIN;
p = find_lock_task_mm(p);
if (!p)
return LONG_MIN;
/*
* ๋ช
์์ ์ผ๋ก OOM kill ๋ถ๊ฐ ํ์๊ฐ ์๊ฑฐ๋ ์ด๋ฏธ OOM reaping๋์๊ฑฐ๋
* vfork ๋์ค์ธ task๋ ํ๋ณด๋ก ๊ณ ๋ คํ์ง ์๋๋ค.
*/
adj = (long)p->signal->oom_score_adj;
if (adj == OOM_SCORE_ADJ_MIN ||
mm_flags_test(MMF_OOM_SKIP, p->mm) ||
in_vfork(p)) {
task_unlock(p);
return LONG_MIN;
}
/*
* badness ์ ์์ ๊ธฐ์ค๊ฐ์ ๊ฐ task์ RSS, page table,
* swap ๊ณต๊ฐ ์ฌ์ฉ๋์ด RAM์์ ์ฐจ์งํ๋ ๋น์จ์ด๋ค.
*/
points = get_mm_rss_sum(p->mm) + get_mm_counter_sum(p->mm, MM_SWAPENTS) +
mm_pgtables_bytes(p->mm) / PAGE_SIZE;
task_unlock(p);
/* oom_score_adj ๋จ์๋ก ์ ๊ทํ */
adj *= totalpages / 1000;
points += adj;
return points;
}
์ ์ ๊ณ์ฐ ์์ฝ:
(RSS_anon + RSS_file + RSS_shmem) + SwapUsed + PageTablePagesoom_score_adj๊ฐ ์์(์: 1000)์ด๋ฉด ์ ์๊ฐ ํฌ๊ฒ ์ฆ๊ฐ โ ๋จผ์ ์ฃฝ์oom_score_adj๊ฐ ์์(์: -500)์ด๋ฉด ์ ์๊ฐ ๊ฐ์ โ ๋ณดํธ๋จOOM_SCORE_ADJ_MIN(-1000)์ ์ฌ์ค์ OOM์์ ์์ ๋ณดํธoom_badness()๋ ์ปค๋ ๋ด๋ถ์์ ํ๋ณด๋ฅผ ๋น๊ตํ๊ธฐ ์ํ raw ํ์ด์ง ์ ์๋ฅผ ๋ฐํํฉ๋๋ค. ์ฌ์ฉ์ ๊ณต๊ฐ์ /proc/๋ fs/proc/base.c:585-601์์ ์ด ๊ฐ์ totalram_pages() + total_swap_pages ๊ธฐ์ค์ผ๋ก ๋ค์ ์ค์ผ์ผ๋งํด ํ์ํ๋ฏ๋ก, ์ด์ ์ค์๋ oom_score์ oom_score_adj๋ฅผ ํจ๊ป ๋ด์ผ ์ค์ kill ์ฐ์ ์์๋ฅผ ํด์ํ ์ ์์ต๋๋ค.
ํต์ฌ ํจ์
1. `out_of_memory()` โ OOM Killer ์ง์ ์
// mm/oom_kill.c:1119-1188
bool out_of_memory(struct oom_control *oc)
์ญํ : ํ์ด์ง ํ ๋น์๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ๋ณดํ์ง ๋ชปํ์ ๋ ํธ์ถ๋๋ ๋ฉ์ธ ํจ์.
๋ถ๊ธฐ ๋ก์ง:
1. oom_killer_disabled โ true๋ฉด ์ฆ์ ๋ฐํ (cgroup freeze ๋ฑ)
2. ๊ธ๋ก๋ฒ OOM์ด๋ฉด notifier ์ฒด์ธ ํธ์ถ โ ๋ฉ๋ชจ๋ฆฌ ํ์ ๊ฐ๋ฅํ๋ฉด ๋ฐํ
3. task_will_free_mem(current) โ ํ์ฌ ํ๋ก์ธ์ค๊ฐ ์ด๋ฏธ ์ข
๋ฃ ์ค์ด๋ฉด ์์ ์ ํฌ์์๋ก ์ง์
4. __GFP_FS๊ฐ ์๋ ํ ๋น ์คํจ์ด๊ณ memcg OOM์ด ์๋๋ฉด true ๋ฐํ (NOFS/NOIO ๊ฒฝ๋ก๋ OOM kill๋ก ํด๊ฒฐํ๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ)
5. constrained_alloc()์ผ๋ก ์ ์ฝ ์กฐ๊ฑด ํ๋จ โ check_panic_on_oom() ํธ์ถ
6. oom_kill_allocating_task sysctl์ด ์ผ์ ธ ์์ผ๋ฉด ํ์ฌ ํ๋ก์ธ์ค๋ฅผ ์ง์ ํฌ์์๋ก ์ ํ
7. select_bad_process()๋ก ์ต์ ํฌ์์ ์ ํ
8. ํฌ์์๊ฐ ์์ผ๋ฉด panic("System is deadlocked on memory")
9. ํฌ์์๊ฐ ์์ผ๋ฉด oom_kill_process() ํธ์ถ
2. `oom_kill_process()` โ ํฌ์์ ํ๋ก์ธ์ค ์ฒ๋ฆฌ
// mm/oom_kill.c:1024-1070
static void oom_kill_process(struct oom_control *oc, const char *message)
์ญํ : ์ ํ๋ ํฌ์์๋ฅผ ์ค์ ๋ก ์ข ๋ฃ์ํค๋ ํจ์.
๋ถ๊ธฐ ๋ก์ง:
1. task_will_free_mem(victim) โ ์ด๋ฏธ ์ข
๋ฃ ์ค์ด๋ฉด mark_oom_victim()๋ง ํ๊ณ ์ข
๋ฃ
2. dump_header()์ dump_oom_victim()์ผ๋ก ์์คํ
์ํ ์ถ๋ ฅ
3. mem_cgroup_get_oom_group()๋ก memory.oom.group=1์ธ ์์ cgroup ํ์ธ โ ๊ทธ๋ฃน ์ ์ฒด๋ฅผ ์ฃฝ์ฌ์ผ ํ๋์ง ํ๋จ
4. __oom_kill_process()์ผ๋ก ์ค์ ๋ก SIGKILL ์ ์ก
5. OOM ๊ทธ๋ฃน์ด ์์ผ๋ฉด mem_cgroup_scan_tasks()๋ก ๊ทธ๋ฃน ๋ด ๋ชจ๋ ํ๋ก์ธ์ค์ SIGKILL
3. `__oom_kill_process()` โ SIGKILL ์ ์ก๊ณผ reaper ํ์
// mm/oom_kill.c:928-1008
static void __oom_kill_process(struct task_struct *victim, const char *message)
์ญํ : ํฌ์์์ ๊ณต์ mm๋ฅผ ๊ฐ์ง ๋ชจ๋ ํ๋ก์ธ์ค์ SIGKILL์ ๋ณด๋ด๊ณ oom_reaper๋ฅผ ํ์ํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. find_lock_task_mm()๋ก ์ ํจํ mm๋ฅผ ๊ฐ์ง ์ค๋ ๋ ์ฐพ์
2. do_send_sig_info(SIGKILL)๋ก ํฌ์์์ ์๊ทธ๋ ์ ์ก
3. mark_oom_victim()์ผ๋ก TIF_MEMDIE ํ๋๊ทธ ์ค์ โ ๋ฉ๋ชจ๋ฆฌ ์์ฝ ์ ๊ทผ ๊ถํ ๋ถ์ฌ
4. for_each_process()๋ก ๋์ผ mm๋ฅผ ๊ณต์ ํ๋ ๋ค๋ฅธ ํ๋ก์ธ์ค์๋ SIGKILL
5. is_global_init(p)์ด๋ฉด oom_reaper๊ฐ reapingํ์ง ์๋๋ก ํ๋๊ทธ ์ค์
6. queue_oom_reaper()๋ก 2์ด ํ์ด๋จธ ํ reaping ์์
4. `select_bad_process()` โ ์ต์ ํฌ์์ ์ ํ
// mm/oom_kill.c:365-380
static void select_bad_process(struct oom_control *oc)
์ญํ : ๋ชจ๋ ํ๋ณด ํ๋ก์ธ์ค๋ฅผ ํ๊ฐํ์ฌ oom_badness ์ ์๊ฐ ๊ฐ์ฅ ๋์ ํ๋ก์ธ์ค๋ฅผ ์ ํํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. memcg OOM์ด๋ฉด mem_cgroup_scan_tasks()๋ก ํด๋น cgroup ๋ด๋ถ๋ง ์ค์บ
2. ๊ธ๋ก๋ฒ OOM์ด๋ฉด for_each_process()๋ก ์ ์ฒด ํ๋ก์ธ์ค ์ค์บ
3. ๊ฐ ํ๋ก์ธ์ค์ ๋ํด oom_evaluate_task() ํธ์ถ โ oom_badness ์ ์ ๋น๊ต
4. oom_task_origin(task)๊ฐ true๋ฉด ๋ฐ๋ก ์ ํ (ํ์ฌ ํ ๋น ์ค์ธ ํ๋ก์ธ์ค๊ฐ OOM์ ์ ๋ฐ)
5. ์ด์ ํฌ์์(tsk_is_oom_victim)๋ ๊ฑด๋๋ โ ์ค๋ณต_kill ๋ฐฉ์ง
5. `oom_reap_task()` โ ๋ฉ๋ชจ๋ฆฌ ์ฆ์ ํ์
// mm/oom_kill.c:619-648
static void oom_reap_task(struct task_struct *tsk)
์ญํ : ํฌ์์์ ์ฃผ์ ๊ณต๊ฐ ์ค ํ์ ๊ฐ๋ฅํ ํ์ด์ง๋ฅผ ์ฆ์ unmapํ์ฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ๋ณดํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. mmap_read_trylock(mm) ์คํจ โ ์ฌ์๋ (์ต๋ 10ํ, 100ms ๊ฐ๊ฒฉ)
2. MMF_OOM_SKIP ํ๋๊ทธ๊ฐ ์ค์ ๋์ด ์์ผ๋ฉด ๊ฑด๋๋
3. __oom_reap_task_mm()๋ก VMA ์ํ โ ์ต๋ช
ํ์ด์ง์ ๋น-๊ณต์ ํ์ผ ํ์ด์ง๋ฅผ unmap
4. VM_HUGETLB ๋๋ VM_PFNMAP VMA๋ ๊ฑด๋๋
5. ์ต์ข
์ ์ผ๋ก MMF_OOM_SKIP ์ค์ โ ํฅํ OOM์์ ์ด mm ๋ฌด์
ํธ์ถ ํ๋ฆ
ํ ๋น slowpath๋ OOM Killer๋ฅผ ๋ฐ๋ก ํธ์ถํ์ง ์๊ณ direct reclaim๊ณผ compaction์ ๋จผ์ ์๋ํฉ๋๋ค. ๋ ๊ฒฝ๋ก๊ฐ ๋ชจ๋ ์คํจํ๊ณ , ์ฌ์๋ ์กฐ๊ฑด๋ ๋ ์ด์ ๋จ์ง ์์์ ๋ __alloc_pages_may_oom()์ผ๋ก ๋์ด๊ฐ๋ฉฐ, ์ฌ๊ธฐ์ oom_lock์ ์ก๊ณ ๋ง์ง๋ง watermark ํ์ธ ํ out_of_memory()๋ฅผ ํธ์ถํฉ๋๋ค.
์์ค: mm/page_alloc.c:4844-4855
/* ์ง์ ํ์๋ฅผ ์๋ํ ๋ค ํ ๋นํ๋ค */
if (!compact_first) {
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags,
ac, &did_some_progress);
if (page)
goto got_pg;
}
/* ์ง์ compaction์ ์๋ํ ๋ค ํ ๋นํ๋ค */
page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
compact_priority, &compact_result);
if (page)
goto got_pg;
์์ค: mm/page_alloc.c:4928-4937
/*
* ๋ถํ์ํ OOM kill์ ํผํ๋ ค๊ณ cpuset/zonelist ๊ฐฑ์ ๊ฒฝ์์ ์ฒ๋ฆฌํ๋ค.
*/
if (check_retry_cpuset(cpuset_mems_cookie, ac) ||
check_retry_zonelist(zonelist_iter_cookie))
goto restart;
/* Reclaim์ด ์คํจํ์ผ๋ฏ๋ก kill์ ์์ํ๋ค */
page = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);
__alloc_pages() (ํ ๋น ์คํจ)
โโ __alloc_pages_slowpath()
โโ __alloc_pages_direct_reclaim()
โโ __alloc_pages_direct_compact()
โโ __alloc_pages_may_oom()
โโ out_of_memory(oc)
โโ blocking_notifier_call_chain() โ notifier๋ก ํ์ ์๋
โโ task_will_free_mem(current) โ ์ด๋ฏธ ์ข
๋ฃ ์ค์ด๋ฉด ์์ ์ ํฌ์์๋ก
โโ constrained_alloc(oc) โ ์ ์ฝ ์กฐ๊ฑด ํ๋จ (MEMCG/CPUSET/POLICY)
โโ check_panic_on_oom(oc) โ panic_on_oom sysctl ํ์ธ
โโ select_bad_process(oc)
โ โโ oom_evaluate_task(task, oc)
โ โโ oom_badness(task, totalpages) โ ์ ์ ๊ณ์ฐ
โโ oom_kill_process(oc, message)
โ โโ task_will_free_mem(victim) โ ์ด๋ฏธ ์ข
๋ฃ ์ค์ด๋ฉด reaping๋ง
โ โโ dump_header(oc) โ ์์คํ
์ํ ์ถ๋ ฅ
โ โโ dump_oom_victim(oc, victim) โ ํฌ์์ ์ ๋ณด ์ถ๋ ฅ
โ โโ __oom_kill_process(victim, message)
โ โ โโ do_send_sig_info(SIGKILL, victim) โ ์๊ทธ๋ ์ ์ก
โ โ โโ mark_oom_victim(victim) โ TIF_MEMDIE ์ค์
โ โ โโ for_each_process โ SIGKILL โ ๊ณต์ mm ํ๋ก์ธ์ค
โ โ โโ queue_oom_reaper(victim) โ reaper ํ์ด๋จธ ํ์
โ โโ mem_cgroup_scan_tasks โ oom_kill_memcg_member (๊ทธ๋ฃน kill)
โโ return true/false
์กฐ๊ฑด๋ณ ๋น๊ต
| ์กฐ๊ฑด | ๋์ ๋ฐฉ์ | ํ์ธ ๋ฐฉ๋ฒ | |
|---|---|---|---|
| **๊ธ๋ก๋ฒ OOM** | ์์คํ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ. ๋ชจ๋ ํ๋ก์ธ์ค ํ๋ณด. | `dmesg \ | grep "Out of memory"` |
| **memcg OOM** | ํน์ ์ปจํ ์ด๋/cgroup ํ๋ ์ด๊ณผ. ํด๋น cgroup ๋ด๋ถ๋ง ์ค์บ. | `cat /sys/fs/cgroup/memory.events \ | grep oom` |
| **memory.oom.group = 1** | cgroup v2์์ ํฌ์์ ํ๋๊ฐ ์๋๋ผ ํด๋น cgroup์ kill ๊ฐ๋ฅํ task ์ ์ฒด๋ฅผ ์ข ๋ฃ. | `cat /sys/fs/cgroup/ | |
| **SysRq OOM** | `echo f > /proc/sysrq-trigger`๋ก ์๋ ๋ฐ๋. `order == -1`. | `dmesg \ | grep "SysRq"` |
| **Constrained: CPUSET** | cpuset์ด ํ์ฉํ๋ ๋ฉ๋ชจ๋ฆฌ ๋ ธ๋๋ง ์ฌ์ฉ ๊ฐ๋ฅ. ํด๋น ๋ ธ๋ ๋ด ํ๋ก์ธ์ค๋ง ํ๋ณด. | `cat /proc/self/cpuset` | |
| **Constrained: MEMPOLICY** | NUMA mempolicy๊ฐ ํน์ ๋ ธ๋๋ก ์ ํ. ํด๋น ๋ ธ๋ ํ๋ก์ธ์ค๋ง ํ๋ณด. | `numactl --show` | |
| **panic_on_oom = 0** | ๊ธฐ๋ณธ๊ฐ. OOM Killer๊ฐ ํฌ์์๋ฅผ ์ฐพ์ kill. | `sysctl vm.panic_on_oom` | |
| **panic_on_oom = 1** | CONSTRAINT_NONE์ผ ๋๋ง panic. cpuset/mempolicy/memcg ์คํจ๋ kill. | `sysctl vm.panic_on_oom` | |
| **panic_on_oom = 2** | ๋ชจ๋ ์กฐ๊ฑด์์ panic. | `sysctl vm.panic_on_oom` | |
| **oom_kill_allocating_task = 1** | OOM์ ์ ๋ฐํ ํ ๋น ํ๋ก์ธ์ค ์์ ์ ์ง์ kill. | `sysctl vm.oom_kill_allocating_task` | |
| **OOM_SCORE_ADJ_MIN(-1000)** | oom_badness()๊ฐ LONG_MIN ๋ฐํ โ ์ฌ์ค์ ๋ณดํธ. | `cat /proc/ | |
| **/proc/ | ์ฌ์ฉ์ ๊ณต๊ฐ์ ๋ณด์ด๋ ์ ๊ทํ๋ OOM ์ฐ์ ์์. ๋ด๋ถ raw ์ ์์ ํจ๊ป `oom_score_adj` ์ํฅ์ด ๋ฐ์๋จ. | `cat /proc/ | |
| **PSI memory pressure** | OOM ์ง์ ์ stall์ ์กฐ๊ธฐ ๊ฐ์ง. `some/full`์ด ๋์ ๋๋ฉด reclaim ์ง์ฐ์ด ์ปค์ง ์ํ. | `cat /proc/pressure/memory` | |
| **oom_reaper (MMU ์์)** | ํฌ์์ SIGKILL ํ 2์ด ๋ค ์ฃผ์ ๊ณต๊ฐ์ ์ฆ์ reaping. | `dmesg \ | grep "oom_reaper"` |
| **OOM victim ์ฌ์ง์ ๋ฐฉ์ง** | `MMF_OOM_SKIP` ํ๋๊ทธ๊ฐ ์ค์ ๋ mm๋ ๋ค์ OOM์์ ๋ฌด์. | ์ปค๋ ๋ด๋ถ ํ๋๊ทธ |
์ฆ์๋ณ ์ง๋จ ๊ธฐ์ค
| ์ฆ์ | ๋จผ์ ๋ณผ ์งํ | ์๋ฏธ | ๋ค์ ํ์ธ |
|---|---|---|---|
| ๊ฐํ์ OOM | `dmesg`, `/proc/meminfo`, `/proc/vmstat`์ `oom_kill` | ํ์๋ swap์ผ๋ก ๋ฒํฐ๋ค๊ฐ ํน์ ์๊ฐ์ ํ๋ณด๊ฐ kill๋จ. | `oom_score_adj`, RSS ์์ ํ๋ก์ธ์ค, `memory.events` |
| ์ปจํ ์ด๋๋ง ์ข ๋ฃ | cgroup v2 `memory.events`, `memory.max` | ์์คํ ์ ์ฒด๊ฐ ์๋๋ผ ํด๋น cgroup์ hard limit ์ด๊ณผ. | `memory.oom.group`, `memory.current`, `memory.high` |
| ํ์ ์ง์ฐ ํ OOM | `/proc/pressure/memory`, `allocstall`, `pgscan`, `pgsteal` | direct reclaim์ด ๊ธธ์ด์ง๊ณ ํ ๋น์๊ฐ stall์ ๊ฒช์. | ํ์ด์ง ํ์ ์ ์ฑ , swap/zswap ์ํ, slab ์ฆ๊ฐ |
| ๊ณ ์ฐจ ํ์ด์ง ํ ๋น ์คํจ | `/proc/buddyinfo`, `compact_*` vmstat | free ์ด๋์ ์์ด๋ ์ฐ์ ํ์ด์ง๊ฐ ๋ถ์กฑํ ์ ์์. | THP defrag, compaction, CMA/Movable ์์ญ |
| swap I/O ๊ธ์ฆ ๋ค OOM | `vmstat 1`, `SwapFree`, `pgmajfault` | working set์ด RAM์ ๋๊ณ swap๋ ๋ถ์กฑํด์ง๋ ํ๋ฆ. | swappiness, zswap/zram, ์ต๋ช ํ์ด์ง ์ฆ๊ฐ |
| ํน์ ์๋น์ค๊ฐ ๋ฐ๋ณต ํฌ์ | `/proc/ | ํฐ RSS ๋๋ ๋์ adj๋ก ์ฐ์ ์์๊ฐ ๋์์ง. | adj ์ ์ฑ , cgroup ๋ถ๋ฆฌ, ์๋น์ค๋ณ memory.max |
๊ด๋ จ ๋ฌธ์
์ฐธ๊ณ ์๋ฃ
mm/oom_kill.c โ Linux 7.0 ์์คmm/page_alloc.c โ OOM ์ง์
์ ํ ๋น slowpathmm/memcontrol.c โ cgroup v2 OOM ๊ทธ๋ฃน ์ฒ๋ฆฌinclude/linux/oom.h โ oom_control ์ ์fs/proc/base.c โ /proc//oom_score ํ์๊ฐ