当前位置:首页>Linux>Linux内存管理:OOM内存溢出

Linux内存管理:OOM内存溢出

  • 2026-03-26 09:15:48
Linux内存管理:OOM内存溢出
Linux内核为了提高内存的使用效率采用过度分配内存(over-commit memory)的办法,造成物理内存过度紧张进而触发OOM机制来杀死一些进程回收内存。

该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽会把该进程杀掉。

Linux在内存分配路径上会对内存余量做检查,(1)如果检查到内存不足,则触发OOM机制。(2)OOM首先会对系统所有进程(除init和内核线程等特殊进程)进行打分,并选出最bad的进程;然后杀死该进程。(3)同时会触发内核oom_reaper进行内存收割。(4)同时内核还提供了sysfs接口系统OOM行为,以及进程OOM行为。然后借用一个示例来分析OOM时内存状态。

一、关于OOM

内核检测到系统内存不足,在内存分配路径上触发out_of_memory(),然后调用select_bad_process()选择一个'bad'进程oom_kill_process()杀掉,判断和选择一个‘bad'进程的过程由oom_badness()决定。

Linux下每个进程都有自己的OOM权重,在/proc/<pid>/oom_adj里面,范围是-17到+15,取值越高,越容易被杀掉。

下面从几个方面来分析OOM:

  • 触发OOM的条件是什么?

  • 影响OOM行为有哪些参数?

  • OOM流程分析。

  • 一个OOM实例的解析。

  • 如何通过配置参数影响OOM行为?

二、OOM触发路径

在内存分配路径上,当内存不足的时候会触发kswapd、或者内存规整,极端情况会触发OOM,来获取更多内存。

在内存回收失败之后,__alloc_pages_may_oom是OOM的入口,但是主要工作在out_of_memory中进行处理。

由于Linux内存都是以页为单位,所以__alloc_pages_nodemask是必经之处。

alloc_pages  ->_alloc_pages    ->__alloc_pages_nodemask      ->__alloc_pages_slowpath-------------------------此时已经说明内存不够,会触发一些内存回收、内存规整机制,极端情况触发OOM。        ->__alloc_pages_may_oom -----------------------进入OOM的开始,包括一些检查动作。          ->out_of_memory------------------------------OOM的核心            ->select_bad_process-----------------------选择最'bad'进程              ->oom_scan_process_thread              ->oom_badness----------------------------计算当前进程有多'badness'            ->oom_kill_process-------------------------杀死选中的进程

还有一种情况是do_page_fault(),如果产生VM_FAULT_OOM错误,就进入pagefault_out_of_memory()。

asmlinkage voiddo_page_fault(struct pt_regs *regs, unsignedlong write,                              unsigned long mmu_meh){...good_area:...        fault = handle_mm_fault(vma, address, write ? FAULT_FLAG_WRITE : 0);        if (unlikely(fault & VM_FAULT_ERROR)) {                if (fault & VM_FAULT_OOM)-------------------------------------------handle_mm_fault()时产生VM_FAULT_OOM错误,进入out_of_memory处理。                        goto out_of_memory;                else if (fault & VM_FAULT_SIGBUS)                        goto do_sigbus;                else if (fault & VM_FAULT_SIGSEGV)                        goto bad_area;                BUG();        }        if (fault & VM_FAULT_MAJOR)                tsk->maj_flt++;        else                tsk->min_flt++;        up_read(&mm->mmap_sem);        return;...out_of_memory:        pagefault_out_of_memory();        return;...}voidpagefault_out_of_memory(void){    struct oom_control oc = {        .zonelist = NULL,        .nodemask = NULL,        .memcg = NULL,        .gfp_mask = 0,        .order = 0,------------------------------------------------------------------单个页面情况。    };...    out_of_memory(&oc);}

三、影响OOM的内核参数

Linux内核通过系统级sysctl参数进程级proc参数两大维度,控制OOM机制的触发时机、进程选择策略、异常处理行为与现场信息输出。这些参数直接嵌入OOM的核心执行流程,决定了内存紧张时内核的处理逻辑,是OOM机制最核心的可控入口。

3.1 系统级OOM核心行为控制参数

这类参数直接定义OOM触发后的内核执行逻辑,对应代码中out_of_memory()主流程的核心分支判断,是控制OOM宏观行为的核心开关。

3.1.1 vm.panic_on_oom

该参数控制OOM发生时,内核是否直接触发系统panic(崩溃),而非执行进程查杀逻辑,参数位于/proc/sys/vm/panic_on_oom,支持3种取值:

  • 0:默认值,OOM发生时不触发panic,正常执行进程扫描、打分与查杀流程;

  • 1:仅在全局OOM(非memcg、cpuset、mempolicy限制导致的局部OOM)场景下触发panic;

  • 2:无论全局或局部OOM,强制触发系统panic。

该参数的生效逻辑在内核check_panic_on_oom()函数中实现,是OOM流程中最先执行的判断分支:

staticvoidcheck_panic_on_oom(struct oom_control *oc,                   enum oom_constraint constraint){    if (likely(!sysctl_panic_on_oom))-------------panic_on_oom为0,直接退出,不触发panic        return;    if (sysctl_panic_on_oom != 2) {---------------panic_on_oom为1,仅处理全局OOM场景        if (constraint != CONSTRAINT_NONE)            return;    }    if (is_sysrq_oom(oc))-------------------------sysrq手动触发的OOM不触发panic        return;    dump_header(oc, NULL);    panic("Out of memory: %s panic_on_oom is enabled\n",        sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");

3.1.2 vm.oom_kill_allocating_task

该参数控制OOM时是否优先杀死触发内存分配、导致OOM的当前进程,跳过全系统进程扫描与打分流程,参数位于/proc/sys/vm/oom_kill_allocating_task,默认值为0(关闭)。

当设置为非0时,内核在out_of_memory()流程中,会优先检查当前申请内存的进程是否满足可查杀条件(非init进程、非内核线程、oom_score_adj不为-1000),满足则直接选中该进程查杀,无需遍历所有进程,可加快OOM内存回收速度,同时避免误杀关键业务进程。

生效代码对应out_of_memory()中的核心分支:

boolout_of_memory(struct oom_control *oc){...    if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&        current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&        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;    }...}

3.1.3 vm.oom_dump_tasks

该参数控制OOM发生时,是否打印系统所有可查杀进程的内存详情、OOM优先级等信息,参数位于/proc/sys/vm/oom_dump_tasks,默认值为1(开启)。

开启时,OOM现场打印函数dump_header()会调用dump_tasks(),输出全量进程的pid、uid、内存占用、oom_score_adj、进程名等关键信息,是定位OOM问题的核心依据;关闭该参数可减少OOM时的日志输出,避免磁盘IO压力,适合嵌入式等资源受限场景。

生效代码对应dump_header()中的分支:

staticvoiddump_header(struct oom_control *oc, struct task_struct *p){...    if (sysctl_oom_dump_tasks)        dump_tasks(oc->memcg, oc->nodemask);--------------------------输出全量进程内存与OOM信息}

3.2 影响OOM触发时机的内存管理参数

OOM的触发根源是内存分配失败且内存回收无果,这类参数通过控制内核的内存分配策略、空闲内存水位线,直接决定了内核何时判定“内存不足”,进而是否进入OOM流程。

3.2.1 vm.overcommit_memory

该参数是控制Linux内存过度提交(over-commit)策略的核心开关,直接决定了内存分配的准入规则,是OOM是否会被触发的根源性参数,位于/proc/sys/vm/overcommit_memory,支持3种取值:

  • 0:默认值,启发式过度提交。内核会估算系统可用内存,对于明显超出系统承载能力的分配请求直接拒绝,其余场景允许过度提交,是最常见的触发OOM的模式;

  • 1:始终允许过度提交。无论申请多大的内存,分配阶段都不会拒绝,完全依赖OOM机制在物理内存真的耗尽时处理,该模式下OOM触发概率极高;

  • 2:禁止过度提交。严格限制用户空间可分配的内存总量,上限为swap总大小 + 物理内存 * vm.overcommit_ratio/100(或vm.overcommit_kbytes),分配请求超出该上限时直接返回ENOMEM,不会走到内存耗尽的OOM流程。

3.2.2 vm.overcommit_ratio / vm.overcommit_kbytes

这两个参数仅在vm.overcommit_memory=2时生效,用于定义严格过度提交模式下的内存分配上限,二者为二选一关系,设置vm.overcommit_kbytes后vm.overcommit_ratio自动失效:

  • vm.overcommit_ratio:百分比值,默认50,即允许分配的物理内存上限为总物理内存的50%;

  • vm.overcommit_kbytes:绝对值,单位为字节,直接定义允许分配的物理内存上限。

3.2.3 vm.min_free_kbytes

该参数定义了内核保留的最小空闲内存字节数,是内核内存水位线(min/low/high)的计算基准,位于/proc/sys/vm/min_free_kbytes。

内核内存分配的min水位线直接基于该参数计算,当系统空闲内存低于min水位时,内核会拒绝用户态的内存分配请求,触发直接内存回收,回收失败则直接进入OOM流程。调大该值会让内核更早触发内存回收与OOM,避免内存完全耗尽导致系统卡死;调小该值会让系统更晚触发OOM,但可能导致内核无法申请必要的内存而崩溃。

3.2.4 vm.watermark_scale_factor

该参数定义了内存水位线的缩放因子,默认值为10,范围1-1000,位于/proc/sys/vm/watermark_scale_factor。

内核的三个内存水位线关系为:

  • low水位 = min水位 + min水位 * watermark_scale_factor/1000

  • high水位 = min水位 + 2 * min水位 * watermark_scale_factor/1000

当空闲内存低于low水位时,kswapd内核线程被唤醒执行后台内存回收;低于min水位时触发直接回收与OOM。调大该值会拉大水位线间隔,让kswapd更早启动回收内存,降低OOM触发概率;调小该值会让kswapd启动滞后,更容易进入直接回收与OOM流程。

3.3 进程级OOM优先级控制参数

这类参数位于/proc/<pid>/目录下,针对单个进程生效,直接决定了进程在OOM打分环节的最终得分,进而决定了进程被OOM选中查杀的优先级,核心逻辑嵌入oom_badness()打分函数中。

3.3.1 /proc/<pid>/oom_score_adj

该参数是内核原生使用的OOM优先级调整参数,范围为-1000~1000,支持读写,是控制进程OOM优先级的核心接口。

该参数直接参与oom_badness()的最终得分计算,计算公式为:最终得分 = 进程内存占用基础得分 + oom_score_adj * 系统总内存页数 / 1000

  • 取值为-1000(OOM_SCORE_ADJ_MIN):进程完全禁止被OOM查杀,对应oom_badness()中会直接返回0分,不参与进程评选;

  • 取值>0:会增加进程的OOM得分,取值越高,得分越高,越容易被OOM选中查杀;

  • 取值<0:会降低进程的OOM得分,取值越低,得分越低,越不容易被OOM选中查杀。

生效代码对应oom_badness()中的核心计算逻辑:

unsignedlongoom_badness(struct task_struct *p, struct mem_cgroup *memcg,              const nodemask_t *nodemask, unsigned long totalpages){...    adj = (long)p->signal->oom_score_adj;--------------------------------------获取进程oom_score_adj    if (adj == OOM_SCORE_ADJ_MIN ||            test_bit(MMF_OOM_SKIP, &p->mm->flags) ||            in_vfork(p)) {        task_unlock(p);        return 0;--------------------------------------------------------------取值-1000,直接返回0分,不参与评选    }...    adj *= totalpages / 1000;--------------------------------------------------将adj归一化为系统总内存比例    points += adj;-------------------------------------------------------------叠加到进程基础得分中...}

3.3.2 /proc/<pid>/oom_adj

该参数是旧版内核兼容的OOM优先级调整参数,范围为-17~15,支持读写,目前已被oom_score_adj取代,仅为兼容旧程序保留。

内核会自动完成oom_adj与oom_score_adj的换算,换算关系为:oom_score_adj = oom_adj * 1000 / 17,其中oom_adj=-17对应oom_score_adj=-1000(完全禁止被OOM查杀),oom_adj=15对应oom_score_adj=1000(最容易被查杀)。

换算逻辑对应内核oom_adj_read()函数:

staticssize_toom_adj_read(struct file *file, char __user *buf, size_t count,                loff_t *ppos){...    if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX)        oom_adj = OOM_ADJUST_MAX;    else        oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) /              OOM_SCORE_ADJ_MAX;-------------------------------------oom_adj与oom_score_adj的换算逻辑...}

3.3.3 /proc/<pid>/oom_score

该参数是只读接口,返回内核通过oom_badness()计算出的进程最终OOM得分,得分越高,OOM发生时越容易被选中查杀。

该值是动态计算的,会随着进程的内存占用、oom_score_adj的调整实时变化,是判断进程OOM优先级的直接依据。计算逻辑对应内核proc_oom_score()函数:

staticintproc_oom_score(struct seq_file *m, struct pid_namespace *ns,              struct pid *pid, struct task_struct *task){    unsigned long totalpages = totalram_pages + total_swap_pages;    unsigned long points = 0;    points = oom_badness(task, NULLNULL, totalpages) *                    1000 / totalpages;---------------------基于oom_badness()计算归一化后的最终得分    seq_printf(m, "%lu\n", points);    return 0;}

四、OOM代码分析

4.1 OOM数据结构

OOM的核心数据结构是struct oom_control,在include/linux/oom.h中。

struct oom_control {    /* Used to determine cpuset */    struct zonelist *zonelist;    /* Used to determine mempolicy */    nodemask_t *nodemask;    /* Memory cgroup in which oom is invoked, or NULL for global oom */    struct mem_cgroup *memcg;    /* Used to determine cpuset and node locality requirement */    const gfp_t gfp_mask;----------------------------------发生异常时页面分配掩码。    /*     * order == -1 means the oom kill is required by sysrq, otherwise only     * for display purposes.     */    const int order;---------------------------------------发生异常时申请页面order大小。    /* Used by oom implementation, do not set */    unsigned long totalpages;    struct task_struct *chosen;----------------------------OOM选中的当前进程结构。    unsigned long chosen_points;---------------------------OOM对进程评分的最高分。};

4.2 OOM触发路径

__alloc_pages_may_oom是内存分配路径上的OOM入口,在进入OOM之前还会检查一些特殊情况。

static inline struct page *__alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,    const struct alloc_context *ac, unsigned long *did_some_progress){    struct oom_control oc = {---------------------------------------------------------OOM控制参数。        .zonelist = ac->zonelist,        .nodemask = ac->nodemask,        .memcg = NULL,        .gfp_mask = gfp_mask,        .order = order,    };    struct page *page;    *did_some_progress = 0;    /*     * Acquire the oom lock.  If that fails, somebody else is     * making progress for us.     */    if (!mutex_trylock(&oom_lock)) {        *did_some_progress = 1;        schedule_timeout_uninterruptible(1);        return NULL;    }    page = get_page_from_freelist(gfp_mask | __GFP_HARDWALL, order,                    ALLOC_WMARK_HIGH|ALLOC_CPUSET, ac);-----------------------------再次使用高水位检查一次,是否需要启动OOM流程。    if (page)        goto out;    if (!(gfp_mask & __GFP_NOFAIL)) {----------------------------------------------__GFP_NOFAIL是不允许内存申请失败的情况,下面都是允许失败的处理。        /* Coredumps can quickly deplete all memory reserves */        if (current->flags & PF_DUMPCORE)            goto out;        /* The OOM killer will not help higher order allocs */        if (order > PAGE_ALLOC_COSTLY_ORDER)---------------------------------------order超过3的申请失败,不会启动OOM回收。            goto out;        /* The OOM killer does not needlessly kill tasks for lowmem */        if (ac->high_zoneidx < ZONE_NORMAL)            goto out;        if (pm_suspended_storage())            goto out;        /* The OOM killer may not free memory on a specific node */        if (gfp_mask & __GFP_THISNODE)            goto out;    }    /* Exhausted what can be done so it's blamo time */    if (out_of_memory(&oc) || WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL)) {-------------经过上面各种情况,任然需要进行OOM处理。调用out_of_memory()。        *did_some_progress = 1;        if (gfp_mask & __GFP_NOFAIL) {            page = get_page_from_freelist(gfp_mask, order,                    ALLOC_NO_WATERMARKS|ALLOC_CPUSET, ac);-------------------------对于__GFP_NOFAIL的分配情况,降低分配条件从ALLOC_WMARK_HIGH|ALLOC_CPUSET降低到ALLOC_NO_WATERMARKS|ALLOC_CPUSET。            /*             * fallback to ignore cpuset restriction if our nodes             * are depleted             */            if (!page)                page = get_page_from_freelist(gfp_mask, order,                    ALLOC_NO_WATERMARKS, ac);--------------------------------------如果还是分配失败,再次降低分配标准,从ALLOC_NO_WATERMARKS|ALLOC_CPUSET降低到ALLOC_NO_WATERMARKS。真的是为了成功,节操越来越低啊。        }    }out:    mutex_unlock(&oom_lock);    return page;}

4.3 OOM处理:对进程打分以及杀死最高评分进程

out_of_memory函数是OOM机制的核心,他可以分为两部分。一是调挑选最’bad‘的进程,二是杀死它。

boolout_of_memory(struct oom_control *oc){    unsigned long freed = 0;    enum oom_constraint constraint = CONSTRAINT_NONE;    if (oom_killer_disabled)----------------------------------------------------在freeze_processes会将其置位,即禁止OOM;在thaw_processes会将其清零,即打开OOM。所以,如果在冻结过程,不允许OOM。        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 (task_will_free_mem(current)) {----------------------------------------如果当前进程正因为各种原因将要退出,或者释放内存,将当前进程作为OOM候选者,然后唤醒OOM reaper去收割进而释放内存。        mark_oom_victim(current);        wake_oom_reaper(current);        return true;---------------------当前进程由于自身原因将要推出,OOM则将其标注为TIF_MEMDIE状态;然后唤醒OOM Reaper去处理。不需要经过下面的打分和杀死流程。    }    if (oc->gfp_mask && !(oc->gfp_mask & (__GFP_FS|__GFP_NOFAIL)))-----------如果内存申请掩码包括__GFP_DS或__GFP_NOFAIL,则不进行OOM收割。        return true;    constraint = constrained_alloc(oc);--------------------------------------未定义CONFIG_NUMA返回CONSTRAINT_NONE。    if (constraint != CONSTRAINT_MEMORY_POLICY)        oc->nodemask = NULL;    check_panic_on_oom(oc, constraint);--------------------------------------检查sysctl_panic_on_oom设置,以及是否由sysrq触发,来决定是否触发panic。    if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&--------------如果设置了sysctl_oom_kill_allocating_task,那么当内存耗尽时,会把当前申请内存分配的进程杀掉。        current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&        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?!?! Either we hang forever, or we panic. */    if (!oc->chosen && !is_sysrq_oom(oc) && !is_memcg_oom(oc)) {------------如果没有合适候选进程,并且OOM不是由sysrq触发的,进入panic。        dump_header(oc, NULL);        panic("Out of memory and no killable processes...\n");    }    if (oc->chosen && oc->chosen != (void *)-1UL) {        oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :                 "Memory cgroup out of memory");----------------------------杀死选中的进程。        schedule_timeout_killable(1);    }    return !!oc->chosen;}

select_bad_process()通过oom_evaluate_task()来评估每个进程的得分,对于进程1、内核线程、得分低的进程直接跳过。

staticvoidselect_bad_process(struct oom_control *oc){    if (is_memcg_oom(oc))        mem_cgroup_scan_tasks(oc->memcg, oom_evaluate_task, oc);    else {        struct task_struct *p;        rcu_read_lock();        for_each_process(p)----------------------------------------------遍历系统范围内所有进程线程。            if (oom_evaluate_task(p, oc))                break;        rcu_read_unlock();    }    oc->chosen_points = oc->chosen_points * 1000 / oc->totalpages;}staticintoom_evaluate_task(struct task_struct *task, void *arg){    struct oom_control *oc = arg;    unsigned long points;    if (oom_unkillable_task(task, NULL, oc->nodemask))-------------------进程1以及内核线程等等不能被kill的线程跳过。        goto next;    if (!is_sysrq_oom(oc) && tsk_is_oom_victim(task)) {        if (test_bit(MMF_OOM_SKIP, &task->signal->oom_mm->flags))            goto next;        goto abort;    }    if (oom_task_origin(task)) {        points = ULONG_MAX;        goto select;    }    points = oom_badness(task, NULL, oc->nodemask, oc->totalpages);------对进程task进行打分。    if (!points || points < oc->chosen_points)---------------------------这里保证只取最高分的进程,所以分数最高者被选中。其他情况则直接跳过。        goto next;    /* Prefer thread group leaders for display purposes */    if (points == oc->chosen_points && thread_group_leader(oc->chosen))        goto next;select:    if (oc->chosen)        put_task_struct(oc->chosen);    get_task_struct(task);    oc->chosen = task;--------------------------------------------------更新OOM选中的进程和当前最高分。    oc->chosen_points = points;next:    return 0;abort:    if (oc->chosen)        put_task_struct(oc->chosen);    oc->chosen = (void *)-1UL;    return 1;}

在oom_badness()中计算当前进程的得分,返回选中进程的结构体,以及进程得分ppoints。

oom_badness()是给进程打分的函数,可以说是核心中的核心。最终结果受oom_score_adj和当前进程内存使用量综合影响。

  • oom_score_adj为OOM_SCORE_ADJ_MIN的进程不参加评选。进程的oom_score_adj值在/proc/xxx/oom_score_adj中。

  • mm->flags为MMF_OOM_SKIP的进程不参加评选。

  • 处于vfork()中的进程不参加评选。

  • 进程的得分取决于其消耗的RSS部分内存(文件映射内存MM_FILEPAGES、匿名映射内存MM_ANONPAGES、shmem内存MM_SHMEMPAGES)、匿名交换内存MM_SWAPENTS、PTE页表所占内存、PMD页表所占内存。

  • 具有root权限的进程只取其97%的得分参加评选。

所以进程得分points=process_pages + oom_score_adjtotalpages/1000;如果是root权限的进程points=process_pages0.97 + oom_score_adj*totalpages/1000。

  • 在oom_score_adj都为0(默认值)的情况下,最终得分跟进程自身消耗的内存有关;消耗的内存越大越容易被Kill。

  • oom_score_adj每降低1,可以多获得系统内存资源的1/1000使用量。反之,每增加1,则少获得系统内存资源1/1000使用量。

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);------------找到进程p,并使用task_lock()锁上。    if (!p)        return 0;    /*     * Do not even consider tasks which are explicitly marked oom     * unkillable or have been already oom reaped or the are in     * the middle of vfork     */    adj = (long)p->signal->oom_score_adj;--------------------------------------获取当前进程的oom_score_adj参数。    if (adj == OOM_SCORE_ADJ_MIN ||            test_bit(MMF_OOM_SKIP, &p->mm->flags) ||            in_vfork(p)) {        task_unlock(p);        return 0;--------------------------------------------------------------如果当前进程oom_score_adj为OOM_SCORE_ADJ_MIN的话,就返回0.等于告诉OOM,此进程不参数'bad'评比。    }    /*     * 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) + get_mm_counter(p->mm, MM_SWAPENTS) +        atomic_long_read(&p->mm->nr_ptes) + mm_nr_pmds(p->mm);-----------------可以看出points综合了内存占用情况,包括RSS部分、swap file或者swap device占用内存、以及页表占用内存。    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))------------------------------如果是root用户,增加3%的使用特权。        points -= (points * 3) / 100;    /* Normalize to oom_score_adj units */    adj *= totalpages / 1000;--------------------------------------------------这里可以看出oom_score_adj对最终分数的影响,如果oom_score_adj小于0,则最终points就会变小,进程更加不会被选中。    points += adj;-------------------------------------------------------------将归一化后的adj和points求和,作为当前进程的分数。    /*     * 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;}

oom_kill_process()用于杀死最高分的进程,包括进程下的线程。

staticvoidoom_kill_process(struct oom_control *oc, constchar *message){    struct task_struct *p = oc->chosen;    unsigned int points = oc->chosen_points;    struct task_struct *victim = p;    struct task_struct *child;    struct task_struct *t;    struct mm_struct *mm;    unsigned int victim_points = 0;    staticDEFINE_RATELIMIT_STATE(oom_rs, DEFAULT_RATELIMIT_INTERVAL,                          DEFAULT_RATELIMIT_BURST);    bool can_oom_reap = true;    task_lock(p);    if (task_will_free_mem(p)) {------------------------------------------对于非coredump正处于退出状态的线程,标注TIF_MEMDIE并唤醒reaper线程进行收割,然后退出。        mark_oom_victim(p);        wake_oom_reaper(p);        task_unlock(p);        put_task_struct(p);        return;    }    task_unlock(p);    if (__ratelimit(&oom_rs))        dump_header(oc, p);----------------------------------------------在kill进程之前,将系统栈信息、内存信息、所有进程的内存消耗情况打印。5.    pr_err("%s: Kill process %d (%s) score %u or sacrifice child\n",        message, task_pid_nr(p), p->comm, points);-----------------------输出将要kill掉的进程名、pid、score。    read_lock(&tasklist_lock);    for_each_thread(p, t) {-----------------------------------------------遍历进程下的线程        list_for_each_entry(child, &t->children, sibling) {            unsigned int child_points;            if (process_shares_mm(child, p->mm))--------如果子进程有自己单独的mm内存空间,则可以被选中代提父进程被kill。                continue;            /*             * oom_badness() returns 0 if the thread is unkillable             */            child_points = oom_badness(child,                oc->memcg, oc->nodemask, oc->totalpages);-----------------计算子线程的得分情况            if (child_points > victim_points) {---------------------------将得分最高者计为victim,得分为victim_points。                put_task_struct(victim);                victim = child;-------------------------------------------确保victim是p子进程中得分最高者,代提p受死。                victim_points = child_points;                get_task_struct(victim);            }        }    }    read_unlock(&tasklist_lock);    p = find_lock_task_mm(victim);    if (!p) {        put_task_struct(victim);        return;    } else if (victim != p) {        get_task_struct(p);        put_task_struct(victim);        victim = p;    }    /* Get a reference to safely compare mm after task_unlock(victim) */    mm = victim->mm;    atomic_inc(&mm->mm_count);    do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);--------------发送SIGKILL信号给victim进程。    mark_oom_victim(victim);-----------------------------------------------标注TIF_MEMDIE是因为OOM被杀死。6.  pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",        task_pid_nr(victim), victim->comm, K(victim->mm->total_vm),        K(get_mm_counter(victim->mm, MM_ANONPAGES)),        K(get_mm_counter(victim->mm, MM_FILEPAGES)),        K(get_mm_counter(victim->mm, MM_SHMEMPAGES)));---------------------被kill进程的内存信息。    task_unlock(victim);    rcu_read_lock();    for_each_process(p) {--------------------------------------------------继续处理共享内存的相关线程        if (!process_shares_mm(p, mm))            continue;        if (same_thread_group(p, victim))            continue;        if (is_global_init(p)) {            can_oom_reap = false;            set_bit(MMF_OOM_SKIP, &mm->flags);            pr_info("oom killer %d (%s) has mm pinned by %d (%s)\n",                    task_pid_nr(victim), victim->comm,                    task_pid_nr(p), p->comm);            continue;        }        if (unlikely(p->flags & PF_KTHREAD))-----------------------------内核线程跳过。            continue;        do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true);    }    rcu_read_unlock();    if (can_oom_reap)        wake_oom_reaper(victim);----------------------------------------唤醒OOM Reaper内核线程收割。    mmdrop(mm);---------------------------------------------------------释放mm空间的内存。包括申请的页面、mm结构体等。    put_task_struct(victim);--------------------------------------------释放task_struct占用的内存空间,包括cgroup等等。}

 dump_header()有助于发现OOM现场,找出OOM原因。

static void dump_header(struct oom_control *oc, struct task_struct *p){    nodemask_t *nm = (oc->nodemask) ? oc->nodemask : &cpuset_current_mems_allowed;1. pr_warn("%s invoked oom-killer: gfp_mask=%#x(%pGg), nodemask=%*pbl, order=%d, oom_score_adj=%hd\n",        current->comm, oc->gfp_mask, &oc->gfp_mask,        nodemask_pr_args(nm), oc->order,        current->signal->oom_score_adj);--------------------------------显示在哪个进程中触发了OOM。    if (!IS_ENABLED(CONFIG_COMPACTION) && oc->order)        pr_warn("COMPACTION is disabled!!!\n");    cpuset_print_current_mems_allowed();2.      dump_stack();-------------------------------------------------输出当前现场的栈信息。    if (oc->memcg)        mem_cgroup_print_oom_info(oc->memcg, p);    else3.       show_mem(SHOW_MEM_FILTER_NODES);------------------------------输出整个系统的内存使用情况。    if (sysctl_oom_dump_tasks)4.      dump_tasks(oc->memcg, oc->nodemask);--------------------------显示系统所有进程的内存使用情况。}static void dump_tasks(struct mem_cgroup *memcg, const nodemask_t *nodemask){    struct task_struct *p;    struct task_struct *task;    pr_info("[ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name\n");    rcu_read_lock();    for_each_process(p) {        if (oom_unkillable_task(p, memcg, nodemask))-------------------不可被kill的进程不显示。            continue;        task = find_lock_task_mm(p);-----------------------------------内核线程等没有自己的mm,也无法被kill,所以不显示。        if (!task) {            continue;        }        pr_info("[%5d%5d%5d%8lu%8lu%7ld%7ld%8lu%5hd%s\n",            task->pid, from_kuid(&init_user_ns, task_uid(task)),            task->tgid, task->mm->total_vm, get_mm_rss(task->mm),            atomic_long_read(&task->mm->nr_ptes),            mm_nr_pmds(task->mm),            get_mm_counter(task->mm, MM_SWAPENTS),            task->signal->oom_score_adj, task->comm);-----------------total_vm和rss单位都是页。        task_unlock(task);    }    rcu_read_unlock();}

4.4 OOM Reaper

内核创建oom_reaper内核线程,用于快速回收OOM选中的victim进程所使用的匿名内存、非VM_SHARED内存、swapped out内存等。

4.4.1 创建oom_reaper内核线程

oom_init()创建oom_reaper内核线程后即进入睡眠状态,等待产生OOM之后唤醒,进行收割。

subsys_initcall(oom_init)static struct task_struct *oom_reaper_th;------------oom_reaper内核线程的task_struct指针。staticDECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait);-----OOM Reaper等待队列,oom_reaper线程在此等待,当有OOM产生的时候唤醒等待队列,并从oom_reaper_list中获取待收割进程结构体。static struct task_struct *oom_reaper_list;----------待收割的进程。staticDEFINE_SPINLOCK(oom_reaper_lock);staticint __init oom_init(void){    oom_reaper_th = kthread_run(oom_reaper, NULL"oom_reaper");-----创建oom_reaper内核线程。    if (IS_ERR(oom_reaper_th)) {        pr_err("Unable to start OOM reaper %ld. Continuing regardless\n",                PTR_ERR(oom_reaper_th));        oom_reaper_th = NULL;    }    return 0;}staticintoom_reaper(void *unused){    while (true) {        struct task_struct *tsk = NULL;        wait_event_freezable(oom_reaper_wait, oom_reaper_list != NULL);----oom_reaper在此睡眠,直到有OOM产生并且通过wake_oom_reaper()唤醒。        spin_lock(&oom_reaper_lock);        if (oom_reaper_list != NULL) {            tsk = oom_reaper_list;            oom_reaper_list = tsk->oom_reaper_list;        }        spin_unlock(&oom_reaper_lock);        if (tsk)            oom_reap_task(tsk);-----------------------收割OOM选中的最bad进程,从流程看oom_reaper每次只能收割一个线程。    }    return 0;}

4.4.2 收割进程

#define MAX_OOM_REAP_RETRIES 10staticvoidoom_reap_task(struct task_struct *tsk){    int attempts = 0;    struct mm_struct *mm = tsk->signal->oom_mm;    /* Retry the down_read_trylock(mmap_sem) a few times */    while (attempts++ < MAX_OOM_REAP_RETRIES && !__oom_reap_task_mm(tsk, mm))        schedule_timeout_idle(HZ/10);-------最多重试10次,每次间隔100ms,进行收割。    if (attempts <= MAX_OOM_REAP_RETRIES)        goto done;    pr_info("oom_reaper: unable to reap pid:%d (%s)\n",        task_pid_nr(tsk), tsk->comm);-------收割失败,显示系统hold住的lock信息。    debug_show_all_locks();done:    tsk->oom_reaper_list = NULL;    set_bit(MMF_OOM_SKIP, &mm->flags);    put_task_struct(tsk);}static bool __oom_reap_task_mm(struct task_struct *tsk, struct mm_struct *mm){    struct mmu_gather tlb;    struct vm_area_struct *vma;    struct zap_details details = {.check_swap_entries = true,                      .ignore_dirty = true};    bool ret = true;    mutex_lock(&oom_lock);    if (!down_read_trylock(&mm->mmap_sem)) {        ret = false;        goto unlock_oom;    }    if (mm_has_notifiers(mm)) {        up_read(&mm->mmap_sem);        schedule_timeout_idle(HZ);        goto unlock_oom;    }    if (!mmget_not_zero(mm)) {        up_read(&mm->mmap_sem);        goto unlock_oom;    }    set_bit(MMF_UNSTABLE, &mm->flags);    tlb_gather_mmu(&tlb, mm, 0-1);    for (vma = mm->mmap ; vma; vma = vma->vm_next) {        if (is_vm_hugetlb_page(vma))------跳过hugetlb类型页面。            continue;        if (vma->vm_flags & VM_LOCKED)----跳过VM_LOCKED的vma区域。            continue;        if (vma_is_anonymous(vma) || !(vma->vm_flags & VM_SHARED))            unmap_page_range(&tlb, vma, vma->vm_start, vma->vm_end,                     &details);----------释放匿名、非VM_SHARED类型页面。    }    tlb_finish_mmu(&tlb, 0-1);    pr_info("oom_reaper: reaped process %d (%s), now anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",            task_pid_nr(tsk), tsk->comm,            K(get_mm_counter(mm, MM_ANONPAGES)),            K(get_mm_counter(mm, MM_FILEPAGES)),            K(get_mm_counter(mm, MM_SHMEMPAGES)));----------------显示经过oom_reaper之后的victim进程所占用的内存信息。    up_read(&mm->mmap_sem);    mmput_async(mm);unlock_oom:    mutex_unlock(&oom_lock);    return ret;}

五、OOM实例解析

对照一个OOM实例并解析如下:

1. [19174.926798] copy invoked oom-killer: gfp_mask=0x24200c8(GFP_USER|__GFP_MOVABLE), nodemask=0, order=0, oom_score_adj=0--------参考dump_header(),输出OOM产生现场线程信息,包括分配掩码、OOM信息。2. [19174.937586] CPU: 0 PID: 163 Comm: copy Not tainted 4.9.56 #1---------参考show_stack(),显示栈信息。可以看出OOM现场的调用信息,这里可以看出是CMA分配出发了OOM。[19174.943274Call Trace:[<802f63c2>] dump_stack+0x1e/0x3c[<80132224>] dump_header.isra.6+0x84/0x1a0[<800f2d68>] oom_kill_process+0x23c/0x49c[<800f32fc>] out_of_memory+0xb0/0x3a0[<800f7834>] __alloc_pages_nodemask+0xa84/0xb5c[<801306b8>] alloc_migrate_target+0x34/0x6c[<8012f30c>] migrate_pages+0x108/0xbe4[<800f8a0c>] alloc_contig_range+0x188/0x378[<80130c54>] cma_alloc+0x100/0x220[<80388fe2>] dma_alloc_from_contiguous+0x2e/0x48[<8037bb30>] xxxxx_dma_alloc_coherent+0x48/0xdc[<8037be8c>] mem_zone_ioctl+0xf0/0x198[<80148cec>] do_vfs_ioctl+0x84/0x70c[<80149408>] SyS_ioctl+0x94/0xb8[<8004a246>] csky_systemcall+0x96/0xe03. [19175.001223] Mem-Info:------------参考show_mem(),输出系统内存详细使用情况。这里可以看出free=592很少,active_anon和shmem非常大。[19175.003535] active_anon:99682 inactive_anon:12 isolated_anon:1----------显示当前系统所有node不同类型页面的统计信息,单位是页面。[19175.003535]  active_file:55 inactive_file:75 isolated_file:0[19175.003535]  unevictable:0 dirty:0 writeback:0 unstable:0[19175.003535]  slab_reclaimable:886 slab_unreclaimable:652[19175.003535]  mapped:2 shmem:91862 pagetables:118 bounce:0[19175.003535]  free:592 free_pcp:61 free_cma:0[19175.035394] Node 0 active_anon:398728kB inactive_anon:48kB active_file:220kB inactive_file:300kB unevictable:0kB isolated(anon):4kB isolated(file):0kB mapped:8kB dirty:0kB writeback:0kB shmem:367448kB writeback_tmp:0kB unstable:0kB pages_scanned:2515 all_unreclaimable? yes--------------------显示单个节点的内存统计信息,单位是kB。[19175.059602] Normal free:2368kB min:2444kB low:3052kB high:3660kB active_anon:398728kB inactive_anon:48kB active_file:220kB inactive_file:300kB unevictable:0kB writepending:0kB present:1048572kB managed:734584kB mlocked:0kB slab_reclaimable:3544kB slab_unreclaimable:2608kB kernel_stack:624kB pagetables:472kB bounce:0kB free_pcp:244kB local_pcp:244kB free_cma:0kB--------------------显示某一zone下内存统计信息,单位是kB。[19175.091602] lowmem_reserve[]: 0 0 0[19175.095144] Normal: 21*4kB (MHI) 14*8kB (MHI) 13*16kB (HI) 2*32kB (HI) 4*64kB (MI) 2*128kB (MH) 0*256kB 2*512kB (HI) 1*1024kB (H) 1*2048kB (I) 0*4096kB = 5076kB--------------------显示某一zone的按页面可迁移类型划分页面大小,单位是KB。M表示Movable,U表示Unmovable,E表示rEclaimable,H表示Highatomic,C表示CMA,I表示Isolate。91996 total pagecache pages[19175.112370262143 pages RAM[19175.1152540 pages HighMem/MovableOnly[19175.11910678497 pages reserved[19175.12235090112 pages cma reserved4. [19175.125942] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name-----------参考dump_tasks(),输出系统可被kill的进程内存使用情况。[19175.134514] [  135]     0   135     1042       75       4       0        0         -1000 sshd[19175.143070] [  146]     0   146      597      141       3       0        0             0 autologin[19175.152057] [  147]     0   147      608      152       4       0        0             0 sh[19175.160434] [  161]     0   161   109778     7328     104       0        0             0 xxxxx5. [19175.169068] Out of memory: Kill process 161 (xxxxx) score 39 or sacrifice child---------------因为OOM待kill的进程信息。6. [19175.176439] Killed process 161 (xxxxx) total-vm:439112kB, anon-rss:29304kB, file-rss:8kB, shmem-rss:0kB----------已经发送信号SIGKILL强制退出的进程信息。

通过上面的信息可以知道是哪个进程、OOM现场、哪些内存消耗太多。

这里需要重点查看系统的active_anon和shmem为什么如此大,造成了OOM。

六、对OOM的配置

6.1 系统OOM配置

/proc/sys/vm/oom_dump_tasks:如果设置,则dump_tasks()显示当前系统所有进程内存使用状态。/proc/sys/vm/oom_kill_allocating_task:如果设置了sysctl_oom_kill_allocating_task,在当前进程满足一定条件下,优先选择当前进程作为OOM杀死对象。

boolout_of_memory(struct oom_control *oc){...    if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&        current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&        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;    }...}

/proc/sys/vm/panic_on_oom:在发生OOM的时候是否进行panic。

staticvoidcheck_panic_on_oom(struct oom_control *oc,                   enum oom_constraint constraint){    if (likely(!sysctl_panic_on_oom))-------------panic_on_oom为0表示发生OOM的时候不进行panic()。        return;    if (sysctl_panic_on_oom != 2) {---------------panic_on_oom为非2,是否进入panic还需要根据constraint来决定。        /*         * panic_on_oom == 1 only affects CONSTRAINT_NONE, the kernel         * does not panic for cpuset, mempolicy, or memcg allocation         * failures.         */        if (constraint != CONSTRAINT_NONE)            return;    }    /* Do not panic for oom kills triggered by sysrq */    if (is_sysrq_oom(oc))return;    dump_header(oc, NULL);    panic("Out of memory: %s panic_on_oom is enabled\n",        sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");--panic_on_oom为2,则强制进行panic()。

6.2 进程OOM配置

/proc/xxx/oom_adj:可读写,范围是-17~15。oom_adj是oom_score_adj经过换算能得到。

staticssize_toom_adj_read(struct file *file, char __user *buf, size_t count,                loff_t *ppos){    struct task_struct *task = get_proc_task(file_inode(file));    char buffer[PROC_NUMBUF];    int oom_adj = OOM_ADJUST_MIN;    size_t len;    if (!task)        return -ESRCH;    if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX)        oom_adj = OOM_ADJUST_MAX;    else        oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) /              OOM_SCORE_ADJ_MAX;    put_task_struct(task);    len = snprintf(buffer, sizeof(buffer), "%d\n", oom_adj);    return simple_read_from_buffer(buf, count, ppos, buffer, len);}

/proc/xxx/oom_score:只读。经过oom_badness()计算得出对当前进程消耗页面数目,然后相对于totalpages归一化到1000。

staticintproc_oom_score(struct seq_file *m, struct pid_namespace *ns,              struct pid *pid, struct task_struct *task){    unsigned long totalpages = totalram_pages + total_swap_pages;    unsigned long points = 0;    points = oom_badness(task, NULLNULL, totalpages) *                    1000 / totalpages;    seq_printf(m, "%lu\n", points);    return 0;}

/proc/xxx/oom_score_adj:可读写,范围是-1000~1000。内核中用于计算进程badness points参数。

Linux内存管理系列文章:

Linux内存管理:物理内存初始化
Linux内存管理:页表的映射过程
Linux内存管理:内核内存的布局图
Linux内存管理:分配物理页面
Linux内存管理:slab分配器
Linux内存管理:vmalloc内存分配函数
Linux内存管理:VMA 虚拟内存空间
Linux内存管理:malloc 动态内存分配
Linux内存管理:mmap内存映射

Linux内存管理:缺页中断处理

Linux内存管理:page引用计数

Linux内存管理:反向映射RMAP

Linux内存管理:回收页面page reclaim

Linux内存管理:匿名页面生命周期
Linux内存管理:页面迁移
Linux内存管理:内存规整
Linux内存管理:KSM内存共享机制

Linux内存管理:数据结构和API

原作者:ArnoldLu

原文地址:

https://www.cnblogs.com/arnoldlu/p/8567559.html

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-27 11:46:34 HTTP/2.0 GET : https://f.mffb.com.cn/a/482689.html
  2. 运行时间 : 0.103741s [ 吞吐率:9.64req/s ] 内存消耗:4,769.27kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8f90e3aaa86e3c892d3bee24e342f4cf
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000480s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000605s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.004233s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000515s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000662s ]
  6. SELECT * FROM `set` [ RunTime:0.000546s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000598s ]
  8. SELECT * FROM `article` WHERE `id` = 482689 LIMIT 1 [ RunTime:0.001599s ]
  9. UPDATE `article` SET `lasttime` = 1774583194 WHERE `id` = 482689 [ RunTime:0.002239s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000295s ]
  11. SELECT * FROM `article` WHERE `id` < 482689 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000528s ]
  12. SELECT * FROM `article` WHERE `id` > 482689 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.005175s ]
  13. SELECT * FROM `article` WHERE `id` < 482689 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.004072s ]
  14. SELECT * FROM `article` WHERE `id` < 482689 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003795s ]
  15. SELECT * FROM `article` WHERE `id` < 482689 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002792s ]
0.106387s