Linux 负载很高但 CPU 不满:区分运行队列、D 状态与资源压力

5 次浏览1 条回复

适用于 Linux 主机出现 load average 持续升高,但监控中的 CPU 使用率并未接近 100% 的场景。命令基线为 procps-ng 3.3+;pidstatiostat 来自 sysstat,属于可选工具;PSI 需要 Linux 4.20+ 且内核启用相应接口。以下均为只读取证,示例 PID 和 cgroup 路径必须替换为实际值。

1. 先固定口径:负载不是 CPU 百分比

date -u
uptime
nproc
cat /proc/loadavg
vmstat 1 10

Linux 的 load average 统计可运行任务和不可中断睡眠任务,不能直接解释为 CPU 使用率。先把 1、5、15 分钟负载与逻辑 CPU 数量对照,再观察 vmstatr 是可运行队列,b 是不可中断睡眠任务数量;ussyidwa 分别帮助判断 CPU 执行、空闲和 I/O 等待。单次采样容易误判,应覆盖实际告警窗口。

wa 高只说明 CPU 在采样期内存在 I/O 等待,不足以单独证明磁盘故障;虚拟机中的 st 偏高则要继续检查宿主机 CPU 竞争。

2. 找出是运行队列堆积还是 D 状态堆积

ps -e -o state,pid,ppid,comm,wchan:32 --sort=state
ps -e -o stat,pid,ppid,etime,comm,wchan:32 \
  | awk '$1 ~ /^D/ {print}'

r 长时间高于可用 CPU 数且 us+sy 同时较高,优先检查 CPU 饱和、线程数量和配额;若 CPU 仍有明显空闲而 b、D 状态任务持续增加,则应沿任务等待的内核路径排查存储、网络文件系统、块设备或驱动。wchan 只提供等待位置线索,不能仅凭函数名下结论。

有 root 权限时,可为单个持续 D 状态进程补充内核栈:

sudo cat /proc/<pid>/stack

保存同一时间点的 PID、wchan 和内核日志,避免进程退出后丢失关联。公开输出前应移除命令参数、挂载路径和内部主机信息。

3. 用 PSI 判断资源是否真的让任务停顿

for resource in cpu io memory; do
  printf '\n[%s]\n' "$resource"
  cat "/proc/pressure/$resource"
done

PSI 的 some 表示至少有任务因该资源停顿,full 表示所有非空闲任务同时停顿;avg10avg60avg300 是不同时间窗口的平均占比,total 是自启动以来累计的微秒数。应比较告警前后增量,不能把累计值本身当作当前故障强度。

常见组合可这样缩小范围:

  • CPU PSI 上升、运行队列高:检查 CPU 饱和或 cgroup 配额。
  • I/O PSI 上升、D 状态增多:检查块设备延迟、文件系统和网络存储。
  • memory PSI 上升并伴随换页:检查内存回收、swap 和 cgroup 内存限制。

4. 按方向补充设备与进程证据

安装 sysstat 后,可在同一窗口采样:

pidstat -u -d -r -w 1 10
iostat -xz 1 10

pidstat 用于关联进程的 CPU、I/O、缺页和上下文切换;iostat 用于比较设备吞吐、队列和延迟。不要只看 %util:设备类型、并行能力和内核版本会影响它的含义,应结合 await、队列深度、吞吐变化及应用延迟判断。网络文件系统等待不一定体现在本地块设备指标中,还需检查对应挂载与服务端。

5. 容器场景检查 cgroup 限制

宿主机 CPU 空闲不代表容器没有被限流。cgroup v2 环境先定位目标进程所属路径:

cat /proc/<pid>/cgroup

然后在对应 cgroup 目录读取:

cat /sys/fs/cgroup/<path>/cpu.max
cat /sys/fs/cgroup/<path>/cpu.stat
cat /sys/fs/cgroup/<path>/cpu.pressure
cat /sys/fs/cgroup/<path>/io.pressure
cat /sys/fs/cgroup/<path>/memory.events

比较 cpu.statnr_throttledthrottled_usec 的增量;只有它们在故障窗口持续增长,才支持 CPU 配额限流判断。cgroup v1 的文件名与目录布局不同,应先用 stat -fc %T /sys/fs/cgroup 确认层级,不能照搬 v2 路径。

6. 闭环验证

每次只调整一个有证据支持的瓶颈,例如线程并发、cgroup 配额、异常存储路径或内存限制。修复后在相同业务负载下重复 vmstat、PSI 及对应的 pidstatiostat 采样。闭环至少应满足:应用延迟恢复,运行队列或 D 状态任务回落,相关 PSI 增量下降,并且内核日志与应用日志不再出现同类错误。仅看到 load average 延迟回落不足以证明根因已经消除。

补一个容易漏掉的分支:整机 CPU 利用率不高,不等于这些可运行任务能使用所有 CPU。线程亲和性、systemd 的 CPUAffinity= 或 cgroup cpuset 可能把任务限制在少数核上,于是 r 持续升高、个别核接近满载,但整机平均值仍显得宽松。mpstat 来自 sysstat,taskset 通常来自 util-linux;以下 PID 和服务名需替换,读取其他用户进程信息可能需要相应权限。

mpstat -P ALL 1 10
ps -eLo pid,tid,psr,stat,comm --sort=psr
taskset -pc <pid>
awk '/^(Cpus_allowed_list|Mems_allowed_list):/ {print}' /proc/<pid>/status
systemctl show app.service -p CPUAffinity

若是 cgroup v2,可再核对目标进程实际生效的 CPU 集合,而不只看父级配置:

cg=$(awk -F: '$1 == "0" {print $3}' /proc/<pid>/cgroup)
cat "/sys/fs/cgroup${cg}/cpuset.cpus.effective"
cat "/sys/fs/cgroup${cg}/cpu.max"

判断时把 mpstat 的逐核占用、线程当前运行核 psr、允许 CPU 列表放在同一采样窗口。若只有允许集合内的核饱和,先解释亲和性或 cpuset 来源;调整后应验证逐核队列与延迟一起回落。若允许集合很宽但单核仍被打满,再检查单线程热点以及 IRQ/softirq 是否集中,不能仅凭整机平均利用率排除 CPU 瓶颈。