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 ํ˜ธ์ถœ ํ๋ฆ„

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 + PageTablePages
  • oom_score_adj๊ฐ€ ์–‘์ˆ˜(์˜ˆ: 1000)์ด๋ฉด ์ ์ˆ˜๊ฐ€ ํฌ๊ฒŒ ์ฆ๊ฐ€ โ†’ ๋จผ์ € ์ฃฝ์Œ
  • oom_score_adj๊ฐ€ ์Œ์ˆ˜(์˜ˆ: -500)์ด๋ฉด ์ ์ˆ˜๊ฐ€ ๊ฐ์†Œ โ†’ ๋ณดํ˜ธ๋จ
  • OOM_SCORE_ADJ_MIN(-1000)์€ ์‚ฌ์‹ค์ƒ OOM์—์„œ ์™„์ „ ๋ณดํ˜ธ
  • oom_badness()๋Š” ์ปค๋„ ๋‚ด๋ถ€์—์„œ ํ›„๋ณด๋ฅผ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•œ raw ํŽ˜์ด์ง€ ์ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์˜ /proc//oom_score๋Š” 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//memory.oom.group`
    **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//oom_score_adj`
    **/proc//oom_score**์‚ฌ์šฉ์ž ๊ณต๊ฐ„์— ๋ณด์ด๋Š” ์ •๊ทœํ™”๋œ OOM ์šฐ์„ ์ˆœ์œ„. ๋‚ด๋ถ€ raw ์ ์ˆ˜์™€ ํ•จ๊ป˜ `oom_score_adj` ์˜ํ–ฅ์ด ๋ฐ˜์˜๋จ.`cat /proc//oom_score`
    **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_*` vmstatfree ์ด๋Ÿ‰์€ ์žˆ์–ด๋„ ์—ฐ์† ํŽ˜์ด์ง€๊ฐ€ ๋ถ€์กฑํ•  ์ˆ˜ ์žˆ์Œ.THP defrag, compaction, CMA/Movable ์˜์—ญ
    swap I/O ๊ธ‰์ฆ ๋’ค OOM`vmstat 1`, `SwapFree`, `pgmajfault`working set์ด RAM์„ ๋„˜๊ณ  swap๋„ ๋ถ€์กฑํ•ด์ง€๋Š” ํ๋ฆ„.swappiness, zswap/zram, ์ต๋ช… ํŽ˜์ด์ง€ ์ฆ๊ฐ€
    ํŠน์ • ์„œ๋น„์Šค๊ฐ€ ๋ฐ˜๋ณต ํฌ์ƒ`/proc//oom_score`, `/proc//oom_score_adj`ํฐ RSS ๋˜๋Š” ๋†’์€ adj๋กœ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„์ง.adj ์ •์ฑ…, cgroup ๋ถ„๋ฆฌ, ์„œ๋น„์Šค๋ณ„ memory.max

    ๊ด€๋ จ ๋ฌธ์„œ

  • 00-overview.html โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • 01-page_alloc.html โ€” Buddy Allocator (OOM ํ˜ธ์ถœ์›)
  • 05-page_reclaim.html โ€” ํŽ˜์ด์ง€ ํšŒ์ˆ˜ (OOM ์„ ํ–‰ ๋‹จ๊ณ„)
  • 07-swap_zswap.html โ€” Swap / zswap (OOM ํšŒ์ˆ˜ ๊ฒฝ๋กœ)
  • 11-compaction.html โ€” Compaction (OOM ์ „ ๊ณ ์ฐจ ํ• ๋‹น ํšŒ๋ณต ์‹œ๋„)
  • 03-vma_mmap.html โ€” VMA / mmap (OOM reaper ๋Œ€์ƒ)
  • 14-memcontrol.html โ€” Memory Cgroup (memcg OOM๊ณผ memory.oom.group)

  • ์ฐธ๊ณ ์ž๋ฃŒ

  • mm/oom_kill.c โ€” Linux 7.0 ์†Œ์Šค
  • mm/page_alloc.c โ€” OOM ์ง„์ž… ์ „ ํ• ๋‹น slowpath
  • mm/memcontrol.c โ€” cgroup v2 OOM ๊ทธ๋ฃน ์ฒ˜๋ฆฌ
  • include/linux/oom.h โ€” oom_control ์ •์˜
  • fs/proc/base.c โ€” /proc//oom_score ํ‘œ์‹œ๊ฐ’
  • minzkn.com ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” ํŽ˜์ด์ง€ โ€” https://www.minzkn.com/linuxkernel/pages/memory.html