oom-killer, 杀掉进程的凶手

今天发现进程一直被杀掉,几经排查,最后确认是被oom-killer杀掉了。

在内核检测到系统内存不足后,会触发oom-killer,挑选最占用内存的进程杀掉。

Linux 分配内存策略

Linux内核根据应用程序的要求来分配内存,由于进程实际上并不会将分配的内存全部使用,所以,为了提高性能,内核采用了一种过度分配内存(over-commit-memory)的策略,来间接利用进程的空闲内存,提高内存的使用效率。一般来说,这没问题。但如果大多数进程都耗光自己的内存,就有麻烦了。因此此时,所有应用程序的内存之和大于物理内存。所以,必须杀掉一部分进程,一般来说,是选内存占用最大的进程杀掉。

挑选原理

挑选的过程由linux/mm/oom_kill.c里的 oom_badness() 函数决定,挑选的算法很直接:是那个最占用内存的进程。
/**
 * oom_badness - heuristic function to determine which candidate task to kill
 * @p: task struct of which task we should calculate
 * @totalpages: total present RAM allowed for page allocation
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */
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);
    if (!p)
        return 0;

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

    /*
     * 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) + p->mm->nr_ptes +
         get_mm_counter(p->mm, MM_SWAPENTS);
    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))
        adj -= 30;

    /* Normalize to oom_score_adj units */
    adj *= totalpages / 1000;
    points += adj;

    /*
     * 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_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,分数越低越不会被杀掉。
这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。
我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来使得进程不容易被 OOM killer 选中杀掉。比如,如果不想 test进程被轻易杀掉的话可以找到 test运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):

# ps aux | grep test
test    2334  1.6  2.1 623800 4876 ?        Ssl  09:52   0:00 /usr/sbin/test

# cat /proc/2334/oom_score_adj
0
# echo -15 > /proc/2334/oom_score_adj

当然,也可以完全关闭 OOM killer,但线上生产环境最好不要这么做。

转载于:https://www.cnblogs.com/shenlinken/p/10987895.html

<think>嗯,用户遇到了一个系统提示:“Monitor invoked oom-killer: gfp_mask=0x7080c0(GFP_KERNEL_ACCOUNT|__GFP_ZERO), nodemask=(null), order=1, oom_score_adj=0”,需要解释这个错误信息。首先,我要理解这个日志的含义。 首先,oom-killer是Linux内核在内存不足时触发的机制,用于终止进程以释放内存。用户看到的这条信息是系统在内存严重不足时,oom-killer被调用的记录。接下来需要拆解各个参数的意义。 gfp_mask=0x7080c0,这部分是内存分配标志的组合。GFP_KERNEL_ACCOUNT表示内核为某个进程分配内存并进行统计,可能用于cgroup等记账机制。__GFP_ZERO表示分配的内存会被初始化为零。组合起来,这次内存请求可能需要内核账户统计,并且需要清零的内存区域。 nodemask=(null)说明没有指定特定的NUMA节点,可以在所有节点分配内存。order=1表示请求分配2^1=2个连续的内存页,即8KB(假设页大小为4KB)。oom_score_adj=0说明触发oom-killer进程的调整值为0,通常这个值越低,进程越不容易被杀死,但这里可能因为系统整体内存不足,即使调整值为0的进程也可能被终止。 接下来需要分析可能的原因。系统物理内存不足,或者存在内存泄漏导致内存耗尽。另外,虽然order=1看起来不大,但连续内存分配失败可能也是问题。还有cgroup的内存限制可能导致即使系统有空闲内存,特定cgroup中的进程也无法分配。 解决方案方面,需要检查内存使用情况,可以用free -h或top命令。检查内核日志dmesg获取更多OOM相关信息,找到被终止的进程。调整oom_score_adj可能预防问题,但需谨慎。增加系统内存或优化应用程序的内存使用也是根本方法。检查cgroup配置,确保没有不合理的内存限制。 用户可能需要具体的命令和步骤,比如如何查看内存,如何分析日志,如何调整参数。需要确保回答清晰,步骤明确,帮助用户逐步排查问题。同时提醒用户注意数据备份,避免在OOM发生时数据丢失。</think>这个日志信息是 Linux 系统在内存耗尽时触发的 `oom-killer` 机制的记录。以下是逐层解析和应对建议: --- ### 一、关键信息拆解 1. **`Monitor invoked omm-killer`** - 表示系统检测到内存不足(Out Of Memory),触发内核的 `oom-killer` 机制,该机制会强制终止某些进程以释放内存。 2. **`gfp_mask=0x7080c0(GFP_KERNEL_ACCOUNT|__GFP_ZERO)`** - `gfp_mask` 是内存分配的标志位组合: - `GFP_KERNEL_ACCOUNT`: 内核为进程分配内存时需进行会计统计(常用于 cgroup 内存控制)。 - `__GFP_ZERO`: 分配的内存需初始化为零值。 - 此标志组合表示内核正在尝试为某个进程分配需记账且清零的内存区域。 3. **`nodemask=(null)`** - 未指定 NUMA 节点(即允许在所有内存节点分配内存)。 4. **`order=1`** - 请求分配 $2^{\text{order}}$ 个连续物理页(通常页大小为 4KB,即申请 $2^1 \times 4\text{KB} = 8\text{KB}$ 连续内存)。 5. **`oom_score_adj=0`** - 触发 OOM进程的优先级调整值为 0(范围 -1000~1000,值越低越不易被终止)。 --- ### 二、可能原因 1. **系统物理内存不足** - 应用程序或内核模块消耗过多内存。 - 内存泄漏(如内核驱动或用户态进程未释放内存)。 2. **连续内存分配失败** - 尽管 `order=1` 请求的内存较小,但内核无法找到足够的连续物理页。 3. **cgroup 内存限制** -进程cgroup 限制,即使系统整体内存充足,进程也可能因 cgroup 配额耗尽触发 OOM--- ### 三、排查与解决步骤 #### 1. 检查内存使用情况 ```bash # 查看整体内存状态 free -h # 监控进程内存占用 top -o %MEM ``` #### 2. 分析 OOM 日志 ```bash # 查看完整内核日志(关注 OOM 前后的记录) dmesg | grep -i "oom-killer" dmesg | grep -i "killed process" ``` #### 3. 定位问题进程 - 日志中会记录被终止的进程名和 PID,例如: `Killed process 1234 (your_process)` #### 4. 调整 OOM 优先级(谨慎操作) ```bash # 降低某进程被终止的概率(值越小越安全) echo -100 > /proc/<PID>/oom_score_adj ``` #### 5. 优化内存使用 - 升级物理内存。 - 优化应用程序代码(减少内存泄漏,限制缓存大小)。 - 调整内核参数(如 `vm.overcommit_memory`)。 #### 6. 检查 cgroup 限制 ```bash # 若使用容器(如 Docker),检查内存限制 docker inspect <container_id> | grep -i "memory" ``` --- ### 四、典型场景示例 - **场景 1**:数据库服务(如 MySQL)因未配置内存上限,导致占用所有内存。 **解决**:在配置文件中限制 `innodb_buffer_pool_size`。 - **场景 2**:内核驱动存在内存泄漏。 **解决**:更新驱动或通过 `vmstat -s` 监控内核内存使用。 --- 若需进一步分析,请提供完整的 `dmesg` 日志或应用程序内存配置细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值