0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

OOM Killer机制学习

马哥Linux运维 来源:马哥Linux运维 作者:马哥Linux运维 2022-12-19 16:17 次阅读

当系统内存不足以分配时,Linux内核会使用一种OOM Killer(Out-Of-Memory Killer)机制释放内存,该机制通过一系列比较选择出最适合的进程并将其kill掉,从而达到保障系统稳定运行的目的。那么在内核中,OOM Killer具体是怎么运转的呢?

一、触发过程

在申请内存时,必然会调用alloc_page(),在__alloc_pages中有以下调用关系:

17124298-7df8-11ed-8abf-dac502259ad0.png

其中,在__alloc_pages_slowpath中,当反复尝试reclaim和compact后仍不成功,就会调用__alloc_pages_may_oom进行内存释放。

/*
*Ifwefailedtomakeanyprogressreclaiming,thenweare
*runningoutofoptionsandhavetoconsidergoingOOM
*/
if(!did_some_progress){
if(oom_gfp_allowed(gfp_mask)){
if(oom_killer_disabled)
gotonopage;
/*Coredumpscanquicklydepleteallmemoryreserves*/
if((current->flags&PF_DUMPCORE)&&
!(gfp_mask&__GFP_NOFAIL))
gotonopage;
page=__alloc_pages_may_oom(gfp_mask,order,
zonelist,high_zoneidx,
nodemask,preferred_zone,
classzone_idx,migratetype);
......
}

如果定义了oom_killer_disabled,就会直接goto到nopage,不会触发OOM机制(此值默认为0).

二、工作过程(基于Linux-3.18)

当内核检测到内存不足,执行到out_of_memory时,OOM Killer会选择一个进程并把他kill掉:

p = select_bad_process(&points, totalpages, mpol_mask, force_kill);

具体的选择过程在select_bad_process中进行:

/*
*Simpleselectionloop.Wechosetheprocesswiththehighest
*numberof'points'.Returns-1onscanabort.
*
*(notdocbooked,wedon'twantthisoneclutteringupthemanual)
*/
staticstructtask_struct*select_bad_process(unsignedint*ppoints,
unsignedlongtotalpages,constnodemask_t*nodemask,
boolforce_kill)
{
structtask_struct*g,*p;
structtask_struct*chosen=NULL;
unsignedlongchosen_points=0;

rcu_read_lock();
for_each_process_thread(g,p){
unsignedintpoints;

switch(oom_scan_process_thread(p,totalpages,nodemask,
force_kill)){
caseOOM_SCAN_SELECT:
chosen=p;
chosen_points=ULONG_MAX;
/*fallthrough*/
caseOOM_SCAN_CONTINUE:
continue;
caseOOM_SCAN_ABORT:
rcu_read_unlock();
return(structtask_struct*)(-1UL);
caseOOM_SCAN_OK:
break;
};
points=oom_badness(p,NULL,nodemask,totalpages);
if(!points||points< chosen_points)
      continue;
    /* Prefer thread group leaders for display purposes */
    if (points == chosen_points && thread_group_leader(chosen))
      continue;

    chosen = p;
    chosen_points = points;
  }
  if (chosen)
    get_task_struct(chosen);
  rcu_read_unlock();

  *ppoints = chosen_points * 1000 / totalpages;
  return chosen;
}

select_bad_process会选择一个points数值最高的进程并返回。在宏for_each_process_thread循环里,通过switch和oom_scan_process_thread对一些进程做特殊化处理,如一些进程不适合被结束,就跳过本次循环。如果该进程没有特殊状态,oom_scan_process_thread返回OOM_SCAN_OK,继续向下进行判断。这里使用了oom_badness对其points值进行计算。

/**
*oom_badness-heuristicfunctiontodeterminewhichcandidatetasktokill
*@p:taskstructofwhichtaskweshouldcalculate
*@totalpages:totalpresentRAMallowedforpageallocation
*
*Theheuristicfordeterminingwhichtasktokillismadetobeassimpleand
*predictableaspossible.Thegoalistoreturnthehighestvalueforthe
*taskconsumingthemostmemorytoavoidsubsequentoomfailures.
*/
unsignedlongoom_badness(structtask_struct*p,structmem_cgroup*memcg,
constnodemask_t*nodemask,unsignedlongtotalpages)
{
longpoints;
longadj;

if(oom_unkillable_task(p,memcg,nodemask))
return0;

p=find_lock_task_mm(p);
if(!p)
return0;

adj=(long)p->signal->oom_score_adj;
if(adj==OOM_SCORE_ADJ_MIN){
task_unlock(p);
return0;
}

/*
*ThebaselineforthebadnessscoreistheproportionofRAMthateach
*task'srss,pagetableandswapspaceuse.
*/
points=get_mm_rss(p->mm)+atomic_long_read(&p->mm->nr_ptes)+
get_mm_counter(p->mm,MM_SWAPENTS);
task_unlock(p);

/*
*Rootprocessesget3%bonus,justlikethe__vm_enough_memory()
*implementationusedbyLSMs.
*/
if(has_capability_noaudit(p,CAP_SYS_ADMIN))
points-=(points*3)/100;

/*Normalizetooom_score_adjunits*/
adj*=totalpages/1000;
points+=adj;

/*
*Neverreturn0foraneligibletaskregardlessoftherootbonusand
*oom_score_adj(oom_score_adjcan'tbeOOM_SCORE_ADJ_MINhere).
*/
returnpoints>0?points:1;
}

在oom_badness的上半部分,对进程做了一些判断,排除了不可进行kill的进程以及oom_score_adj为OOM_SCORE_ADJ_MIN(-1000)的进程,进行了return 0。接着是进行比重计算,将rss、nr_ptes、swap空间使用量占RAM比重相加。如果是Root进程则去掉3%的比重points -= (points * 3) / 100;。之后对adj进行归一化并与points相加,在返回值计算时,使用了一个三目运算符,即当points大于0时,返回points,否则返回1。这里注释给出的原因是,对于有资格的进程(即可以被OOM Killer掉的进程),是绝不能返回0的。(这里我的理解是,如果points返回0,这个进程可能在之后的比较中就处于劣势,成为漏网之鱼)

17269194-7df8-11ed-8abf-dac502259ad0.png

再回到select_bad_process中看,之后跟的一个if比较就是为了进行取最大值的判断,再之后判断该进程是否为thread_group_leader,若是则continue跳过本次循环,否则该进程就是被chosen的进程。

再回到out_of_memory中,得到p值后,需要对其进行判断:

if(!p){
dump_header(NULL,gfp_mask,order,NULL,mpol_mask);
panic("Outofmemoryandnokillableprocesses...
");
}
if(p!=(void*)-1UL){
oom_kill_process(p,gfp_mask,order,points,totalpages,NULL,
nodemask,"Outofmemory");
killed=1;
}

当p是0时,即没有找到可以kill掉的进程,内核发出一个panic。当p不是0时,即找到了可以kill掉的进程,则通过oom_kill_process将其kill。

在oom_kill_process中有个“有意思”的事是,在kill之前,会先遍历其子进程,重新通过oom_badness计算出一个最适合被kill掉的子进程,该子进程会有限考虑被kill掉,从而避免kill父进程导致的接管子进程的工作开销。并且最终被kill掉的进程的名字叫victim,这个单词的中文含义是牺牲者,有点是为了整个系统的稳定运转而牺牲的意思。在这之后OOM Killer会kill掉和victim使用相同虚拟内存的进程,并通过发送SIGKILL信号将其终止。

1764f574-7df8-11ed-8abf-dac502259ad0.png

三、到底为什么会发生Out Of Memory?

因为物理内存页的分配发生在使用的瞬间而非分配的瞬间。若某个进程申请了200MB内存,但实际上只使用了100MB,未使用到的100MB根本没有分配物理内存页。当进程需要内存时,进程从内核得到的只是虚拟地址的使用权,而不是实际的物理地址,实际的物理内存只有当进程真的去访问新获取的虚拟地址时,产生缺页异常,从而进入分配实际物理地址的过程,之后系统返回产生异常的地址,重新执行内存访问。虚拟内存需要物理内存作为支撑,当分配了太多虚拟内存,导致物理内存不够时,就发生了Out Of Memory。这种允许超额commit的机制就是overcommit。

overcommit即操作系统在应用申请内存空间时不去检查是否超出当前可用量,随意满足申请要求,应用也不管实际是否有足够多的内存可使用,认为我申请了2G,OS肯定就给我2G使用。最后,随着内存越用越多,OS发现内存不够用了,必须要收回一些内存才行,就触发了上述的OOM Killer机制回收内存。

Linux根据参数 vm.overcommit_memory设置overcommit:

0 ——默认值,启发式overcommit,它允许overcommit,但太明显的overcommit会被拒绝,比如malloc一次性申请的内存大小就超过了系统总内存。

1 ——Always overcommit. 允许overcommit,对内存申请来者不拒。

2 ——不允许overcommit,提交给系统的总地址空间大小不允许超过CommitLimit。(CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit)

四、总结

由于物理内存的分配机制,以及overcommit的存在,导致了在物理内存不够时的OOM Killer。OOM Killer机制很有意思,它为了保护整个系统的安全稳定运行,需要找出一个最合适的进程kill掉。这是不得已而为之,内核必须在kill掉进程和系统崩溃之间选择其中一个。内核代码中out_of_memory注释中也体现了这种无奈。> * If we run out of memory, we have the choice between either

killing a random task (bad), letting the system crash (worse)

OR try to be smart about which process to kill. Note that we

don't have to be perfect here, we just have to be good.

在选择合适的进程时,OOM Killer会挑选一个占用内存最大的进程,这也很好理解,毕竟kill掉一个大的可以获得更多的物理内存,并且损失也比较小。如果kill掉多个小的,损失会比较大。Linux内核总是去选择更高效的方法。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1372

    浏览量

    40280
  • Linux
    +关注

    关注

    87

    文章

    11292

    浏览量

    209333
  • 内存
    +关注

    关注

    8

    文章

    3019

    浏览量

    74005

原文标题:OOM Killer机制学习

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    容器JVM内存配置最佳实践

    Killer机制,此时系统会终止内存占用较多的进程以保证系统的正常运行。特别是在容器环境下,不合理的JVM堆参数设置会导致各种异常现象产生,例如应用堆大小还未到达JVM设置的堆阈值或应用的规格限制,就因为OOM导致重启等。
    发表于 06-20 09:45 882次阅读
    容器JVM内存配置最佳实践

    STM32MP157D提示DAC没有进入syspend,导致休眠失败怎么解决?

    sync: 0.023 seconds Freezing user space processes ... (elapsed 0.002 seconds) done. OOM killer
    发表于 05-30 08:16

    ESP8266真的是Arduino Killer

    一大早起来看见篇文章,标题好惊悚[ Meet the Arduino Killer: ESP8266]人家刚要相亲相爱,你们标题party就冒出来写killer这么吓人的文章,真的好么?
    发表于 08-20 06:50

    linux内核oom机制分析

    Linux 内核有个机制OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗
    发表于 11-13 17:01 1307次阅读
    linux内核<b class='flag-5'>oom</b><b class='flag-5'>机制</b>分析

    基于非联合型学习机制学习神经元模型

    针对生物神经细胞所具有的非联合型学习机制,设计了具有非联合型学习机制的新型神经元模型学习神经元。首先,研究了非联合型学习机制中习惯化学习机制
    发表于 11-29 10:52 0次下载
    基于非联合型<b class='flag-5'>学习机制</b>的<b class='flag-5'>学习</b>神经元模型

    如何制作USB Killer

    注意:使用此USB Killer会损坏您的设备,请自行使用风险。
    的头像 发表于 11-15 17:01 7748次阅读

    一个线上服务OOM的问题分享

    大家都知道,如果出现了线上OOM问题,为了不影响用户的正常使用,最快的解决办法就是重启服务。
    的头像 发表于 10-24 10:47 920次阅读

    什么是OOM机制?怎么防止进程因为OOM机制而被杀掉?

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 02-06 11:45 2843次阅读

    细说Linux Out Of Memory机制

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 02-12 09:57 932次阅读

    一图解析K8S OOM和CPU节流

    使用 Kubernetes 时,内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题。
    的头像 发表于 02-15 17:17 1324次阅读

    什么是OOM机制?怎么防止进程因为OOM机制而被杀掉?

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 06-21 08:59 8124次阅读
    什么是<b class='flag-5'>OOM</b><b class='flag-5'>机制</b>?怎么防止进程因为<b class='flag-5'>OOM</b><b class='flag-5'>机制</b>而被杀掉?

    jvm哪些区域会发生oom

    of Memory,OOM),本文将详细介绍 JVM 内容可能发生 OOM 的区域。OOM 是指应用程序在申请分配内存时,没有足够的内存供其使用,导致程序无法正常执行。 堆(Heap)区域: 堆是 JVM 中最大的一块内存区域
    的头像 发表于 12-05 11:51 1403次阅读

    Java oom异常的原因分析

    Java中的OOM(Out of Memory)异常是指当程序在运行过程中无法分配足够的内存空间时抛出的异常。在Java中,内存分为堆内存(Heap)和栈内存(Stack)。堆内存用于存储对象和数
    的头像 发表于 12-05 13:43 776次阅读

    oom异常的原因和解决方法

    一、OOM异常的原因 OOM异常的出现通常是由于以下几个原因造成的: 1.1 内存泄漏 内存泄漏是指资源在使用完毕后没有被正确释放或回收,从而导致内存不断占用的现象。常见的内存泄漏问题包括对象未被
    的头像 发表于 12-05 13:45 6513次阅读

    Java怎么排查oom异常

    Java中的OOM(Out of Memory)异常是指当Java虚拟机的堆内存不足以容纳新的对象时抛出的异常。OOM异常是一种常见的运行时异常,经常出现在长时间运行的Java应用程序或处理大数
    的头像 发表于 12-05 13:47 1236次阅读