Linux系统OOM killer机制详解

本文介绍了Linux下的OOMKiller(OutOfMemorykiller)机制,该机制在系统内存耗尽时启动,通过选择性地终止某些进程来释放内存。文章还讨论了OOMKiller的工作原理及其触发条件,并解释了Linux如何通过评估进程的oom_score来决定杀死哪个进程。

介绍:

Linux下面有个特性叫OOM killer(Out Of Memory killer),会在系统内存耗尽的情况下出现,选择性的干掉一些进程以求释放一些内存。广大从事Linux方面的IT农民工兄弟或多或少都有遇到过吧。

现象:

主机SSH突然无法连接,主机重启要过很久,重启后主机正常,登录查看日志信息,会有大量的 " Out of Memory: Killed process xxx 、Out of Memory: Killed process xxx "

案例:

kernel: lowmem_reserve[]: 0 0 0 0

kernel: Normal free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no

kernel: lowmem_reserve[]: 0 0 0 0

kernel: HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no

kernel: lowmem_reserve[]: 0 0 0 0

kernel: DMA: 0*4kB 1*8kB 1*16kB 1*32kB 1*64kB 1*128kB 1*256kB 1*512kB 1*1024kB 0*2048kB 0*4096kB = 2040kB

kernel: DMA32: 22*4kB 4*8kB 1*16kB 1*32kB 0*64kB 0*128kB 1*256kB 1*512kB 1*1024kB 1*2048kB 0*4096kB = 4008kB

kernel: Normal: empty

kernel: HighMem: empty

kernel: 8917 pagecache pages

kernel: Swap cache: add 96509, delete 88464, find 14295/17401, race 0+4

kernel: Free swap = 0kB

kernel: Total swap = 262136kB

kernel: Free swap: 0kB

kernel: 262144 pages of RAM

kernel: 8801 reserved pages

kernel: 45869 pages shared

kernel: 8045 pages swap cached

kernel: Out of memory: Killed process 330, UID 500, (mysqld).

kernel: httpd invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0

kernel:

kernel: Call Trace:

kernel: [<ffffffff802c2be6>] out_of_memory+0x8b/0x203

kernel:[<ffffffff8020fab8>] __alloc_pages+0x27f/0x308

kernel:[<ffffffff80213a38>] __do_page_cache_readahead+0xc8/0x1af

kernel:[<ffffffff802142a9>] filemap_nopage+0x14c/0x360

kernel:[<ffffffff80208e9d>] __handle_mm_fault+0x444/0x144f

kernel:[<ffffffff80234691>] lock_sock+0xa7/0xb2

kernel:[<ffffffff80232068>] release_sock+0x13/0xbe

kernel:[<ffffffff8022c1bd>] local_bh_enable+0x9/0x9c

kernel:[<ffffffff80234691>] lock_sock+0xa7/0xb2

kernel:[<ffffffff80267d94>] do_page_fault+0xf72/0x131b

kernel: [<ffffffff8041f85e>] move_addr_to_user+0x5d/0x78

kernel: [<ffffffff8041ff45>] sys_accept+0x18c/0x1d2

kernel: [<ffffffff8026082b>] error_exit+0x0/0x6e

kernel:

kernel: Mem-info:

kernel: DMA per-cpu:

kernel: cpu 0 hot: high 0, batch 1 used:0

kernel: cpu 0 cold: high 0, batch 1 used:0

kernel: cpu 1 hot: high 0, batch 1 used:0

kernel: cpu 1 cold: high 0, batch 1 used:0

kernel: DMA32 per-cpu:

kernel: cpu 0 hot: high 186, batch 31 used:46

kernel: cpu 0 cold: high 62, batch 15 used:12

kernel: cpu 1 hot: high 186, batch 31 used:25

kernel: cpu 1 cold: high 62, batch 15 used:14

kernel: Normal per-cpu: empty

kernel: HighMem per-cpu: empty

kernel: Free pages: 6040kB (0kB HighMem)

kernel: Active:120445 inactive:113583 dirty:0 writeback:0 unstable:0 free:1510 slab:4042 mapped-file:548 mapped-anon:234724 pagetables:7852

kernel: DMA free:2040kB min:32kB low:40kB high:48kB active:0kB inactive:0kB present:9060kB pages_scanned:0 all_unreclaimable? yes

kernel: lowmem_reserve[]: 0 994 994 994

kernel: DMA32 free:4000kB min:4016kB low:5020kB high:6024kB active:481780kB inactive:454332kB present:1018080kB pages_scanned:1718135 all_unreclaimable? yes

以上的日志信息标红处就是出现 OOM Killer(Out Of Memory killer)。

原理:

linux oom-killer是一种自我保护机制,当系统分配不出内存时(触发条件)会触发这个机制,由操作系统在己有进程中挑选一个占用内存较多,回收内存收益最大的进程kill掉来释放内存。

系统为每个进程做评估(/proc/<pid>/oom_score中数值最大的进程被kill掉)。

详见oom_killer.c

/**
* badness – calculate a numeric value for how bad this task has been
* @p: task struct of which task we should calculate
* @uptime: current uptime in seconds
*
* The formula used is relatively simple and documented inline in the
* function. The main rationale is that we want to select a good task
* to kill when we run out of memory.
*
* Good in this context means that:
* 1) we lose the minimum amount of work done
* 2) we recover a large amount of memory
* 3) we don’t kill anything innocent of eating tons of memory
* 4) we want to kill the minimum amount of processes (one)
* 5) we try to kill the process the user expects us to kill, this
* algorithm has been meticulously tuned to meet the principle
* of least surprise … (be careful when you change it)
*/

Linux下有3种Overcommit的策略(参考内核文档:vm/overcommit-accounting),可以在/proc/sys/vm/overcommit_memory配置。取0,1和2三个值,默认是0。

0:启发式策略,比较严重的Overcommit将不能得逞,比如你突然申请了128TB的内存。而轻微的Overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多些。

1:永远允许Overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。

2:永远禁止Overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。

转载于:https://www.cnblogs.com/youngerchina/p/5624592.html

<think>嗯,用户现在想深入了解Linux内存回收机制中kswapd与OOMKiller的协作原理。这属于操作系统内存管理的核心机制,需要结合内核源代码和实际行为来解释。回顾之前的对话,用户已经了解过内存分配的基本流程(从虚拟内存申请到物理页分配),现在想深入回收机制。用户提供的三个引用非常关键:引用1说明缺页中断触发物理内存分配;引用2提到OOMKiller的触发条件;引用3涉及伙伴系统分配器。这些正好构成协作链条的基础。用户可能是个系统运维工程师或内核开发者,因为ta的问题直指生产环境中最头疼的内存不足问题。ta需要的不只是理论,而是想理解二者如何配合来避免系统崩溃。从ta连续追问的态势看,对"协作"这个动态过程特别关注。需要构建一个阶梯式框架:先独立说明kswapd和OOM的职责,再重点分析它们的协作接口。特别要注意两点技术细节:一是kswapd如何通过zone->watermark水位评估内存压力;二是OOM选择进程的评分算法要具体到mm->oom_score的计算方法。突然想到个易错点:很多人混淆直接回收和异步回收的触发边界。必须在回答里明确写出判断公式:$$(alloc_flags\&ALLOC_KSWAPD)\land!zone_watermark_fast()$$这个判断来自__alloc_pages_slowpath(),决定是否唤醒kswapd线程。实操建议部分,用户可能会需要/proc/sys/vm/的调优参数,比如调整watermark_scale_factor的影响曲线。不过OOM调整风险高,得强调备份的重要性。最后的延伸问题要引导到cgroup内存子系统,这是云原生场景的实际需求。用户如果做容器运维,接下来可能会问memory.low和oom_group的作用机制。</think> 以下是Linux内存回收机制中 **kswapd** 与 **OOM Killer** 的协作原理详解: --- ### **一、协作机制分层框架** ```mermaid graph LR A[进程申请内存] --> B{物理内存充足?} B --> |是| C[正常分配物理页] B --> |否| D[触发直接回收] D --> E{回收足够内存?} E --> |是| C E --> |否| F[唤醒kswapd后台回收] F --> G{kswapd回收成功?} G --> |是| C G --> |否| H[触发OOM Killer] ``` --- ### **二、关键机制详解** #### **1. kswapd:后台异步回收** - **触发条件**:当空闲内存低于**低水位线**(*low watermark*)时唤醒: $$ \text{空闲内存} < \text{low\_watermark} = \text{min\_free\_kbytes} \times \text{缩放系数} $$ - **回收目标**: ```c // 内核代码 mm/vmscan.c kswapd_try_to_sleep() -> balance_pgdat() ``` 回收三类内存: - 文件页缓存(通过`shrink_page_list()`) - 匿名页(通过`shrink_anon()`交换到swap) - Slab缓存(通过`shrink_slab()`) #### **2. 直接回收:同步阻塞回收** - **触发条件**:申请内存时检测到**空闲内存 < 高水位线**(*high watermark*)且kswapd回收未完成: $$ \text{alloc\_flags} \& \text{ALLOC\_DIRECT\_RECLAIM} $$ - **行为特性**: **阻塞当前进程**,立即执行与kswapd相同的回收逻辑。 #### **3. OOM Killer:终极手段** - **触发条件**: $$ \left( \text{kswapd回收失败} \right) \land \left( \text{直接回收失败} \right) \land \left( \text{空闲内存} \approx 0 \right) $$ - **进程选择算法**(通过`oom_badness()`计算): $$ \text{oom\_score} = \frac{\text{进程占用内存} \times 1000}{\sqrt{\text{进程运行时间}}} $$ 优先杀死分数最高的进程[^1][^2]。 --- ### **三、协作流程图解** ```mermaid sequenceDiagram participant 进程 as 应用程序 participant 分配器 as 内存分配器 participant kswapd participant OOM as OOM Killer 进程->>分配器: 申请内存 alt 内存充足 分配器-->>进程: 分配成功 else 内存不足 分配器->>分配器: 尝试直接回收 alt 直接回收获胜 分配器-->>进程: 分配成功 else 直接回收失败 分配器->>kswapd: 唤醒后台回收 loop 异步回收循环 kswapd->>kswapd: 扫描LRU链表 alt 回收到足够内存 kswapd-->>分配器: 回收完成 分配器-->>进程: 分配成功 end end kswapd->>OOM: 通知回收失败 OOM->>OOM: 计算oom_score OOM->>进程: 杀死高内存进程 end end ``` --- ### **四、调优建议** 1. **调整水位线**: ```bash sysctl -w vm.watermark_scale_factor=200 # 加大回收阈值(默认10-1000) ``` 2. **限制OOM行为**: ```bash echo -17 > /proc/<pid>/oom_adj # 保护关键进程 ``` 3. **优化交换策略**: ```bash sysctl -w vm.swappiness=60 # 控制匿名页回收倾向(0-100) ``` > ⚠️ **注意**: > 过度依赖OOM Killer会导致服务中断,应通过监控`/proc/meminfo`中的`LowFree`和`SwapFree`提前预警。 --- ### **相关问题** 1. **`min_free_kbytes`设置过低会导致OOM更频繁触发吗?** $$ \text{低水位线} \propto \text{min\_free\_kbytes} $$ 过低会延迟kswapd唤醒,增加直接回收和OOM风险。 2. **如何通过cgroup避免容器的OOM?** 使用`memory.low`软限制保护容器内存,避免被优先回收。 3. **kswapd是否回收共享内存(tmpfs)?** 是!共享内存页会被标记为`PG_SWAPBACKED`参与回收。 4. **为什么OOM Killer优先杀死高`oom_score`进程而非父进程?** 最大化释放内存资源,父进程可能内存占用低但持有子进程残留内存。 5. **NUMA架构下内存回收有何不同?** kswapd会优先回收本地节点内存,触发跨节点回收时性能下降显著[^3]。 [^1]: Linux物理内存分配与回收机制 [^2]: OOM选择策略与进程优先级控制 [^3]: NUMA内存管理策略对比
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值