vmstat r 队列长期高但无明显 CPU 消耗的等待锁或 IO 等待分析

10次阅读

r值持续偏高而CPU使用率低,说明就绪进程因D状态进程阻塞、内核锁争用、cgroups限制或内核缺陷等原因无法被调度执行。

vmstat r 队列长期高但无明显 CPU 消耗的等待锁或 IO 等待分析

vmstat 中的 r(运行队列长度)持续偏高,但 ussywa 等 CPU 使用率指标却很低时,说明系统中有大量进程处于“就绪态”(ready to run),却迟迟得不到 CPU 时间片——这不是因为 CPU 忙,而是因为调度受阻。最常见且容易被忽略的原因是:不可中断睡眠(D状态)进程阻塞了调度器路径,或内核锁争用导致就绪进程无法被及时唤醒和调度

检查是否存在 D 状态进程(不可中断睡眠)

D 状态进程通常在等待慢速 I/O(如挂起的 NFS、坏盘、cgroup 冻结、内核模块死锁等),它们不响应信号,也不释放资源,但会持续占据运行队列槽位,使 r 值虚高。注意:D 进程本身不消耗 CPU,也不会体现在 wa 中(尤其在非存储 I/O 场景下)。

  • 运行 ps -eo pid,stat,comm,wchan --sort=-time | head -20,重点关注 STAT 列含 D 的进程,及其 WCHAN(等待的内核函数)
  • 配合 cat /proc/[pid]/stack 查看 D 进程的内核调用,确认卡在哪个路径(如 nfs_wait_bit_killable__mutex_lock_slowpathcpuhp_state_wait
  • 检查是否有异常挂载(mount | grep -E "(nfs|cifs)")、cgroup v1 冻结(cat /sys/fs/cgroup/freezer/*/freezer.state)、或 SCSI 设备超时(dmesg -T | grep -i "timeout|reset|hang"

排查内核锁竞争(尤其是调度器/RCU/Per-CPU 锁)

现代内核中,即使没有明显 I/O 或 CPU 消耗,高并发场景下对调度器数据结构(如 rqcfs_rq)、RCU 全局状态、或 per-CPU 资源(如 task_struct 分配缓存)的激烈争用,会导致就绪进程反复尝试获取锁失败而自旋或短暂休眠,表现为 r 高但 sy 不高(因锁等待未计入传统上下文切换开销)。

  • 使用 perf record -e 'sched:sched_switch' -a sleep 10perf script | awk '{print $9}' | sort | uniq -c | sort -nr | head -10,观察是否频繁在 try_to_wake_uppick_next_task_fair 等调度路径上切换
  • 检查 RCU stall(dmesg -T | grep -i "rcu.*stall"),RCU 宽限期过长会拖慢整个调度周期
  • 查看 /proc/sched_debugnr_cpusnr_switches、各 CPU 的 rq->nr_running 是否严重不均衡;若某 CPU 的 nr_running 远高于其他,可能是负载均衡失效或该 CPU 被锁住

确认是否受 cgroups 或 Namespace 机制限制

cgroups v1 的 CPU 子系统(特别是 cpu.sharescpu.cfs_quota_us 设置不当)、或容器 runtime(如 runc)在创建进程时触发的 namespace 初始化延迟(如 user ns 映射、seccomp 加载),都可能让进程卡在内核态初始化阶段,处于就绪态但无法真正进入可运行状态。

  • 执行 find /sys/fs/cgroup/cpu -name "tasks" -exec sh -c 'echo {} ; wc -l /dev/null | awk '$1 > 100 {print}',快速定位任务数异常多的 cgroup
  • 检查容器内进程的 /proc/[pid]/statusCapEffNSpid 字段是否完整;若 NSpid 显示为 0,说明其 PID namespace 初始化卡住
  • 对比 cat /sys/fs/cgroup/cpu/cpu.statnr_throttledthrottled_time,确认是否因 CFS 配额耗尽导致进程被 throttle 后仍留在运行队列

验证是否由内核缺陷或特定驱动引发

某些内核版本存在已知调度器 bug(如 4.19~5.4 间部分版本的 dl_task 处理缺陷)、或特定硬件驱动(如某些 NVMe 驱动、虚拟化平台 paravirt 驱动)在异常路径下未正确更新调度状态,也会造成运行队列积。

  • 比对当前内核版本是否在 kernel.org bugzilla 或发行版 errata 中有相关报告(搜索关键词:"runqueue stuck" "D state" "scheduler hang"
  • 临时禁用可疑模块(如 modprobe -r nvme_core 后观察 r 值变化),或升级到 LTS 内核(如 6.1+)验证是否缓解
  • 启用调度器调试:启动参数加 sched_debug,然后 cat /proc/sched_debug 查看 rq->clock 是否停滞、nr_switches 是否增长缓慢

这类问题本质是“调度可见性”与“实际执行能力”的脱节。不要只盯着 CPU 百分比,要深入内核态行为。从 D 进程入手,再查锁与 cgroup,最后考虑内核版本,往往能准确定位根源。

text=ZqhQzanResources