理解和配置 Linux 下的 OOM Killer

来源:互联网 发布:微纳制造 知乎 编辑:程序博客网 时间:2024/05/21 15:45

文章转至http://www.vpsee.com/2013/10/how-to-configure-the-linux-oom-killer


最近同事碰到一个linux上 Tuxedo 因为 Out of Memory 的问题导致主要进程被 OMM-killer杀掉,这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃。如果检查相关的日志文件(/var/log/messages)就会看到下面类似的 Out of memory: Kill process 信息:

...Out of memory: Kill process 9682 (mysqld) score 9 or sacrifice childKilled process 9682, UID 27, (mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kBhttpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0httpd cpuset=/ mems_allowed=0Pid: 8911, comm: httpd Not tainted 2.6.32-279.1.1.el6.i686 #1...21556 total pagecache pages21049 pages in swap cacheSwap cache stats: add 12819103, delete 12798054, find 3188096/4634617Free swap  = 0kBTotal swap = 524280kB131071 pages RAM0 pages HighMem3673 pages reserved67960 pages shared124940 pages non-shared


Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。


内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。


/** * oom_badness - heuristic function to determine which candidate task to kill * @p: task struct of which task we should calculate * @totalpages: total present RAM allowed for page allocation * * The heuristic for determining which task to kill is made to be as simple and * predictable as possible.  The goal is to return the highest value for the * task consuming the most memory to avoid subsequent oom failures. */unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,  const nodemask_t *nodemask, unsigned long totalpages){long points;long adj;if (oom_unkillable_task(p, memcg, nodemask))return 0;p = find_lock_task_mm(p);if (!p)return 0;adj = (long)p->signal->oom_score_adj;if (adj == OOM_SCORE_ADJ_MIN) {task_unlock(p);return 0;}/* * The baseline for the badness score is the proportion of RAM that each * task's rss, pagetable and swap space use. */points = get_mm_rss(p->mm) + p->mm->nr_ptes + get_mm_counter(p->mm, MM_SWAPENTS);task_unlock(p);/* * Root processes get 3% bonus, just like the __vm_enough_memory() * implementation used by LSMs. */if (has_capability_noaudit(p, CAP_SYS_ADMIN))adj -= 30;/* Normalize to oom_score_adj units */adj *= totalpages / 1000;points += adj;/* * Never return 0 for an eligible task regardless of the root bonus and * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here). */return points > 0 ? points : 1;}


上面代码里的注释写的很明白,理解了这个算法我们就理解了为啥 Tuxedo 躺着也能中枪了,因为它的体积总是最大(一般来说它在系统上占用内存最多),所以如果 Out of Memeory (OOM) 的话总是不幸第一个被 kill 掉。解决这个问题最简单的办法就是增加内存,或者想办法优化你的应用程序(如Tuxedo更少的使用内容)。或者像办法优化OS的内存,让程序可以使用更多的内存,还有一个临时的办法就是调整内核参数,让 Tuxedo 进程不容易被 OOM killer 发现。


# ps aux | grep mysqldmysql    2196  1.6  2.1 623800 44876 ?        Ssl  09:42   0:00 /usr/sbin/mysqld# cat /proc/2196/oom_score_adj0# echo -15 > /proc/2196/oom_score_adj

当然,如果需要的话可以完全关闭 OOM killer(不推荐用在生产环境):

# sysctl -w vm.overcommit_memory=2# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf


找出最有可能被 OOM Killer 杀掉的进程

我们知道了在用户空间可以通过操作每个进程的 oom_adj 内核参数来调整进程的分数,这个分数也可以通过 oom_score 这个内核参数看到,比如查看进程号为981的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3:

# cat /proc/981/oom_score18# echo -15 > /proc/981/oom_score_adj# cat /proc/981/oom_score3


下面这个 bash 脚本可用来打印当前系统上 oom_score 分数最高(最容易被 OOM Killer 杀掉)的进程:

# vi oomscore.sh#!/bin/bashfor proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do    printf "%2d %5d %s\n" \        "$(cat $proc/oom_score)" \        "$(basename $proc)" \        "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"done 2>/dev/null | sort -nr | head -n 10# chmod +x oomscore.sh# ./oomscore.sh18   981 /usr/sbin/mysqld 4 31359 -bash 4 31056 -bash 1 31358 sshd: root@pts/6 1 31244 sshd: vpsee [priv] 1 31159 -bash 1 31158 sudo -i 1 31055 sshd: root@pts/3 1 30912 sshd: vpsee [priv] 1 29547 /usr/sbin/sshd -D


0 0
原创粉丝点击