本篇文章主要和大家分享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
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐