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

当 vmstat 中的 r(运行队列长度)持续偏高,但 us、sy、wa 等 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_slowpath、cpuhp_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 消耗,高并发场景下对调度器数据结构(如 rq、cfs_rq)、RCU 全局状态、或 per-CPU 资源(如 task_struct 分配缓存)的激烈争用,会导致就绪进程反复尝试获取锁失败而自旋或短暂休眠,表现为 r 高但 sy 不高(因锁等待未计入传统上下文切换开销)。
- 使用
perf record -e 'sched:sched_switch' -a sleep 10后perf script | awk '{print $9}' | sort | uniq -c | sort -nr | head -10,观察是否频繁在try_to_wake_up、pick_next_task_fair等调度路径上切换 - 检查 RCU stall(
dmesg -T | grep -i "rcu.*stall"),RCU 宽限期过长会拖慢整个调度周期 - 查看
/proc/sched_debug中nr_cpus、nr_switches、各 CPU 的rq->nr_running是否严重不均衡;若某 CPU 的nr_running远高于其他,可能是负载均衡失效或该 CPU 被锁住
确认是否受 cgroups 或 namespace 机制限制
cgroups v1 的 CPU 子系统(特别是 cpu.shares 或 cpu.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]/status中CapEff、NSpid字段是否完整;若NSpid显示为0,说明其 PID namespace 初始化卡住 - 对比
cat /sys/fs/cgroup/cpu/cpu.stat中nr_throttled和throttled_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,最后考虑内核版本,往往能准确定位根源。










