理解和配置 Linux 下的 OOM Killer

From: http://www.vpsee.com/2013/10/how-to-configure-the-linux-oom-killer/

 

最近有位 VPS 客户抱怨 MySQL 无缘无故挂掉,还有位客户抱怨 VPS 经常死机,登陆到终端看了一下,都是常见的 Out of memory 问题。这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 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 child
Killed process 9682, UID 27, (mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
httpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
httpd cpuset=/ mems_allowed=0
Pid: 8911, comm: httpd Not tainted 2.6.32-279.1.1.el6.i686 #1
...
21556 total pagecache pages
21049 pages in swap cache
Swap cache stats: add 12819103, delete 12798054, find 3188096/4634617
Free swap  = 0kB
Total swap = 524280kB
131071 pages RAM
0 pages HighMem
3673 pages reserved
67960 pages shared
124940 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;
}

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

配置 OOM killer

我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统。

# sysctl -w vm.panic_on_oom=1
vm.panic_on_oom = 1
# sysctl -w kernel.panic=10
kernel.panic = 10
# echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
# echo "kernel.panic=10" >> /etc/sysctl.conf

从上面的 oom_kill.c 代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。比如,如果不想 MySQL 进程被轻易杀掉的话可以找到 MySQL 运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):

# ps aux | grep mysqld
mysql    2196  1.6  2.1 623800 44876 ?        Ssl  09:42   0:00 /usr/sbin/mysqld
# cat /proc/2196/oom_score_adj
0
# 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_score
18
# echo -15 > /proc/981/oom_score_adj
# cat /proc/981/oom_score
3

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

# vi oomscore.sh
#!/bin/bash
for 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.sh
18   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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 里面的单下划线与双下划线的区别

python 里面的单下划线与双下划线的区别 Python 用下划线作为变量前缀和后缀指定特殊变量。 _xxx 不能用from moduleimport *导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格:避免用下划线作为变量名的开始。 因为下划线对解释器有特殊的意义&#x…

前端vscode插件合集

工欲善其事必先利其器 安装步骤 . . . . . . . . . 1.中文包Chinese (Simplified) Language Pack for Visual Studio Code 2.自动闭合标签Auto Close Tag 3.同步修改标签Auto Rename Tag 4.高亮代码的回调,括号的区域范围Bracket Pair Colorizer 4.支持多中…

无法加载安装安装程序:Wbemupgd.dll

今天打开添加/删除程序 ,准备安装几个windows组件,结果出现下面的错误:无法加载安装安装程序:Wbemupgd.dll,或是找不到函数0cEntry.请与您的系统管理员联系。特定错误码是0x7e于是,搜索一下,原来是path环境变量中,缺少指向System32/wbem目录的指向...OK.…

Android卷一全文 第一章 阅读前的准备工作

本章主要内容本章简单介绍Android系统架构、编译环境的搭建以及一些工具的使用。1.1 系统架构 1.1.1 Android系统架构 Android是Google公司推出的一款手机开发平台。该平台本身是基于Linux内核的,图1-1展示了这个系统的架构:图1-1 Android系统架构 从…

/etc/fstab详解

From: http://blog.csdn.net/clozxy/article/details/5603222 fstab(/etc/fstab)是Linux下比较重要的配置文件,它包含了系统在启动时挂载文件系统和存储设备的详细信息。下面是我机子上的 fstab文件: LABEL/ / …

web安全字体

webfont解剖 Unicode字体可以包含数以千计字形有四个字体格式: WOFF2, WOFF, EOT, TTF一些字体格式需要使用GZIP压缩 一个web字体是字形的集合,且每个字形是一个描述了一个字母亦或符号的矢量图。 所以,一个字体文件的大小由两个因素决定&…

vue如何引入ant部分组件

官网ant 在marn.js下 import Vue from vue; import { Button, message } from ant-design-vue; import App from ./App;Vue.config.productionTip false;/* v1.1.2 */ Vue.component(Button.name, Button); Vue.component(Button.Group.name, Button.Group);/* v1.1.3 自动注…

各个行业纷纷瞅准了这块大蛋糕

今年以来,新疆民生工程和各援疆项目不断加大建设力度,钢材、石料等建筑上必不可少的材料需求见涨,钢铁行业和砂石行业纷纷进驻新疆市场,建厂投资,使新疆展现出一派向着繁荣发展的热火景象。黎明重工是矿山机械行业的领…

关于头文件中的 static inline函数

关于头文件中的 static inline函数头文件中常见static inline函数,于是思考有可能遇到的问题,如头文件经常会被包含会不会产生很多副本?网上说法不一。于是自己验证。经过arm-none-eabi-gcc下测试后得出结论。inline 关键字实际上仅是建议内联…

sql中问号是干什么的??

第一次在后台 程序中遇到sql语句中的问号: /*** * 方法描述 : 通过账号id更新该账号状态* param state 状态* param id 账号id*/ModifyingQuery("update LabAccount t set t.userState ?1 where t.userAcctId ?2")void updateState(String state, Stri…

linux下挂载windows上的共享目录,并设置所有者为非root用户

参考了很多文章,这里总结下我得出来的最优答案(针对我的需求而言)吧,但是还是存在bug,稍后指出! 以下是我的bash脚本,防止多次重复挂载,相信看了就能明白: [zcmvm-fedora20 share]$ cat m.sh #…

vue项目登录及token验证 vue-ant

在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下: 1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码 2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个…

广告狂人 第1季

《Mad Men/广告狂人》是由American Movie Classics公司出品的美剧。故事背景设定在上世纪六十年代的纽约,大胆地描述了美国广告业黄金时代残酷的商业竞争。该剧曾获得第65届、第66届、第67届美国电影电视金球奖最佳电视剧。并连续四年夺得艾美奖剧情类最佳电视剧奖。…

设置Clover默认进入Windows,按快捷键F8可选择不同的引导

系统情况: Win7 Mac10.9.5 Clover 我要达到的目标是:默认进入Windows系统,如果有需要,可以选择进入其他系统,如Mac OS X 我原以为可以在clover中配置,达到这个目标,可是我经过多次实践&am…

适配器和外观模式

结构型:Adapter与Facade(适配器和外观模式)   一般作为阅读材料,首先想要明确的是我现在了解的设计模式的初衷,即为了解决什么问题。 适配器,如果有买过港版Iphone在内地使用的人应该会有三角大插头必须接…

js获取cookie获取不到问题 vue获取cookie以及获取不到问题

1.下载依赖包 npm i js-cookie -S2.在使用cookie的页面上进行引入 import Cookies from js-cookie3.使用 创建一个在整个网站上有效的CookieCookies.set(name, value);创建一个从现在起7天后过期的cookie,在整个站点上有效:Cookies.set(name, value, …

smarty二维foreach示例[顺代一维数组],再次加强版

2019独角兽企业重金招聘Python工程师标准>>> smarty二维foreach示例[顺代一维数组],再次加强版 WEB2.0 root 2009-4-9 10:46 评论(0) 阅读(682) 大 | 中 | 小 WEB2.0 | 评论(0) | 引用(0) | 阅读(682) view plain print ? {foreach itemrec from$result…

linux 函数学习--kzalloc()

使用include <linux/slab.h>void *kmalloc(size_t size, int flags);static inline void *kzalloc(size_t size, gfp_t flags) { return kmalloc(size, flags | __GFP_ZERO); } 123456说明给 kmalloc 的第一个参数是要分配的块的大小. 第 2 个参数, 分配标志 用kza…

动态链接库和静态链接库

From: http://blog.163.com/zhangjie_0303/blog/static/99082706201081105654484/ 动态链接库dll&#xff0c;静态链接库lib, 导入库lib 目前以lib后缀的库有两种&#xff0c;一种为静态链接库(Static Libary&#xff0c;以下简称“静态库”)&#xff0c;另一种为动态连接库(…

div与div区别小结

两者之间的区别:例如div span得到的是div下所有的span元素&#xff0c;而div>span则是取得的div下第一级的span元素。 示例代码如下&#xff1a; <!DOCTYPE html><html lang"en" xmlns"http://www.w3.org/1999/xhtml"> <head><met…