Linux的OOM Killer机制研究
简单来说,linux的内存分为虚拟内存和物理内存,进程在申请内存时,首先申请的是虚拟内存,等到真正需要的时候才去申请物理内存。内核通过这种机制避免内存的浪费,实现了按需分配物理内存。
所以虚拟内存可以大于实际的物理内存,超过这部分就是memory overcommit,而一旦进程需要申请的物理内存超过实际内存和交换空间的总和,内核就会用OOM Killer,选择杀掉一个或部分进程,保障系统和其它进程有可用的内存。
1. overcommit 设置
在 Linux 中,可以通过内核参数vm.overcommit_memory去控制是否允许 overcommit:
- 默认值是 0,在这种情况下,只允许轻微的 overcommit,但一次申请大量内存会被禁止,root能使用的overcommit也比普通用户多
- 设置为 1,表示总是允许 overcommit
- 设置为 2,则表示总是禁止 overcommit,系统分配的内存不能超过系统内存+交换内存
2. OOM Killer 设置
linux内核会为每个进程算一个分数,发生OOM时分数最高的进程会被kill掉,主要看以下几个参数:
- /proc/PID/oom_score ,OOM 最终得分,值越大越有可能被杀掉
- /proc/PID/oom_score_adj ,取值范围为-1000到1000,计算oom_score时会加上该参数
- /proc/PID/oom_adj ,取值是-17到+15,该参数主要是为兼容旧版内核
只要允许overcommit,就会有oom killer发生,所以关闭overcommit就可以关闭OOM Killer:
sysctl -w vm.overcommit_memory=2
echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
如果不想关闭OOM,也可以直接调整相应进程的oom_score_adj和oom_adj值,来按需进行设置OOM的优先级:
echo -1000 > /proc/PID/oom_score_adj
或者:
echo -17 > /proc/PID/oom_adj
3. 找出最可能产生OOM的进程
通过脚本计算一下所有进程的oom_score和oom_score_adj,就可以找出最可能产生OOM的进程,系统进程的OOM score是0,需要排除,脚本是网上找的,内容如下:
#!/bin/bash
printf 'PID\tOOM_Score\tOOM_Adj\tCommand\n'
while read -r pid comm;
do
[ -f /proc/$pid/oom_score ] && [ $(cat /proc/$pid/oom_score) != 0 ] && printf '%d\t%d\t\t%d\t%s\n' "$pid" "$(cat /proc/$pid/oom_score)" "$(cat /proc/$pid/oom_score_adj)" "$comm";
done < <(ps -e -o pid= -o comm=) | sort -k 2nr
4. 手动触发OOM killer
学习的时候需要测试OOM,可以用以下方法手动触发OOM
- 前台执行一个循环,不停的为变量赋值,最终就会耗光内存
for b in {0..99999999}; do a=$b$a; done
- 多次循环触发OOM-killer
for x in {1..10}; do echo "Start trigger OOM-killer: $x"; bash -c "for b in {0..99999999}; do a=$b$a; done"; done
- 基本上面的方式,边触发OOM-killer,边监控OOM score
#!/bin/bash
oom(){
echo "Start trigger OOM-killer" && bash -c "for b in {0..99999999}; do a=$b$a; done"
}
oom &
ppid=`ps -ef |grep -v grep |grep 99999999 |awk '{print $3}'`
pid=`pgrep -P $ppid`
echo ppid: $ppid
echo fork pid: $pid
while [ -d "/proc/$pid" ];
do
echo $pid : oom_score: `cat /proc/$pid/oom_score`
done
5. OOM监控
OOM发生时,会在/var/log/message中出现如下日志:
Nov 26 10:51:25 nixops.me kernel: Out of memory: Kill process 18903 (bash) score 645 or sacrifice child
Nov 26 10:51:25 nixops.me kernel: Killed process 18903, UID 0, (bash) total-vm:3640712kB, anon-rss:3441992kB, file-rss:4kB
zabbix之类的监控软件可以通过关键字过滤,来进行监控。如果有ELK这类的日志系统,可以收集messages日志,用logstash插件进行告警。
参考文章:
https://askubuntu.com/questions/1188024/how-to-test-oom-killer-from-command-line/1188169#1188169
https://learning-kernel.readthedocs.io/en/latest/mem-management.html
[...]Linux的OOM Killer机制研究[...]