OOM killer 是基于虚拟内存还是物理内存?
前言
偶然在网上看到一个问题: OOM 是按照虚拟内存还是实际内存来打分? 这里“实际内存”表达的意思应该是物理内存,而“打分”想表达的意思应该是 OOM killer 机制里面的 badness score
. 当内存吃紧时,假如开启了 OOM killer,OOM killer 会计算进程的 badness score
, badness score
越高,就越优先被 OOM killer 杀死.
实验
当内存吃紧,页分配器尝试回收物理Page失败后,会调用 OOM killer,选择 badness score
最高的进程杀死,释放内存. badness score
的分数范围是0-1000, 0表示不杀死, 1000表示总是杀死, 可以直接通过 cat /proc/<pid>/oom_score
查看进程的 badness score
.
这个问题的本质是 OOM 机制是基于虚拟内存还是物理内存,我们可以先通过一个实验验证这个问题:
机器内存:
通过free -h
可以查看机器的内存情况,物理内存为16GB
实验代码
1 |
|
这段代码新建了一个名为 oom_killer_file
的文件,先使用 posix_fallocate()
预分配16GB的大小,然后利用 mmap()
分配16GB的虚拟空间, 这段代码会 sleep 600s, 假如 OOM killer 是基于虚拟内存的,这段代码会被 kill. mmap()
的原理可以查看文章mmap源码分析, 调用 mmap()
会为进程分配虚拟内存,当真正写入触发缺页中断时才分配物理内存页.
编译运行:
1 | g++ -std=c++11 -O2 oom_killer.cc -o oom_killer |
实验结果:
我们通过 htop
观察发现进程确实已经分配了16GB的虚拟内存,但物理内存只有1768 bytes, 而经过600s的运行,程序并没有被 kill, 所以可以断定 OOM killer 不是基于虚拟内存而应该是物理内存计算 badness score
.
OOM killer源码分析
OOM killer的核心函数是 out_of_memory()
, 执行流程如下:
- 调用
check_panic_on_oom()
检查是否允许执行内核恐慌,假如允许,需要重启系统. - 假如打开了
/proc/sys/vm/oom_kill_allocating_task
即允许 kill 掉当前正在申请分配物理内存的进程,那么杀死当前进程. - 调用
select_bad_process
,选择badness score
最高的进程. - 调用
oom_kill_process
, 杀死选择的进程.
我们分析 badness score
的计算函数来理解 OOM killer如何选择需要被 kill 掉的进程:
1 | unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg, |
总结:
通过分析 badness score
的计算函数,我们可以发现 OOM killer 是基于RSS即常驻的物理内存来选择进程进行kill, 从而释放内存. Linux内核内存管理部分最主要的一个逻辑就是延迟分配.