Linux内核:OOM-killer打分机制
人生在世,被喜欢和被讨厌各占50%;自己喜欢自己,喜欢你的人占比就超过了50%。
Linux 内核中的 OOM (Out-Of-Memory) 机制是当系统内存耗尽时,用来杀死某些进程以释放内存的机制。为了决定应该杀死哪个进程,内核使用了一套复杂的打分系统,这个过程称为 OOM 打分机制。
基本概述
OOM 机制的触发
当系统内存耗尽,无法满足进程分配内存的需求时,内核会触发 OOM 杀手(OOM Killer)。OOM 杀手会选中一个进程并终止它,以释放内存。整个过程大致分为以下几个步骤:
- 检测内存不足:当系统内存不足且无法通过释放缓存、回收页面等方式缓解时,触发 OOM 机制。
- 选择目标进程:通过打分机制选定一个进程作为目标。
- 杀死目标进程:终止目标进程并释放其占用的内存资源。
OOM 打分机制
内核通过为每个进程打分来决定哪个进程应被杀死。打分机制主要依据进程对系统资源的占用情况,打分越高,越有可能被杀死。打分过程涉及多个因素:
- 进程的内存占用:占用内存越多的进程,得分越高。这是最重要的因素。
- 通过
mm->total_vm
(虚拟内存总量) 计算,值越大,打分越高。
- 通过
- 进程的优先级 (nice 值):进程的优先级也影响得分。优先级越低(nice 值越高),得分越低。
- 通过进程的
oom_score_adj
进一步调整。用户可以通过/proc/[pid]/oom_score_adj
来手动调节这个值,使某些进程更容易或更不容易被杀。
- 通过进程的
- 进程年龄:某些情况下,运行时间越长的进程得分越低,内核倾向于保留长期运行的服务进程。
- 子进程数:如果进程有大量子进程,意味着它可能是一个服务进程,内核可能降低它的得分。
- CAP_SYS_ADMIN 权限:具有此权限的进程通常得分较低,除非没有其他选择,因为这些进程通常是系统管理进程。
- 用户调整:用户可以通过调整
oom_score_adj
或者oom_adj
(旧版本)来影响某个进程的 OOM 分数。这些值允许用户通过/proc/[pid]/oom_score_adj
对某个进程的 OOM 得分进行加权。
具体打分公式
在 Linux 3.8 及更高版本中,内核使用了一个计算公式来评估进程的 OOM 分数:
1 | cCopy codepoints = mm->total_vm + (mm->hiwater_rss - mm->rss); |
解释:
total_vm
是进程的虚拟内存总量。hiwater_rss
是进程历史上消耗内存的最高点。rss
是当前实际驻留在内存中的页数。oom_score_adj
是用户自定义的调整值。
OOM 优先级与保护机制
- oom_score: 每个进程的 OOM 分数可以在
/proc/[pid]/oom_score
中查看。分数越高,进程越有可能被杀死。 - oom_adj 和 oom_score_adj:用户和管理员可以通过
/proc/[pid]/oom_adj
或/proc/[pid]/oom_score_adj
文件调整进程的 OOM 优先级。例如,将oom_score_adj
设置为-1000
可以保护进程不被 OOM 杀手杀死,而设置为1000
会让它优先被杀。
OOM 杀手的执行
一旦 OOM 打分完成,内核会选择得分最高的进程进行杀死。杀死进程时,会发送 SIGKILL
信号给目标进程,强制终止它。杀死目标进程后,内核会重新评估系统内存,如果仍然不足,可能继续杀死其他进程,直到内存压力缓解。
具体的系统配置
管理员可以通过 /proc/sys/vm/oom_kill_allocating_task
来配置是否优先杀死当前触发 OOM 的进程。此外,还可以通过 /proc/sys/vm/panic_on_oom
配置系统在 OOM 时是否直接崩溃而不是杀死进程。
源码
1. OOM Killer 触发机制
当系统内存耗尽且无法回收更多内存时,OOM Killer 会被触发。这个过程通常发生在内存管理的页面分配过程中,如果内核无法满足内存分配请求且所有回收机制(如文件系统缓存回收、页面交换等)均失败,就会调用 OOM Killer。
代码位置:
1 | // Linux 内核 进程内存管理代码 |
2. out_of_memory()
函数
out_of_memory()
是 OOM Killer 的入口函数,它决定是否需要触发 OOM Killer,以及后续的处理。
1 | // 文件:mm/oom_kill.c |
3. 选择被杀进程:select_bad_process()
select_bad_process()
函数会遍历系统中的所有进程,并为每个进程计算一个 OOM 分数,根据分数决定哪个进程应该被杀死。分数越高的进程越可能被杀死。
1 | // 文件:mm/oom_kill.c |
4. 计算 OOM 分数:oom_badness()
oom_badness()
函数根据多个因素计算进程的 OOM 分数,包括内存占用、优先级、进程年龄等。
1 | // 文件:mm/oom_kill.c |
5. 杀死进程:oom_kill_process()
oom_kill_process()
函数负责最终的杀死操作。它向选中的进程发送 SIGKILL
信号,强制终止该进程。
1 | // 文件:mm/oom_kill.c |
6. 相关数据结构
task_struct
是进程的核心数据结构,mm_struct
则用于描述进程的内存使用情况。oom_score_adj
则是用户可以调整的一个参数,用于影响进程的 OOM 打分。
1 | struct task_struct { |
总结
Linux 内核中的 OOM Killer 是一个复杂但高效的机制,用于在系统内存耗尽时自动释放内存资源。通过 out_of_memory()
函数触发,选择打分最高的进程并执行杀死操作,以保持系统的稳定性。用户还可以通过调整 oom_score_adj
等参数影响 OOM Killer 的行为。