linux内核中内存耗尽OOM killer

   

目录

 工程实例

一、内存耗尽可配置的参数如下:

二、杀死进程的计算方法

三、技术原理

oom_kill_process执行过程

四、源码分析 


 工程实例

        当内存严重不足时,页分配器在多次尝试直接回收失败后,就会调用内存耗尽OOM killer,选择杀死进程,释放内存。

        先看一段oom 输出的错误

[ 7981.765805] kthreadd invoked oom-killer: gfp_mask=0x2dc2(GFP_KERNEL|__GFP_HIGHMEM|__GFP_NOWARN|__GFP_ZERO), order=0, oom_score_adj=0
[ 7981.777742] CPU: 3 PID: 2 Comm: kthreadd Tainted: G           O      5.4.0-xilinx-v2020.1 #1
[ 7981.786164] Hardware name: xlnx,zynqmp (DT)
[ 7981.790329] Call trace:
[ 7981.792767]  dump_backtrace+0x0/0x140
[ 7981.796415]  show_stack+0x14/0x20
[ 7981.799717]  dump_stack+0xac/0xd0

...

[ 7982.331629] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=net_process,pid=1275,uid=0
[ 7982.344516] Out of memory: Killed process 1275 (net_process) total-vm:4113316kB, anon-rss:3888320kB, file-rss:36kB, shmem-rss:2052kB, UID:0 pgtables:7852032kB oom_score_adj:0
[ 7982.974247] oom_reaper: reaped process 1275 (net_process), now anon-rss:0kB, file-rss:0kB, shmem-rss:2052kB

在应用程序开发时会出现内存泄露,如上述所示,杀死net_process进程信息。在内核中是如何处理的呢?

一、内存耗尽可配置的参数如下:

/proc/sys/vm/oom_kill_allocating_task

        是否允许杀死正在申请分配内存并触发内存耗尽的进程,避免扫描进程链表选择进程。

        0表示禁止,非零表示允许

/proc/sys/vm/oom_dump_tasks

        是否允许内存耗尽杀死进程的时候,是否打印所有用户进程的内存使用信息,

        0表示禁止,非零表示允许

/proc/sys/vm/panic_on_oom

        是否允许在内存耗尽的时候内核panic,重启系统。

        0:表示禁止内核恐慌,1 表示允许内核恐慌;2强制执行内核恐慌

二、杀死进程的计算方法

        给进程计算分数(badness score),选择分数最高的进程。分数范围0~1000,0 表示不杀死,1000表示总是杀死。

  1. 用户可以在/proc//oom_score_adj 为进程设置分数值范围[-1000,1000]。值越大导致杀死时的分数越高。
  2. 查看进程分数/proc//oom_socre

三、技术原理

        内存耗尽杀手分为全局的内存耗尽杀手内存控制组的内存耗尽杀手

  1. 内存控制组的内存耗尽杀手指内存控制组的内存使用量超过硬限制的时候,内存控制组选择进程杀死。
  2. 全局的内存耗尽杀手是指内存严重不足的时候,从整个系统选择进程杀死。

linux内核中内存耗尽OOM killer_第1张图片

oom_kill_process执行过程

  1. 如果被选中的进程有子进程,那么从所有子进程中选择badness score最高的子进程代替父进程牺牲,试图使丢失的工作量最小化。(在多进程的服务端场景)
  2. 向被选中的进程发送杀死信号SIGKILL

四、源码分析 

bool out_of_memory(struct oom_control *oc)
{
    unsigned long freed = 0;
    if (oom_killer_disabled)
        return false;
    if (!is_memcg_oom(oc)) {
        blocking_notifier_call_chain(&oom_notify_list, 0, &freed);
        if (freed > 0)
            /* Got some memory back in the last second. */
            return true;
    }
    /*
     * If current has a pending SIGKILL or is exiting, then automatically
     * select it.  The goal is to allow it to allocate so that it may
     * quickly exit and free its memory.
     */
    if (task_will_free_mem(current)) {
        mark_oom_victim(current);
        wake_oom_reaper(current);
        return true;
    }
    /*
     * The OOM killer does not compensate for IO-less reclaim.
     * pagefault_out_of_memory lost its gfp context so we have to
     * make sure exclude 0 mask - all other users should have at least
     * ___GFP_DIRECT_RECLAIM to get here. But mem_cgroup_oom() has to
     * invoke the OOM killer even if it is a GFP_NOFS allocation.
     */
    if (oc->gfp_mask && !(oc->gfp_mask & __GFP_FS) && !is_memcg_oom(oc))
        return true;
    /*
     * Check if there were limitations on the allocation (only relevant for
     * NUMA and memcg) that may require different handling.
     */
    oc->constraint = constrained_alloc(oc);
    if (oc->constraint != CONSTRAINT_MEMORY_POLICY)
        oc->nodemask = NULL;
    check_panic_on_oom(oc);
    if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&
        current->mm && !oom_unkillable_task(current) &&
        oom_cpuset_eligible(current, oc) &&
        current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
        get_task_struct(current);
        oc->chosen = current;
        oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)");
        return true;
    }
    select_bad_process(oc);//选择进程
    /* Found nothing?!?! */
    if (!oc->chosen) {//没有选择要杀死的的进程
        dump_header(oc, NULL);
        pr_warn("Out of memory and no killable processes...\n");
        /*
         * If we got here due to an actual allocation at the
         * system level, we cannot survive this and will enter
         * an endless loop in the allocator. Bail out now.
         */
        if (!is_sysrq_oom(oc) && !is_memcg_oom(oc))
            panic("System is deadlocked on memory\n");
    }
    //有选择的杀死的进程
    if (oc->chosen && oc->chosen != (void *)-1UL)
        oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :
                 "Memory cgroup out of memory");
    return !!oc->chosen;
}

参考:
《深入理解linux内核》
《Linux内核深度解析》
《linux内核深度解析 (余华兵) 》

你可能感兴趣的:(linux内核分析,linux内核调试与追踪,linux内核,内核debug)