Linux OOM killer 与相关参数详解
CONSTRAINT_NONE,
CONSTRAINT_CPUSET,
CONSTRAINT_MEMORY_POLICY,
CONSTRAINT_MEMCG,
};
int order, const nodemask_t *nodemask)
{
if (likely(!sysctl_panic_on_oom))----0表示启动OOM killer,因此直接return了
return;
if (sysctl_panic_on_oom != 2) {----2是强制panic,不是2的话,还可以商量
if (constraint != CONSTRAINT_NONE)---在有cpuset、memory policy、memcg的约束情况下
return; 的OOM,可以考虑不panic,而是启动OOM killer
}
dump_header(NULL, gfp_mask, order, NULL, nodemask);
panic("Out of memory: %s panic_on_oom is enabled\n",
sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");---死给你看啦
}
int order, nodemask_t *nodemask, bool force_kill) {
check_panic_on_oom(constraint, gfp_mask, order, mpol_mask);
!oom_unkillable_task(current, NULL, nodemask) &&
current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
get_task_struct(current);
oom_kill_process(current, gfp_mask, order, 0, totalpages, NULL,
nodemask, "Out of memory (oom_kill_allocating_task)");
goto out;
}
}
const nodemask_t *nodemask, unsigned long totalpages)
{……
if (adj == OOM_SCORE_ADJ_MIN) {----------------------(1)
task_unlock(p);
return 0;---------------------------------(2)
}
atomic_long_read(&p->mm->nr_ptes) + mm_nr_pmds(p->mm);---------(3)
task_unlock(p);
points -= (points * 3) / 100;
points += adj;
return points > 0 ? points : 1;
}
OOM killer
当物理内存和交换空间都被用完时,如果还有进程来申请内存,内核将触发OOM killer,其行为如下:
1.检查文件/proc/sys/vm/panic_on_oom,如果里面的值为2,那么系统一定会触发panic
2.如果/proc/sys/vm/panic_on_oom的值为1,那么系统有可能触发panic(见后面的介绍)
3.如果/proc/sys/vm/panic_on_oom的值为0,或者上一步没有触发panic,那么内核继续检查文件/proc/sys/vm/oom_kill_allocating_task
3.如果/proc/sys/vm/oom_kill_allocating_task为1,那么内核将kill掉当前申请内存的进程
4.如果/proc/sys/vm/oom_kill_allocating_task为0,内核将检查每个进程的分数,分数最高的进程将被kill掉(见后面介绍)
进程被kill掉之后,如果/proc/sys/vm/oom_dump_tasks为1,且系统的rlimit中设置了core文件大小,将会由/proc/sys/kernel/core_pattern里面指定的程序生成core dump文件,这个文件里将包含
pid, uid, tgid, vm size, rss, nr_ptes, nr_pmds, swapents, oom_score_adj
score, name等内容,拿到这个core文件之后,可以做一些分析,看为什么这个进程被选中kill掉。
这里可以看看ubuntu默认的配置:
- #OOM后不panic
- dev@ubuntu:~$ cat /proc/sys/vm/panic_on_oom
- 0
- #OOM后kill掉分数最高的进程
- dev@ubuntu:~$ cat /proc/sys/vm/oom_kill_allocating_task
- 0
- #进程由于OOM被kill掉后将生成core dump文件
- dev@ubuntu:~$ cat /proc/sys/vm/oom_dump_tasks
- 1
- #默认max core file size是0, 所以系统不会生成core文件
- dev@ubuntu:~$ prlimit|grep CORE
- CORE max core file size 0 unlimited blocks
- #core dump文件的生成交给了apport,相关的设置可以参考apport的资料
- dev@ubuntu:~$ cat /proc/sys/kernel/core_pattern
- |/usr/share/apport/apport %p %s %c %P
参考:apport
panic_on_oom
正如上面所介绍的那样,该文件的值可以取0/1/2,0是不触发panlic,2是一定触发panlic,如果为1的话就要看mempolicy和cpusets,这篇不介绍这方面的内容。
panic后内核的默认行
为是死在那里,目的是给开发人员一个连上去debug的机会。但对于大多数应用层开发人员来说没啥用,倒是希望它赶紧重启。为了让内核panic后重启,可以修改文件/proc/sys/kernel/panic,里面表示的是panic多少秒后系统将重启,这个文件的默认值是0,表示永远不重启。
- #设置panic后3秒重启系统
- dev@ubuntu:~$ sudo sh -c "echo 3 > /proc/sys/kernel/panic"
调整分数
当oom_kill_allocating_task的值为0时(系统默认配置),系统会kill掉系统中分数最高的那个进程,这里的分数是怎么来的呢?该值由内核维护,并存储在每个进程的/proc/<pid>/oom_score文件中。
每个进程的分数受多方面的影响,比如进程运行的时间,时间越长表明这个程序越重要,所以分数越低;进程从启动后分配的内存越多,表示越占内存,分数会越高;这里只是列举了一两个影响分数的因素,实际情况要复杂的多,需要看内核代码,这里有篇文章可以参考:Taming the OOM killer
由于分数计算复杂,比较难控制,于是内核提供了另一个文件用来调控分数,那就是文件/proc/<pid>/oom_adj,这个文件的默认值是0,但它可以配置为-17到15中间的任何一个值,内核在计算了进程的分数后,会和这个文件的值进行一个计算,得到的结果会作为进程的最终分数写入/proc/<pid>/oom_score。计算方式大概如下:
如果/proc/<pid>/oom_adj的值为正数,那么分数将会被乘以2的n次方,这里n是文件里面的值
如果/proc/<pid>/oom_adj的值为负数,那么分数将会被除以2的n次方,这里n是文件里面的值
由于进程的分数在内核中是一个16位的整数,所以-17就意味着最终进程的分数永远是0,也即永远不会被kill掉。
当然这种控制方式也不是非常精确,但至少比没有强多了。
修改配置
上面的这些文件都可以通过下面三种方式来修改,这里以panic_on_oom为例做个示范:
直接写文件(重启后失效)
dev@ubuntu:~$ sudo sh -c "echo 2> /proc/sys/vm/panic_on_oom"
通过控制命令(重启后失效)
dev@dev:~$ sudo sysctl vm.panic_on_oom=2
修改配置文件(重启后继续生效)
- #通过编辑器将vm.panic_on_oom=2添加到文件sysctl.conf中(如果已经存在,修改该配置项即可)
- dev@dev:~$ sudo vim /etc/sysctl.conf
- #重新加载sysctl.conf,使修改立即生效
- dev@dev:~$ sudo sysctl -p
日志
一旦OOM killer被触发,内核将会生成相应的日志,一般可以在/var/log/messages里面看到,如果配置了syslog,日志可能在/var/log/syslog里面,这里是ubuntu里的日志样例
- dev@dev:~$ grep oom /var/log/syslog
- Jan 23 21:30:29 dev kernel: [ 490.006836] eat_memory invoked oom-killer: gfp_mask=0x24280ca, order=0, oom_score_adj=0
- Jan 23 21:30:29 dev kernel: [ 490.006871] [<ffffffff81191442>] oom_kill_process+0x202/0x3c0
cgroup的OOM killer
除了系统的OOM killer之外,如果配置了memory cgroup,那么进程还将受到自己所属memory cgroup的限制,如果超过了cgroup的限制,将会触发cgroup的OOM killer,cgroup的OOM killer和系统的OOM killer行为略有不同,详情请参考Linux Cgroup系列(04):限制cgroup的内存使用。
malloc
malloc是libc的函数,C/C++程序员对这个函数应该都很熟悉,它里面实际上调用的是内核的sbrk和mmap,为了避免频繁的调用内核函数和优化性能,它里面在内核函数的基础上实现了一套自己的内存管理功能。
既然内存不够时有OOM killer帮我们kill进程,那么这时调用的malloc还会返回NULL给应用进程吗?答案是不会,因为这时只有两种情况:
当前申请内存的进程被kill掉:都被kill掉了,返回什么都没有意义了
其它进程被kill掉:释放出了空闲的内存,于是内核就能给当前进程分配内存了
那什么时候我们调用malloc的时候会返回NULL呢,从malloc函数的帮助文件可以看出,下面两种情况会返回NULL:
使用的虚拟地址空间超过了RLIMIT_AS的限制
使用的数据空间超过了RLIMIT_DATA的限制,这里的数据空间包括程序的数据段,BSS段以及heap
关于虚拟地址空间和heap之类的介绍请参考Linux进程的内存使用情况,这两个参数的默认值为unlimited,所以只要不修改它们的默认配置,限制就不会被触发。有一种极端情况需要注意,那就是代码写的有问题,超过了系统的虚拟地址空间范围,比如32位系统的虚拟地址空间范围只有4G,这种情况下不确定系统会以一种什么样的方式返回错误。
rlimit
上面提到的RLIMIT_AS和RLIMIT_DATA都可以通过函数getrlimit和setrlimit来设置和读取,同时linux还提供了一个prlimit程序来设置和读取rlimit的配置。
prlimit是用来替代
ulimit的一个程序,除了能设置上面的那两个参数之外,还有其它的一些参数,比如core文件的大小。关于prlimit的用法请参考它的帮助文件。
- #默认情况下,RLIMIT_AS和RLIMIT_DATA的值都是unlimited
- dev@dev:~$ prlimit |egrep "DATA|AS"
- AS address space limit unlimited unlimited bytes
- DATA max data size unlimited unlimited bytes
测试代码
C语言的程序会受到libc的影响,可能在触发OOM killer之前就触发了segmentfault错误,如果要用C语言程序来测试触发OOM killer,一定要注意malloc的行为受MMAP_THRESHOLD影响,一次申请分配太多内存的话,malloc会调用mmap映射内存,从而不一定触发OOM killer,具体细节目前还不太清楚。这里是一个触发oom killer的例子,供参考:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #define M (1024 * 1024)
- #define K 1024
- int main(int argc, char *argv[])
- {
- char *p;
- int size =0;
- while(1) {
- p = (char *)malloc(K);
- if (p == NULL){
- printf("memory allocate failed!\n");
- return -1;
- }
- memset(p, 0, K);
- size += K;
- if (size%(100*M) == 0){
- printf("%d00M memory allocated\n", size/(100*M));
- sleep(1);
- }
- }
- return 0;
- }
结束语
对一个进程来说,内存的使用受多种因素的限制,可能在系统内存不足之前就达到了rlimit和memory cgroup的限制,同时它还可能受不同编程语言所使用的相关内存管理库的影响,就算系统处于内存不足状态,申请新内存也不一定会触发OOM killer,需要具体问题具体分析。
参考
转载自0:
Linux OOM killer 与相关参数详解的更多相关文章
- MySQL复制相关参数详解
MySQL复制相关参数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.复制相关系统变量 1>.server_id 是必须设置在master和每个slave上的唯一标 ...
- 【DataGuard】部署Data Guard相关参数详解 (转载)
原文地址:[DataGuard]部署Data Guard相关参数详解 作者:secooler 有关物理Data Guard部署参考<[DataGuard]同一台主机实现物理Data Gua ...
- Linux 之 编译器 gcc/g++参数详解
2016年12月9日16:48:53 ----------------------------- 内容目录: [介绍] gcc and g++分别是gnu的c & c++编译器 gcc/g++ ...
- gcc与g++区别以及相关参数详解
---恢复内容开始--- 原文链接:g++和gcc的区别 一 .二者区别 gcc和g++都是GNU(一个组织)的编译器. 1.对于.c后缀的文件,gcc把它当做是C程序:g++当做是C++程序: 2. ...
- flume基本概念及相关参数详解
1.flume是分布式的日志收集系统,把手机来的数据传送到目的地去 2.flume传输的数据的基本单位是 event,如果是文本文件,通常是一行记录. event代表着一个数据流的最小完整 ...
- mount挂载相关参数详解
mount [ -t 设备类型 ] [ -o 扩展参数 ] dev dir -t:指定mount挂载设备类型,常见的类型有nfs.ntfs.vfat.iso9660等: is09 ...
- linux系统下top命令参数详解
简介 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. top显示系统当前的进程和其他状况,是一个动态显示过程,即可以通过用户按 ...
- mybatis学习记录三——SqlMapConfig.xml相关参数详解
5 SqlMapConfig.xml mybatis的全局配置文件SqlMapConfig.xml,配置内容如下: properties(属性) settings(全局配置参数) ty ...
- Hbase相关参数详解
转载:http://www.cnblogs.com/nexiyi/p/hbase_config_94.html 版本:0.94-cdh4.2.1 hbase-site.xml配置 hbase.tmp. ...
随机推荐
- 通用32位CPU 常用寄存器及其作用
目录 32位CPU所含有的寄存器 数据寄存器 变址寄存器 指针寄存器 段寄存器 指令指针寄存器 标志寄存器 32位CPU所含有的寄存器 4个数据寄存器(EAX.EBX.ECX和EDX) 2个变址和指针 ...
- vue 写一个瀑布流插件
效果如图所示: 采用了预先加载图片,再计算高度的办法..网络差的情况下,可能有点卡 新建 vue-water-easy.vue 组件文件 <template> <div class ...
- netcore使用EFcore(第一个实例)
说明:搭建netcore 使用efcore入门教程,跟着这个教程,傻瓜都可以成功!O(∩_∩)O哈哈~,咱们开始吧: 首先介绍下环境: vs2017, netcore2.2, EntityFramew ...
- interface Part2(定义接口)
一. 在 C# 语言中,类之间的继承关系仅支持单重继承,而接口是为了实现多重继承关系设计的. 二. 一个类能同时实现多个接口,还能在实现接口的同时再继承其他类,并且接口之间也可以继承. 三. 无论是表 ...
- iOS 如何判断一个点在某个指定区域中
在iOS 开发中会遇到 判断位置的情况 iOS 自己都有函数实现的这些功能. 判断一个点是否在这个rect区域中 bool CGRectContainsPoint(CGRect rect,CGPoin ...
- hive分区理念介绍
一.背景 1.在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作.有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念. 2.分区表指的是在创建表 ...
- 探究Java如何实现原子操作(atomic operation)
1. 让我们首先了解下java 中 Volatile 关键字 Volatile可实现java内存模型当中的可见性, java内存模型的可见性: 可见性,是指线程之间的可见性,一个线程修改的状态对另一个 ...
- ubuntu18.04安装wine
wine是一个兼容层,可以从多平台(linux,macos,等)运行windows应用. Wine (Wine Is Not an Emulator)[即Wine不是一个模拟器]是一个在Linux和U ...
- Image Processing and Computer Vision_Review:A Performance Evaluation of Local Descriptors——2005.08
翻译 本地描述符的性能评估——http://tongtianta.site/paper/56756 摘要 - 在本文中,我们比较了为局部感兴趣区域计算的描述符的性能,例如,由Harris-Affine ...
- CSS之选择器相关
一.选择器的作用 选择器就是用来选择标签的,要使用css对HTML页面中的元素实现一对一,一对多或者多对一的控制,这就需要用到CSS选择器. HTML页面中的元素就是通过CSS选择器进行控制的.每一条 ...