3. OOM优先级核心参数:oom_score_adj取值范围与含义、oom_adj与oom_score_adj的映射关系、进程级OOM优先级设置

在Linux内存过载场景下,内核的OOM Killer(Out-Of-Memory Killer)必须选择一个进程终止以释放内存。选择哪个进程并非随机,而是基于一套优先级评分机制。本章深入剖析OOM优先级的核心参数,帮助容器运行时工程师精确控制容器进程的“被杀概率”。

3.1 OOM评分机制概述

内核为每个进程维护一个动态计算的 oom_score(范围 0 ~ 1000),分数越高,越容易被OOM Killer选中。该分数由以下因素综合决定:

  • 内存占用:进程实际使用的物理内存(RSS)越大,分数越高。
  • 进程运行时间:运行时间越短,分数越高(倾向于杀死刚启动的进程)。
  • 子进程数量:子进程越多,分数越高。
  • oom_score_adj:用户可调节的偏移量,直接叠加到最终评分上。

最终评分公式为:

final_oom_score = oom_score + oom_score_adj

其中 oom_score_adj 是用户态唯一可控的OOM优先级参数,也是容器运行时调优的核心抓手。

关键认知: oom_score_adj 不是直接替换 oom_score,而是在内核计算出的基础分数上做加减法。负值降低被杀概率,正值增加被杀概率。

3.2 oom_score_adj 取值范围与含义

该参数定义在 /proc/[pid]/oom_score_adj 文件中,取值范围为 -1000 到 +1000。不同取值段的含义如下表所示:

取值范围 含义 典型场景
-1000 完全禁用OOM Killer对该进程的杀伤。进程的 oom_score 被强制设为0,永远不会被选中。 关键系统进程(init、sshd)、容器Pause进程
-999 ~ -1 降低被杀概率。值越大,保护程度越高。 重要业务进程、监控代理
0 默认值。不调整基础评分,完全由内核动态计算决定。 普通用户进程、默认容器进程
1 ~ 999 增加被杀概率。值越大,越容易被选中。 低优先级批处理任务、缓存进程
1000 强制优先被杀。无论内存占用多少,该进程都会成为首选目标。 内存超限的cgroup内所有进程
实践建议: 在容器场景中,通常将容器主进程的 oom_score_adj 设置为 0 或较小的负值(如 -100 ~ -200),将非关键辅助进程设置为正值(如 200 ~ 500),确保内存紧张时辅助进程优先被回收。

3.3 oom_adj 与 oom_score_adj 的映射关系

在早期的Linux内核(2.6.x)中,OOM优先级通过 /proc/[pid]/oom_adj 控制,取值范围为 -17 ~ +15。为了兼容旧版应用,内核保留了 oom_adj 接口,但内部已映射到 oom_score_adj。映射规则如下:

oom_adj 值 映射后的 oom_score_adj 值 说明
-17 -1000 完全禁用OOM Killer(等同于 oom_score_adj = -1000)
-16 ~ -1 -1000 ~ -1(线性映射) 保护程度逐渐降低
0 0 默认值,不调整
1 ~ 14 1 ~ 933(线性映射) 被杀概率逐渐增加
15 1000 强制优先被杀(等同于 oom_score_adj = 1000)

内核源码中的映射函数(简化逻辑):

int oom_adj_to_score_adj(int oom_adj) {
    if (oom_adj == -17) return -1000;
    if (oom_adj < 0)  return oom_adj * 62;   // 近似线性映射
    if (oom_adj == 0)  return 0;
    if (oom_adj <= 15) return oom_adj * 66 + 10; // 近似线性映射
    return 1000;
}
兼容性注意: 虽然 oom_adj 接口仍然可用,但新代码应优先使用 oom_score_adj,因为其语义更清晰、粒度更细(步长1 vs 步长1~66)。容器运行时(如 runc)在创建容器时默认设置 oom_score_adj,而非 oom_adj。

3.4 进程级OOM优先级设置

在容器运行时环境中,设置OOM优先级主要有三种方式:

3.4.1 通过 /proc 文件系统直接设置

适用于已运行的进程或调试场景:

# 将PID为1234的进程设置为高保护(不易被杀)
echo -500 > /proc/1234/oom_score_adj

# 将PID为5678的进程设置为优先被杀
echo 500 > /proc/5678/oom_score_adj

# 验证设置
cat /proc/1234/oom_score_adj
cat /proc/5678/oom_score_adj

3.4.2 通过 runc 容器运行时设置

在容器创建时通过 OCI 运行时规范中的 process.oomScoreAdj 字段指定:

{
  "ociVersion": "1.0.2",
  "process": {
    "oomScoreAdj": -200,
    "args": ["/bin/myapp"],
    "env": ["PATH=/usr/local/bin"]
  },
  "linux": {
    "resources": {
      "memory": {
        "limit": 536870912,
        "reservation": 268435456
      }
    }
  }
}

runc 在启动容器进程前,会读取该字段并写入 /proc/self/oom_score_adj,确保容器主进程从启动时就带有正确的OOM优先级。

3.4.3 通过 containerd / crictl 设置

在 containerd 或 Kubernetes 环境中,可以通过 CRI 接口的 LinuxContainerConfig.Resources.OomScoreAdj 字段设置:

# 使用 crictl 创建容器时指定
crictl run \
  --oom-score-adj=-300 \
  --memory=512M \
  my-container

在 Kubernetes Pod 的 YAML 中,可以通过 securityContext 设置:

apiVersion: v1
kind: Pod
metadata:
  name: oom-demo
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      runAsUser: 1000
      procMount: Default
      # 注意:Kubernetes 原生不支持直接设置 oom_score_adj
      # 需要通过 Pod 的 QoS 等级间接控制
    resources:
      limits:
        memory: "512Mi"
      requests:
        memory: "256Mi"
Kubernetes 中的间接控制: Kubelet 会根据 Pod 的 QoS 等级自动设置 oom_score_adj:Guaranteed Pod 设置为 -998,Burstable Pod 设置为 0 ~ 999(基于内存申请比例),BestEffort Pod 设置为 1000。这是容器运行时调优的重要背景知识。

3.5 OOM优先级与cgroup内存限制的协同

当进程位于一个设置了 memory.limit_in_bytes 的cgroup中时,OOM Killer的行为会发生变化:

  • cgroup内OOM:当cgroup内存使用达到限制时,内核只在该cgroup内选择进程杀死,不会影响其他cgroup。
  • 系统级OOM:当整个系统内存不足时,内核在所有进程中根据 oom_score + oom_score_adj 选择。
  • 优先级叠加:cgroup内的OOM Killer同样会考虑 oom_score_adj,因此即使在cgroup内,设置合理的优先级也能保护关键进程。

下图展示了OOM优先级参数在进程、cgroup和系统三个层面的作用范围:

OOM优先级参数作用范围示意图 系统全局(System-wide) cgroup 内存限制组 进程A(关键业务) oom_score_adj = -500 进程B(辅助任务) oom_score_adj = 300 进程C(默认) oom_score_adj = 0 进程D(缓存) oom_score_adj = 700 cgroup内OOM:优先杀死进程D → 进程B → 进程C → 进程A(受保护)

3.6 容器运行时调优最佳实践

基于以上参数分析,总结容器运行时OOM优先级设置的推荐策略:

容器类型 推荐 oom_score_adj 理由
Pause容器(sandbox) -1000 完全禁用OOM Killer,确保网络命名空间不丢失
关键业务容器 -200 ~ -100 适度保护,避免被非关键进程抢占资源
普通业务容器 0 默认值,公平竞争
Sidecar代理容器 100 ~ 300 优先被杀,保护主业务进程
批处理/日志容器 500 ~ 800 高优先级被杀,不影响在线服务
BestEffort QoS Pod 1000 Kubernetes自动设置,系统内存不足时首选回收
调优检查清单:
✅ 确认容器主进程的 oom_score_adj 不为 1000(除非明确需要优先被杀)
✅ 确认 Pause 容器始终为 -1000
✅ 避免在同一 cgroup 内将多个进程都设为 -1000(会导致OOM Killer无目标)
✅ 结合 cgroup 内存限制使用,避免系统级OOM影响其他租户