LabHub
学习 学习路径 课程

Linux 故障处理

内存与 OOM 诊断

在 LabHub 中继续学习

目标

准确解读内存指标,区分进程实际占用的内存与仅预留的地址空间,并确认 OOM Killer 根据什么选择目标。

为什么重要

大多数内存问题的误诊,都始于查看了错误的列。应该看 free 的 available 列,而不是 free 列;应该看 top 的 RES(RSS),而不是 VIRT。OOM Killer 的评分也以 RSS 为依据——仅通过 mmap 预留 64GB 的进程甚至不会成为候选对象。此外,容器内的 free 显示的是整台主机,因此真正的上限必须从 cgroup 文件读取。本实验第 7 步之所以连‘文件不存在时要写不存在’都纳入评分,是因为在实际环境中,首先确认该文件是否存在本身就是第一步。

步骤

/root/mem 目录中工作。

  1. /proc/meminfo 读取 MemTotal,只把 kB 数值写入 /root/mem/total_kb.txt
  2. 只把 MemAvailable 的 kB 数值写入 /root/mem/available_kb.txt
  3. 在后台启动一个实际常驻内存(VmRSS)至少为 100MB 的进程,并把 PID 写入 /root/mem/hog_pid.txt。该进程必须在后续步骤中始终存活。
  4. 将该进程的 VmSizeVmRSS 按以下两行写入 /root/mem/vm.txt
    • vsize_kb=<값>
    • rss_kb=<값>
  5. 将该进程的 oom_score_adj 设为 500,然后把设置之后读取的 oom_score 值写入 /root/mem/oom_score.txt
  6. 创建 /root/mem/topmem.sh <N>。把 RSS 最高的 N 个进程按 <rss_kb> <comm> 格式降序输出。必须正好输出 N 行。
  7. 将该容器的 cgroup 内存上限原样写入 /root/mem/limit.txt。如果当前环境无法读取对应文件,就写入 unavailable
  8. 创建 /root/mem/memguard.sh <임계값kB>。如果存在 RSS 超过阈值的进程,逐行输出 over=<PID> 并返回退出码 1;如果没有,则输出一行 ok 并返回退出码 0。没有参数时必须返回非 0。

提示

读取总内存

/proc/meminfo 读取 MemTotal,只把 kB 数值写入 /root/mem/total_kb.txt

这是 /proc/meminfo 的第一项。保留 kB 单位,只写数字。

区分 MemAvailable 与 MemFree

只把 MemAvailable 的 kB 数值写入 /root/mem/available_kb.txt

Linux 会把空闲内存用于页面缓存。有一个单独的值会把可回收容量也计算在内。

真正占用内存

在后台启动一个实际常驻内存(VmRSS)至少为 100MB 的进程,并把 PID 写入 /root/mem/hog_pid.txt。该进程必须在后续步骤中始终存活。

只分配而不访问,RSS 不会增加。必须实际填充内存。可以用 python3 创建 bytearray 后 sleep。

比较 VmSize 与 VmRSS

将该进程的 VmSizeVmRSS 按以下两行写入 /root/mem/vm.txt

/proc//status 中同时包含二者。请确认预留地址空间与实际常驻内存之间的差异。

调整 OOM 分数

将该进程的 oom_score_adj 设为 500,然后把设置之后读取的 oom_score 值写入 /root/mem/oom_score.txt

向 /proc//oom_score_adj 写入值即可。提高该值始终被允许。修改后再读取 oom_score。

列出 RSS 最高的进程

创建 /root/mem/topmem.sh <N>。把 RSS 最高的 N 个进程按 <rss_kb> <comm> 格式降序输出。必须正好输出 N 行。

收集 /proc/*/status 中的 VmRSS 并排序。内核线程没有 VmRSS 项。

找出容器的真实上限

将该容器的 cgroup 内存上限原样写入 /root/mem/limit.txt。如果当前环境无法读取对应文件,就写入 unavailable

free 命令显示的是主机内存。真实上限位于 cgroup 文件中。如果不存在,就明确写成不存在。

内存监控脚本

创建 /root/mem/memguard.sh <임계값kB>。如果存在 RSS 超过阈值的进程,逐行输出 over=<PID> 并返回退出码 1;如果没有,则输出一行 ok 并返回退出码 0。没有参数时必须返回非 0。

通过参数接收阈值并报告超标进程。存在超标时返回退出码 1,否则输出 ok 并返回 0。