oom killer

概念

oom killer是内核设计的一种机制,在内存不足时选择一个占用内存较大的进程把他杀死,释放这一部分内存来满足内存请求的的需求。

oom(out of memory)

OOM(Out of Memory)指Linux在无可用内存时的一个最后策略。当系统中可用内存过少时,OOM killer会选择kill某些进程,来释放这些进程所占用的内存。

void out_of_memory(int gfp_mask)
{struct mm_struct *mm = NULL;task_t * p;read_lock(&tasklist_lock);retry:p = select_bad_process();//挑选合适的进程,大页框,少损失,低静态优先级,非root权限,不能是特殊进程if (PTR_ERR(p) == -1UL)goto out;/* Found nothing?!?! Either we hang forever, or we panic. */
if (!p) {//如果没有合适的进程read_unlock(&tasklist_lock);show_free_areas();panic("Out of memory and no killable processes...\n");
}printk("oom-killer: gfp_mask=0x%x\n", gfp_mask);
show_free_areas();
mm = oom_kill_process(p);//调用oom_kill_process删除进程p
if (!mm)goto retry;out:read_unlock(&tasklist_lock);if (mm)mmput(mm);/** Give "p" a good chance of killing itself before we* retry to allocate memory.*/__set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(1);}

触发时机

现代内核通常在分配内存时,允许申请的内存量超过实际可分配的free内存,这种技术称为Overcommit,可以认为是一种内存超卖的策略。开启了Overcommit,其实是基于一个普遍的规律——大部分应用并不会将其申请的内存全部用满——来尽量提升内存的利用率,可以允许系统分配出的内存总和超过系统能提供之和(物理内存+Swap空间),但是这种做法也导致OOM的风险。

​ overcommit的策略。

通过内核参数vm.overcommit_memory来进行控制,取值如下

  • 0 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。

  • Overcommit的内存过大将会失败,轻微的Overcommit将被允许。(如何区分?)

  • 1 永远允许Overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。

  • 2 永远禁止Overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。

事实上,如果选择2的话,在某些情况下内存得不到申请,又没办法释放内存,会导致系统死机。

可以查看本机上的overcommit

两个分别是内存允许分配的内存上限和实际已分配的内存大小。可以看到这里才须的是策略1。所以当参数为0或1时,允许omm killer工作。当内存没有swap可用,又无法reclaim到空闲内存时,就会触发oom killer选进程进行kill了。

杀死进程

哪个进程会被杀死?如何选择?linux会为每个进程进行分数统计,他会选择分数最高的进程杀死。这个分数统计需要兼顾一次kill释放出最多的内存,同时也要对系统重要性最小。为实现这个目标,内核对每个进程维护了一个评分oom_score,可以通过/proc//oom_score看到。分数越高则被kill的可能性越大。调整oom kill。

/proc/<pid>/oom_adj

调小可以降低被OOM killer选中的概率。从 -16 to +15来调整被选中的概率,如果设置为-17(OOM_DISABLE)的话,可以彻底避免进程被oom killer。默认值是0.

注意: oom_adj在Linux 2.6.36之后就废弃了,改用oom_score_adj,不过为向前兼容,改oom_adj或者oom_score_adj任意一个,另一个会做等比例的调整。

/proc/<pid>/oom_score

这个可以用来标记多大可能性进程会被oom killer选中。0表示不会被kill。值越大表示被选中kill概率越高。oom_score值的基础考量是进程用到的内存,结合下面一些考量点适当加+或者减-:

【+】是否使用fork创建了大量子进程

【-】是否长时间运行,或使用了大量CPU时间

【+】是否有更低nice优先级(比如>0,越大优先级越低)

【-】进程是否是privileged

【-】进程是否在直接访问硬件

再加上对oom_score_ad以及oom_adj调整的综合计算,得出最后打分值。

/proc/<pid>/oom_score_adj

取值范围-1000到1000。要说明这个值首先得明白oom killer选择对象时的启发式的打分策略(badness heuristic)。

这个策略会对所有任务打分0-1000,0表示永远不kill,而1000表示总是kill。这个值一定程度上反应的是任务使用了可用内存的比例(比如任务所有可用内存为100G,使用了100G,则打分1000.使用50G则打分500)。注意这里进程的“可用内存”指的是OOM-killer运行时系统内允许该进程使用的内存,跟CPUSET所占内存或者设置的内存上限有关。

root用户进程通常被认为比较重要,比其他进程高3%的优惠(adj -= 30; 分数越低越不容易被杀掉)。

oom_score_adj在系统打分基础上进行调整,进一步控制进程是优先还是尽量避免被kill。

所以,oom_score最高的就是会被OOM killer优先处理的进程,要控制进程的优先级,调整oom_score_adj。

如图1号init进程:

显然他不可能被杀死,当然他也不能被杀死。

这是编写的程序选出的最有可能被杀死的进程的分数排名。

识别oom

OOM killer会将kill的信息记录到系统日志/var/log/messages,检索相关信息就能匹配到是否进行了,如果配置了syslog,日志可能在/var/log/syslog里面。

下面是一个模拟oom killer的程序。

查看日志。

May 17 10:59:31 lp kernel: [ 4682.464038] Tasks state (memory values in pages):
May 17 10:59:31 lp kernel: [ 4682.464039] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
May 17 10:59:31 lp kernel: [ 4682.464053] [    396]     0   396    27853      167   200704       66             0 systemd-journal
May 17 10:59:31 lp kernel: [ 4682.464056] [    454]     0   454    12552      215   118784     1075         -1000 systemd-udevd
May 17 10:59:31 lp kernel: [ 4682.464058] [    522]   101   522    17719       62   176128      165             0 systemd-resolve
May 17 10:59:31 lp kernel: [ 4682.464059] [    524] 62583   524    36491       17   196608      101             0 systemd-timesyn
May 17 10:59:31 lp kernel: [ 4682.464060] [    623]     0   623    27629       38   118784       43             0 irqbalance
May 17 10:59:31 lp kernel: [ 4682.464062] [    626]   103   626    12878      124   147456      368          -900 dbus-daemon
May 17 10:59:31 lp kernel: [ 4682.464063] [    758]     0   758   140749      605   462848      174             0 NetworkManager
May 17 10:59:31 lp kernel: [ 4682.464064] [    797]     0   797    43071      213   241664     1767             0 networkd-dispat
May 17 10:59:31 lp kernel: [ 4682.464066] [    798]     0   798   125842       49   339968      512             0 udisksd
May 17 10:59:31 lp kernel: [ 4682.464066] [    807]   102   807    65760        0   159744      374             0 rsyslogd
May 17 10:59:31 lp kernel: [ 4682.464068] [    808]   116   808    11815       18   135168       68             0 avahi-daemon
May 17 10:59:31 lp kernel: [ 4682.464069] [    817]     0   817    90149       92   339968      258             0 ModemManager
May 17 10:59:31 lp kernel: [ 4682.464070] [    841]     0   841   273430     3154   311296     1319          -900 snapd
May 17 10:59:31 lp kernel: [ 4682.464071] [    842]     0   842     8269       26   106496       47             0 cron
May 17 10:59:31 lp kernel: [ 4682.464072] [    846]     0   846    11309       86   126976       50             0 wpa_supplicant
May 17 10:59:31 lp kernel: [ 4682.464073] [    847]     0   847     1139        0    53248       22             0 acpid
May 17 10:59:31 lp kernel: [ 4682.464074] [    848]     0   848    17646       36   184320      140             0 systemd-logind
May 17 10:59:31 lp kernel: [ 4682.464075] [    850]     0   850    72407       32   204800      202             0 accounts-daemon
May 17 10:59:31 lp kernel: [ 4682.464076] [    857]     0   857     9130       25   122880       64             0 bluetoothd
May 17 10:59:31 lp kernel: [ 4682.464077] [    912]   116   912    11770       11   131072       75             0 avahi-daemon
May 17 10:59:31 lp kernel: [ 4682.464078] [    933]     0   933    74566      194   221184      565             0 polkitd
May 17 10:59:31 lp kernel: [ 4682.464080] [   1024]     0  1024    47246      422   262144     1551             0 unattended-upgr
...
May 17 10:59:31 lp kernel: [ 4682.464175] [   6818]  1000  6818   514707   425754  4173824    87862             0 test

这里的total_vm和rss都以4k为单位。

由编写的程序invoked申请内存,kill了6818号进程占用内存1703016KB。

总结

对一个进程来说,内存的使用受多种因素的限制,有可能在达到内存不足情况之前就已经被回收,又或者收到映射的影响,申请新内存也不一定会触发OOM killer。这种情况很少发生,因为内核对内存分配和回收机制做了很好的优化,我们可以看看内存回收部分,只有迫不得已的时候我们才选择这种方式,因为有其他的机制也能帮助我们来回收,比如睡眠回收,kswapd周期回收,设计omm killer这种机制是有必要的,我们需要在最坏情况下有所处理,和行动。

参考资料:

https://segmentfault.com/a/1190000008268803

https://www.cnblogs.com/xibuhaohao/p/11087922.html

http://evertrain.blogspot.com/2018/04/oom.html

OOM Killer机制相关推荐

  1. Linux内核OOM killer机制

    程序运行了一段时间,有个进程挂掉了,正常情况下进程不会主动挂掉,简单分析后认为可能是运行时某段时间内存占用过大,系统内存不足导致触发了Linux操作系统OOM killer机制,将运行中的进程杀掉了. ...

  2. linux内核killler,Linux 的 OOM Killer 机制分析

    按需分配物理页面 很多情况下,一个进程会申请一块很大的内存,但只是用到其中的一小部分.为了避免内存的浪费,在分配页面时,Linux 采用的是按需分配物理页面的方式.譬如说,某个进程调用malloc() ...

  3. 红帽linux杀进程,weblogic进程无故被kill,redhat oom killer机制

    ./startWebLogic.sh: line 74: 30374 已杀死 ${JAVA_HOME}/bin/java ${JAVA_VM} ${MEM_ARGS} ${JAVA_OPTIONS} ...

  4. linux内核oom,linux OOM killer分析

    基本概念 Linux 内核有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀 ...

  5. (转载)Linux Out-of-Memory(OOM) Killer

    Linux有一个特性:OOM Killer,一个保护机制,用于避免在内存不足的时候不至于出现严重问题,把一些无关的进程优先杀掉,即在内存严重不足时,系统为了继续运转,内核会挑选一个进程,将其杀掉,以释 ...

  6. linux进程莫名其妙被kill,Linux进程突然被杀掉(OOM killer),查看系统日志

    Linux进程被杀掉(OOM killer),查看系统日志 基本概念: Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是 ...

  7. (转载)Linux OOM Killer个人总结

    Linux下面有个特性叫OOM killer(Out Of Memory killer),这个东西会在系统内存耗尽的情况下跳出来,选择性的干掉一些进程以求释放一些内存.典型的情况是:某天机器突然登不上 ...

  8. Android low memory killer 机制

    Android中,进程的生命周期都是由系统控制的.即使用户在界面上关掉一个应用,切换到了别的应用,那个应用的进程依然是存在于内存之中的.这样设计的目的是为了下次启动应用能更加快速.当然,随着系统运行时 ...

  9. ubuntu虚拟机进程被杀死_Linux进程被杀掉(OOM killer),查看系统日志

    基本概念: Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉. ...

  10. linux进程被杀掉日志,Linux进程突然被杀掉(OOM killer),查看系统日志

    Linux进程被杀掉(OOM killer),查看系统日志 基本概念: Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是 ...

最新文章

  1. BCH专属“谷歌地图”凸显BCH魅力
  2. Morph 3D拥有近千名艺术家,欲打造全球最大的VR虚拟化身服装库
  3. zend studio让编译器导入项目为utf-8如何设置
  4. 无声息格式化磁盘的API,VB版
  5. vim特殊符号的意义
  6. JS面向对象的程序设计之继承-继承的实现-借用构造函数
  7. 习题6-5 使用函数验证哥德巴赫猜想 (20 分)
  8. java从数组中删除元素(数组的缩容)
  9. hadoop 之NullWritable与ObjectWritable
  10. php面向对象受保护,php面向对象二之封装,protected ,public,private权限管理
  11. 从零开始学习python编程-从零开始学Python程序设计 PDF 完整影印版
  12. KASLR-内核地址空间布局随机化
  13. 机房火灾自动报警系统常见问题及解决方案
  14. 计算机驱动程序的安装过程,u盘驱动程序安装,教您如何安装u盘驱动程序
  15. linux ap 模式,无线AP是什么,客户端模式(apclient)是什么意思?
  16. oracle官网下载过程,Oracle 11g安装步骤
  17. pytorch manual_seed()
  18. python 大气污染物模型_Python AQI空气污染指数数据分析与机器学习
  19. endNote X9 导入英文文献(谷歌学术、web of science 等)
  20. 微信小程序学习笔记——wxss使view填充整个页面

热门文章

  1. Unity Timeline自定义轨道 DefaultPlayables源码剖析
  2. scrapy--Rule()与LinkExtractor()函数理解
  3. 目标检测算法——YOLOv5/YOLOv7改进之结合CBAM
  4. JEECG常见问题大全
  5. [CSS3] 使用边框和背景(设置元素的背景)
  6. 哈希(散列)函数的一些应用
  7. ecshop模板如何修改详细图解
  8. 80后:结婚太难 ZZ
  9. Gerrit环境与代码Review实战
  10. GD32F130之LVD低压检测