本篇文章主要和大家分享linux的oom_score的工作原理。
关于kubernetes的Qos在之前的blog已经分享过了,感兴趣的童鞋可以去看一下。k8s的Qos底层便是依赖内核的OOM机制。
linux的OOM killer是为了在内存不足的情况下,杀掉消耗内存最多、优先级最低的任务,从而保障系统不被搞挂。这里有两个取决因素:(1)oom_score内核通过内存消耗计算得出 (2)oom_score_adj允许用于自定义,等同于优先级,优先级越高值越小(-1000 ~1000)。
当内存紧张的时候,内核通过 oom = oom_score + oom_score_adj 计算出分数最高的进程,向其发送关闭信号。
整个流程怎样的呢?直接上代码(代码位于内核/mm/oom_kill.c)
1、计算oom
//计算oom
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
const nodemask_t *nodemask, unsigned long totalpages)
{
//总内存= 物理内存 + 交换分区
*totalpages = totalram_pages + total_swap_pages;
//累加 常驻内存RSS + 进程页面 +交换内存
points = get_mm_rss(p->mm) + atomic_long_read(&p->mm->nr_ptes) +
get_mm_counter(p->mm, MM_SWAPENTS);
//oom_score_adj 归一化
adj *= totalpages / 1000;
//oom = oom_score + oom_score_adj
points += adj;
2、通过循环调用oom_badness找出oom最大的进程
//找出oom最大的进程
static struct task_struct *select_bad_process(unsigned int *ppoints,
unsigned long totalpages, const nodemask_t *nodemask,
bool force_kill)
//循环遍历所有进程
for_each_process_thread(g, p) {
//调用上面的oom_badness方法计算oom
points = oom_badness(p, NULL, nodemask, totalpages);
//选出最大oom进程
if (!points || points < chosen_points)
continue;
if (points == chosen_points && thread_group_leader(chosen))
continue;
chosen = p;
chosen_points = points;
3、关闭进程
//发送kill信号关闭进程
oom_kill_process(p, gfp_mask, order, points, totalpages, NULL,
nodemask, "Out of memory");
如果觉得上面计算oom_score代码看着有点晕,简单来说就是
oom_score = 内存消耗/总内存 *1000
其中
内存消耗包括了:常驻内存RSS + 进程页面 +交换内存
总内存就简单了:总的物理内存 +交换分区
下面通过一个Demo证实一下。
通过下面的脚本可以打印所以oom_score不为0的程序
#!/bin/bash
# Displays running processes in descending order of OOM score
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
输出如下:
PID OOM Score OOM Adj Command
7859 26 0 java
可以看到pid为7859进程的oom_score 为26
# pidstat -r -p 7859
05:50:32 PM UID PID minflt/s majflt/s VSZ RSS %MEM Command
05:50:32 PM 0 7859 0.15 0.00 10081456 363704 2.21 java
程序占用的RSS为:363704KB/1024 = 355.2M
通过下面脚本可以看出swap使用
for i in $(ls /proc | grep "^[0-9]" | awk '$0>100'); do awk '/Swap:/{a=a+$2}END{print '"$i"',a/1024"M"}' /proc/$i/smaps;done| sort -k2nr | head
7859 292.898M
考虑到页表本身占用很少,在此忽略了,
# free -m
total used free shared buff/cache available
Mem: 16040 1878 2095 290 12066 13397
Swap: 8191 969 7222
整个机器的内存:16040 + 8191 = 24231 M。
综合上面的数据可以算出: oom_score = (293+355)/24231 和oom_score相等。
补充一下,如果想修改oom_score_adj可以通过下面的方式
echo -200 > /proc/进程ID/oom_score_adj