题干与适用场景
一台 16 个逻辑 CPU 的 Linux 服务器响应变慢,uptime 显示 Load Average 为 48 / 36 / 18,但 mpstat 显示整体 CPU idle 仍有 72%。请判断这是否代表 CPU 过载,并说明如何找到真正等待的资源。
回答需要解释 Linux 负载计量中的可运行任务与不可中断睡眠任务,正确解读 1、5、15 分钟三个值,再用任务状态、Pressure Stall Information(PSI)和子系统指标区分以下情况:
- CPU 运行队列确实拥堵;
- 本地存储、网络文件系统、驱动或其他内核等待使任务长期处于 D 状态;
- 内存回收或换页间接造成等待;
- 主机级指标与容器或服务级指标范围不同。
这是一道适合 SRE、DevOps、基础设施、系统软件和后端岗位的 Linux 诊断题。16、48、36、18 和 72% 都是虚构的练习数据,不是通用告警阈值。题目假设指标来自同一台主机、同一时间窗口;若数据来自不同采集器或不同 cgroup,必须先对齐口径。
面试官考察点
第一,看候选人是否知道 Load Average 不是 CPU 使用率。Linux 的全局负载是可运行任务与不可中断睡眠任务数量的指数衰减平均。只有前一部分直接表示 CPU 需求,后一部分可能在 CPU 空闲时仍把负载推高。
第二,看能否准确解释三个数字。它们不是最近 1、5、15 分钟样本的简单算术平均,而是具有相应时间常数的指数衰减值。48 > 36 > 18 支持“近期负载在上升”这个方向性判断,但不能反推出某一分钟的精确队列长度,也不能单独证明根因。
第三,看诊断是否从“总数”走向“组成和等待位置”。强回答会比较 vmstat 的 r、b,统计 R 和 D 状态的线程,再从 wchan、内核栈、PSI 和存储或网络文件系统指标把等待定位到具体资源。只说“运行 top、iostat”没有说明证据如何改变结论。
第四,看候选人是否会处理反例。D 状态常被工具称为 disk sleep,但不可中断等待不只可能来自本地磁盘;NFS、文件系统、设备驱动和部分内核资源等待也可能出现。%iowait 是 CPU 时间口径,低 iowait 不能排除任务已经阻塞。
最后看是否有安全的处置与验证闭环。扩容 CPU、重启、杀进程或升级磁盘各自只适用于特定证据。修复后需要证明 R/D 任务、PSI、子系统延迟和业务尾延迟一起恢复,不能只等 Load Average 缓慢下降。
回答前需要澄清的问题
- 指标范围一致吗? Load Average 通常是主机全局值,而应用 CPU 可能只统计一个容器或进程。若范围不同,先查看主机、cgroup 和服务三层指标,否则“高负载低 CPU”可能只是观测口径错位。
- CPU idle 是整体平均还是每核数据? 单个核心、NUMA 节点或受 CPU affinity 限制的任务可能已经排队,而其他核心空闲。需要看
mpstat -P ALL、亲和性和 cgroup CPU 配额。 - 变慢的是哪些请求,何时开始? 对齐发布、流量、备份、存储抖动、挂载变化和内存回收时间,才能把主机信号与用户影响关联起来。
- R 状态还是 D 状态占主导? R 多且 CPU 忙支持 CPU 竞争;D 多且 CPU 空闲支持不可中断等待。两者可以同时存在,必须按线程和工作负载分组。
- 底层存储是什么? 本地 NVMe、云盘、块设备、FUSE、NFS 和数据库远程调用使用不同证据。
iostat看不到普通网络 RPC,NFS 还需要挂载和网络文件系统指标。 - 是否有内存或虚拟化压力? 换入换出、直接回收、major fault、宿主机 steal time 和 cgroup 限制会改变诊断优先级。
- 能否读取任务栈?
/proc/12345/stack、部分wchan和进程 I/O 数据可能受权限限制。生产排查要使用最小必要权限,并先做低开销采样。
30 秒回答框架
“Load Average 不是 CPU 百分比。Linux 统计可运行任务和不可中断睡眠任务的数量,并计算 1、5、15 分钟时间常数的指数衰减平均。16 个逻辑 CPU 上负载 48 只能说明有很多活跃或不可中断等待的任务;CPU 仍有 72% idle,说明不能直接断言 CPU 饱和。
我会先确认指标范围和每核利用率,再用 vmstat 看可运行队列 r 与阻塞任务 b,统计线程的 R/D 状态。R 多、CPU PSI 高且 CPU 忙,才像 CPU 排队;D 多、I/O PSI 高时,再用 wchan、任务栈、pidstat、iostat、NFS 与内核日志定位等待资源。若 memory PSI、换页和 major fault 同时升高,就转查内存压力。修复后同时验证业务 p99、R/D 数量、PSI 和子系统延迟,而不是只等负载数字下降。”
分步骤深入解答
第一步:先把 Load Average 拆成两个来源
Linux 内核计算全局负载时,核心输入可以概括为:
nrrunning + nruninterruptible
nrrunning 包括正在 CPU 上执行和已可运行、等待调度的任务;nruninterruptible 表示不可中断睡眠任务。/proc/loadavg 的前三项是这组活跃任务数量的 1、5、15 分钟负载值。第四项形如 7/1280,前一个数字是当前可运行的调度实体数量,后一个是当前存在的调度实体总数;第五项是最近创建进程的 PID。
因此,“负载 48”不是“CPU 使用率 300%”,也不是某一时刻恰好有 48 个进程排队。它是经过平滑的任务数量信号。将负载除以逻辑 CPU 数,只能作为纯 CPU 型负载的粗略饱和提示;D 状态任务较多时,这个比值不再表示 CPU 队列程度。
内核按固定周期更新指数衰减平均。新样本权重更高,旧样本逐渐衰减,所以 48 / 36 / 18 表示近期活跃任务比更早时期多。即使根因已经消失,15 分钟值也会滞后;恢复判断应优先看实时任务状态、PSI 和业务指标。
第二步:确认范围、每核利用率和实时队列
先做一组低开销观测,并保留时间戳:
nproc
uptime
cat /proc/loadavg
vmstat 1 10
mpstat -P ALL 1 10vmstat 第一行通常是自启动以来的平均,诊断当前问题时看后续采样。关键字段是:
r:可运行任务数量;持续明显高于可用 CPU 并伴随 CPU busy,支持 CPU 排队;b:处于不可中断睡眠的任务数量;持续升高时优先调查等待资源;us、sy、id、wa、st:区分用户态、内核态、空闲、I/O wait 和虚拟机被抢占时间;si、so:换入换出活动,需与 memory PSI 和 fault 一起判断。
整体 72% idle 仍可能掩盖单核热点。若一个串行线程被固定在 CPU 3,CPU 3 可以接近饱和,而其他 15 个 CPU 大多空闲。此时查看每核利用率、任务亲和性、容器 CPU 配额与 throttling,比盲目增加整机 CPU 更有区分力。
第三步:按线程统计 R 与 D,并找到等待点
进程内部多个线程都会参与负载计量,所以要使用线程视图:
ps -eLo state,pid,tid,ppid,wchan:32,comm --sort=state
ps -eLo state= | sort | uniq -cR 表示正在运行或可运行。D 表示不可中断睡眠;信号通常要等任务离开该等待后才能生效,所以反复执行 kill -9 既不能立即释放等待的内核资源,也会破坏现场。
对集中出现的 D 状态线程,按命令、父进程、cgroup 与 wchan 分组。wchan 显示线程在内核中睡眠的位置,能把范围缩小到块 I/O、NFS、文件系统或驱动路径。权限允许时,再读取代表性线程的内核栈:
cat /proc/12345/stack
cat /proc/12345/wchan单个函数名仍不是根因。相同 wchan 大量聚集、同一时间对应子系统延迟和业务变慢,才形成较强证据。D 状态也不能自动归因于应用发起了大量 I/O;设备故障、远端挂载失联或内核路径卡住都可能让少量请求积累成大量等待线程。
第四步:用 PSI 判断哪类资源让任务停止推进
PSI 直接量化 CPU、内存和 I/O 争用造成的停顿时间:
for resource in cpu io memory; do
echo "[$resource]"
cat /proc/pressure/"$resource"
donesome 表示至少有部分任务因该资源停顿的时间占比,full 表示所有非 idle 任务同时停顿的时间占比;系统级 CPU full 保留为零,不应用它判断 CPU 饱和。avg10、avg60、avg300 分别给出近 10、60、300 秒趋势,total 是累计停顿微秒数。
PSI 与 Load Average 回答不同问题:Load Average 告诉你有多少活跃或不可中断等待任务,PSI 告诉你工作负载有多少时间无法推进。两者结合能避免“负载高就扩 CPU”的误判。使用 cgroup v2 时,还应读取目标 cgroup 的 cpu.pressure、io.pressure 和 memory.pressure,把服务自身压力与主机噪声分开。
可以用以下矩阵组织证据:
| 观测组合 | 首要假设 | 下一步证据 | |---|---|---| | CPU busy 高、r 高、CPU PSI 高 | CPU 运行队列竞争 | 每核热点、CPU profile、亲和性与配额 | | CPU idle 高、b/D 多、I/O PSI 高 | I/O 或内核不可中断等待 | wchan/栈、设备或 NFS 延迟、内核日志 | | memory PSI 高、si/so 或 major fault 增加 | 内存回收或换页压力 | cgroup memory、工作集、回收与存储读写 | | 主机负载高、目标 cgroup PSI 低 | 范围错位或其他租户影响 | 按 cgroup/进程归属拆分任务与资源 |
这张表是调查起点,不是自动根因判定器。CPU、内存和 I/O 压力可能形成反馈环,例如内存回收引发文件读写,再把线程推入 D 状态。
第五步:进入对应子系统,不把所有等待都叫“磁盘慢”
若证据指向块设备,观察设备吞吐、延迟、队列与错误:
pidstat -d -p ALL 1 10
iostat -xz 1 10
dmesg -T | tail -200pidstat 帮助定位发起 I/O 的进程,iostat 报告设备与分区负载。任何字段都需要结合设备类型和基线解释;例如设备 %util 不能跨所有存储架构使用同一个饱和阈值。内核日志中的超时、reset、文件系统错误或设备脱离,比单独的高队列更接近故障原因。
若 wchan 或挂载点指向 NFS、FUSE 或网络存储,要检查挂载状态、nfsiostat、客户端重传、网络延迟和服务端健康。普通 HTTP 或数据库 RPC 等待通常处于可中断睡眠,不一定进入 Linux Load Average;因此还需把应用追踪、连接池和依赖延迟放进同一时间轴。
若 memory PSI、换页或 major fault 上升,继续检查 cgroup 内存事件、匿名与文件工作集、直接回收和 swap 设备。只增加磁盘性能可能缓解换页代价,却没有修正工作集超过内存预算的原因。
第六步:基于证据止损,并验证因果关系
处置要对应已确认的等待资源:
- CPU 队列竞争:限制入口、降低并发或批处理优先级,解除错误亲和性,必要时按已测容量扩 CPU;
- 存储或 NFS 故障:停止继续放大 I/O,切换健康副本或挂载路径,修复设备、网络或服务端;
- 内存压力:限制工作集和并发,停止造成抖动的任务,在兼容的容量边界内扩内存;
- 范围错位:先隔离噪声工作负载或修正 cgroup 配额,再评估目标服务是否需要扩容。
重启可能清除积累任务,却也可能丢失关键栈和日志;若底层资源仍不可用,新进程会再次阻塞。采取动作前保存代表性 PID、线程状态、wchan、PSI 和子系统快照,并写下预期变化。
验收至少包含:业务 p95/p99 与错误率恢复;R/D 数量回到基线;对应 PSI 趋势下降;设备、NFS、内存或 CPU 指标恢复;队列与积压被安全处理。Load Average 的指数衰减会产生滞后,所以它是恢复证据之一,不是关闭事故的唯一门槛。
高质量示范回答
“我不会因为 16 个 CPU 上负载 48 就直接说 CPU 过载。Linux Load Average 平滑统计正在运行或等待 CPU 的任务,以及不可中断睡眠任务。CPU idle 还有 72%,说明负载很可能包含大量 D 状态等待,或者整体平均掩盖了单核、配额或指标范围问题。
48 / 36 / 18 是三个指数衰减平均,方向上说明近期活跃任务增加,不是三个简单时间窗口的算术平均,也不能告诉我根因。我先确认 Load、CPU 和业务延迟来自同一主机与时间窗,再看 mpstat -P ALL,排除单核热点和 steal time。
下一步用 vmstat 1 看 r 与 b。如果 r 持续高、CPU busy 和 CPU PSI 也高,我会按 CPU profile、亲和性和 cgroup throttling 排查。如果 b 高、D 状态线程很多而 CPU 仍空闲,我会按命令和 wchan 聚合线程,选择代表性线程查看 /proc/12345/stack,再和 I/O PSI 对齐。
假设大量线程都停在 NFS 相关等待,I/O PSI 同时上升,本地块设备 iostat 却正常,我会转查挂载、客户端重传、网络和 NFS 服务端,不会升级本地云盘。止损可能是停止相关批任务、把读取切到健康副本或摘除故障节点;直接反复杀 D 状态进程通常不会立即生效。
修复后,我会验证业务尾延迟和错误率、R/D 线程数、cgroup 与主机 PSI、NFS 延迟和积压都恢复。1、5、15 分钟负载会按时间衰减,所以即使 15 分钟值仍高,只要实时等待和用户结果已稳定,也不应为追求一个立刻变小的数字继续做高风险操作。”
常见错误
- 把 Load Average 当 CPU 使用率 → 它统计任务数量,并包含不可中断睡眠 → 先拆分
r与b、R 与 D,再判断 CPU 竞争。 - 看到负载超过 CPU 数就宣布过载 → 该规则只对 CPU 型负载有粗略提示价值 → 结合 CPU busy、CPU PSI 和可运行队列。
- 把 1、5、15 分钟值当简单窗口平均 → Linux 使用指数衰减,旧样本逐渐保留 → 只用三值判断方向,并用实时指标确认当前状态。
- 把所有 D 状态都归因于本地磁盘 → NFS、文件系统、驱动和其他内核等待也可能进入不可中断睡眠 → 沿
wchan、栈与对应子系统指标定位。 - iowait 低就排除 I/O → iowait 是 CPU 时间统计,不等于等待任务数量 → 查看 D 状态、I/O PSI、设备与远程存储延迟。
- 只看主机平均 CPU → 单核热点、亲和性、配额或其他 cgroup 会被平均掩盖 → 比较每核、主机和目标 cgroup。
- 反复
kill -9卡住的任务 → 信号要等不可中断等待返回后才处理,现场还可能被破坏 → 先保存证据并修复被等待的资源。 - 把一条
wchan当作根因 → 它只显示当前内核睡眠位置 → 要求大量任务聚集、时间相关性和子系统异常互相印证。 - 只等 Load Average 归零才宣布恢复 → 指数衰减会滞后,正常系统也不要求负载为零 → 以用户结果、实时任务、PSI 与资源指标共同验收。
追问及应对
追问一:CPU idle 很高,为什么 %iowait 也可能很低?
%iowait 表示 CPU 处于 idle 且系统认为有未完成 I/O 的时间比例,它是 CPU 记账口径。任务可以阻塞在 NFS、驱动或其他不可中断内核等待,而 CPU 同时运行别的工作或保持普通 idle;多核系统上的聚合也会稀释局部现象。用 D 状态数量、I/O PSI、wchan 和子系统延迟验证,不能用低 iowait 单独排除 I/O 或内核等待。
追问二:容器里看到的 Load Average 能代表这个容器吗?
不能默认代表。负载值和 /proc 的可见范围取决于宿主机、PID namespace、运行时与监控实现;应用 CPU 指标又可能按 cgroup 统计。应同时读取宿主机与目标 cgroup 的 CPU、I/O、内存 PSI 和配额事件,并把线程归属到 cgroup。若主机负载很高但目标 cgroup 没有压力,扩容该服务可能没有收益。
追问三:负载 48、CPU idle 72%,是否一定存在大量 D 状态?
不一定。还可能是指标不同步、CPU 平均掩盖单核热点、任务受 affinity 或 cgroup 配额限制,或负载刚从高点下降而指数平均尚未衰减。检查当前 /proc/loadavg 的 runnable 数、vmstat r/b、每核利用率、R/D 线程数和采集时间,才能确定组成。
追问四:为什么 D 状态进程有时连 kill -9 都不消失?
不可中断睡眠的设计目标是让某些内核操作在关键阶段不被普通信号打断。SIGKILL 可以被标记为待处理,但任务通常要先从等待返回,调度到能够处理退出的路径,进程才会消失。修复设备、挂载、网络或驱动等待往往比重复发信号更关键;必要的主机重启应按数据完整性和服务冗余评估。
追问五:如何为 Load Average 设置告警?
不要复制一个固定的“负载大于 CPU 数”阈值。CPU 型服务应把 load、可运行队列、CPU PSI、利用率和 cgroup throttling 组合;I/O 型服务应关注 I/O PSI、D 状态、设备或远程存储延迟与业务 SLO。告警阈值来自正常基线、持续时间、扩容速度和用户影响,并且要标注指标范围。