土法炼钢兴趣小组的算法知识备份

【操作系统百科】OOM Killer

文章导航

分类入口
os
标签入口
#oom-killer#oom-score#systemd-oomd#earlyoom#memcg-oom

目录

当物理内存 + swap 都不够,回收也搞不定时,内核最后一招:选一个进程 SIGKILL。这就是 OOM Killer。它的目标是”花最小代价释放最多内存”。

一、先看图

flowchart TD
    ALLOC[页分配失败<br/>回收/compact 都试过] --> OOM[out_of_memory]
    OOM --> CG{memcg 限制<br/>还是全局?}
    CG -->|memcg| CGOOM[memcg OOM<br/>只杀 cgroup 内进程]
    CG -->|global| GOOM[全局 OOM]
    GOOM --> SELECT[select_bad_process<br/>遍历 task_struct]
    CGOOM --> SELECT
    SELECT --> SCORE[计算 oom_score<br/>RSS + adj]
    SCORE --> KILL[SIGKILL 最高分者]
    KILL --> FREE[释放其内存]
    classDef crit fill:#f8514922,stroke:#f85149,color:#adbac7;
    classDef warn fill:#f0883e22,stroke:#f0883e,color:#adbac7;
    classDef ok fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    class ALLOC,OOM crit
    class SELECT,SCORE warn
    class FREE ok

二、oom_score 算法

内核给每个进程打分 0-1000:

cat /proc/$$/oom_score       # 当前分数
cat /proc/$$/oom_score_adj   # 调整值

分最高的先被杀。

oom_score_adj

echo -1000 > /proc/$$/oom_score_adj    # 保护关键进程
echo 500 > /proc/$$/oom_score_adj      # 牺牲品

systemd 用 OOMScoreAdjust= 配置。

三、全局 OOM 流程

__alloc_pages_slowpath
  → __alloc_pages_may_oom
    → out_of_memory
      → select_bad_process      # 遍历 for_each_process
        → oom_badness(task)     # 打分
      → oom_kill_process
        → do_send_sig_info(SIGKILL)
        → 标记 TIF_MEMDIE       # 给其分配 "OOM victim" 特权

TIF_MEMDIE 让被杀进程的分配暂时不受 watermark 限制——给它机会在 SIGKILL 处理中释放资源。

四、memcg OOM

cgroup v2 memory.max 超限触发 memcg 级 OOM:

echo 1 > /sys/fs/cgroup/myapp/memory.oom.group

Kubernetes pod 的 OOM 通常是 memcg OOM。

五、unkillable 进程

如果所有进程都 -1000,OOM Killer panic(没得杀了 → 系统挂)。

六、用户态 OOM:systemd-oomd

内核 OOM Killer 的问题:

systemd-oomd(247+)基于 PSI 提前介入:

# /etc/systemd/oomd.conf
[OOM]
SwapUsedLimit=90%
DefaultMemoryPressureLimit=60%
DefaultMemoryPressureDurationUSec=30s

原理: 1. 轮询 cgroup 的 memory.pressure 2. 某 cgroup PSI some > 阈值 + 持续时间 3. 杀掉该 cgroup(发 SIGKILL + memory.oom.group

好处:在内存完全耗尽之前就介入。

earlyoom

更简单的用户态 OOM daemon:

earlyoom -m 5 -s 5    # 可用内存或 swap < 5% 时杀最大进程

比 systemd-oomd 轻量,适合嵌入式/桌面。

七、追查 OOM 事故

7.1 dmesg

[  123.456] Out of memory: Killed process 1234 (myapp) total-vm:8192000kB ...

OOM 日志包含:

7.2 journalctl

journalctl -k --grep="Out of memory"
journalctl --unit=systemd-oomd

7.3 cgroup

cat /sys/fs/cgroup/myapp/memory.events
# oom         3        ← 触发了 3 次 OOM
# oom_kill    3        ← 杀了 3 次
# oom_group_kill  1

7.4 kdump

严重 OOM → panic → kdump 捕获 vmcore。crash 工具可还原当时每个进程的 mm_struct。

八、防御策略

8.1 配额

# 给关键服务设 memcg 限制
systemctl set-property myapp.service MemoryMax=4G MemorySwapMax=2G

8.2 保护

echo -1000 > /proc/$(pidof sshd)/oom_score_adj   # 保护 sshd

8.3 牺牲

echo 1000 > /proc/$(pidof benchmark)/oom_score_adj

8.4 overcommit

sysctl vm.overcommit_memory=2    # 严格:malloc 会失败但不会 OOM

8.5 预警

# PSI 触发器
echo "some 500000 2000000" > /proc/pressure/memory
# 500ms 窗口内 some stall > 2s 时触发 epoll 通知

九、常见误区

A:“OOM = 内存泄漏” ❌ 不一定;正常负载超出配额也会。先看 memory.eventsmeminfo

B:“关 swap 就不 OOM” ❌ 反而更容易 OOM——没有 swap 缓冲。

C:“-1000 所有关键进程” ❌ 不留牺牲品 = panic。

D:“OOM 后只杀一个” 不一定;可能连续触发杀多个。

十、小结


参考文献

工具


上一篇交换 下一篇NUMA 内存

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。

2026-04-28 · os

【操作系统百科】交换

swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。

2026-05-07 · os

【操作系统百科】用户态分配器

glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。


By .