r队列高但%wa低说明进程就绪却未获CPU调度,主因是锁竞争、futex争用或CPU抢占;可用perf record捕获futex系统调用占比,结合pidstat、/proc/pid/stack等交叉验证。

vmstat 中 r 队列高但 %wa 低,大概率不是 I/O 等待
这说明系统有大量进程就绪却得不到 CPU 调度,但又没在等磁盘或网络——%wa 低直接排除了传统 I/O 瓶颈。常见于锁竞争、futex 争用、CPU 资源被抢占(如 cgroups 限频)、或内核态自旋等待。此时 r 值持续 ≥ CPU 核数 × 2 就值得警惕。
确认是否为 futex 等待:用 perf record -e 'syscalls:sys_enter_futex' -a sleep 5
futex 是用户态锁(如 pthread_mutex、Go runtime mutex)的底层机制,高频调用往往意味着线程在抢锁。该命令能捕获实际 futex 系统调用次数:
- 若输出中
syscalls:sys_enter_futex占比异常高(比如 >30% 的 syscall 总量),基本锁定是锁争用 - 配合
perf script | head -20可看到调用栈,定位到具体函数(如pthread_mutex_lock或 Go 的runtime.semasleep) - 注意:需 root 权限;生产环境建议用
-g加调用图,但开销略大
排查锁等待的其他关键线索
仅靠 vmstat 不够,要交叉验证:
-
pidstat -w 1:看%cwch(上下文切换)是否飙升——锁争用常伴随频繁唤醒/休眠 -
cat /proc/(挑 r 状态进程):若栈顶是/stack futex_wait_queue_me或do_futex,就是卡在 futex 上 -
ps -eo pid,ppid,comm,wchan --sort=-wchan | head -10:wchan列显示进程阻塞在哪个内核函数,futex_wait或mutex_lock是典型信号 - 应用层日志:Java 应用可 dump
jstack看 BLOCKED 线程;Go 可查runtime/pprof?debug=2的 goroutine stack
为什么 %wa 低却仍有“等待”?关键在调度语义
%wa 只统计进程明确处于 TASK_UNINTERRUPTIBLE 并等待 I/O 完成的状态(如读磁盘)。而 futex 等待默认是 TASK_INTERRUPTIBLE,进程挂起后仍算作“可运行”(进入 r 队列),调度器认为它“随时能跑”,只是没轮到——所以 r 高但 %wa 不涨。
这种等待本质是**调度延迟**,不是 I/O 延迟。真正瓶颈可能是:锁粒度太粗、临界区过长、或 NUMA 节点间 cache line bouncing。优化方向不在加大磁盘带宽,而在减少锁持有时间或改用无锁结构。










