vmstat r 队列长期高但 cpu %wa 低的锁等待 / futex 分析

21次阅读

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

vmstat r 队列长期高但 cpu %wa 低的锁等待 / futex 分析

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//stack(挑 r 状态进程):若顶是 futex_wait_queue_medo_futex,就是卡在 futex 上
  • ps -eo pid,ppid,comm,wchan --sort=-wchan | head -10wchan 列显示进程阻塞在哪个内核函数,futex_waitmutex_lock 是典型信号
  • 应用层日志:java 应用可 dump jstack 看 BLOCKED 线程;Go 可查 runtime/pprof?debug=2goroutine stack

为什么 %wa 低却仍有“等待”?关键在调度语义

%wa 只统计进程明确处于 TASK_UNINTERRUPTIBLE 并等待 I/O 完成的状态(如读磁盘)。而 futex 等待默认是 TASK_INTERRUPTIBLE,进程挂起后仍算作“可运行”(进入 r 队列),调度器认为它“随时能跑”,只是没轮到——所以 r 高但 %wa 不涨。

这种等待本质是**调度延迟**,不是 I/O 延迟。真正瓶颈可能是:锁粒度太粗、临界区过长、或 NUMA 节点间 cache line bouncing。优化方向不在加大磁盘带宽,而在减少锁持有时间或改用无锁结构。

text=ZqhQzanResources