vmstat 1 是最直接有效的命令,每秒输出系统级上下文切换总数(cs列),从内核全局计数器读取,覆盖进程切换、中断和软中断三类事件,是判断“是否过高”的第一道筛子。

vmstat 1 是最直接有效的命令
不用装包、不依赖额外工具,vmstat 1 就能实时输出系统级每秒上下文切换总数(cs 列)。它从内核全局计数器读取,覆盖进程切换、中断和软中断三类事件,是判断“是否过高”的第一道筛子。
注意:第一行是启动以来的平均值,无效;从第二行开始才是每秒实时数据。空闲系统下 cs 通常在 1000–1500 次/秒,持续超过 5000/秒需警惕,但必须结合其他列看——单独一个高 cs 值无法定位根因。
-
r(就绪队列长度)长期大于 CPU 核数 → 非自愿切换主导,大概率 CPU 竞争 -
b(不可中断睡眠进程数)同步升高 → 自愿切换多,常见于磁盘 I/O 卡顿或 NFS 挂起 -
in(每秒中断数)与 cs 同步飙升 → 中断风暴,比如网卡收包过载
pidstat -w 1 定位“谁在贡献切换”
pidstat -w 1 输出每个进程的 cswch/s(自愿)和 nvcswch/s(非自愿)切换次数,这才是真正能查到“哪个 Java 进程在疯狂刷日志”“哪个 Python 线程在死循环抢锁”的命令。
关键区别不能混:自愿切换高,说明它在等资源(如 read() 等磁盘、sleep() 主动挂起、缺页被阻塞);非自愿切换高,说明它被调度器强制切出(时间片耗尽、CPU 太忙),典型场景是高并发计算或短生命周期线程频繁创建销毁。
- 同一进程
cswch/s和nvcswch/s都高 → 很可能是设计缺陷:一个线程既做密集计算又发 I/O,导致反复被切出又被唤醒 - 只看总和没用:
pidstat -w的总和一定小于vmstat的cs,差值就是中断相关切换,别试图拿它们对齐
perf record -e context-switches 查“为什么切、在哪切”
当 pidstat -w 锁定了高切换进程,但不知道它到底在哪个函数里触发切换时,perf 是唯一能钻进内核路径的工具。
执行 sudo perf record -e context-switches -p <code>PID sleep 5,再用 sudo perf script 查调用栈。你会看到类似:do_syscall_64 → sys_read → __fdget(I/O 触发)或 sched_slice → pick_next_task_fair(CFS 调度器主动切出)。
- 加
-g才能包含内核调用路径,但开销显著上升,生产环境慎用 - 默认只记录用户态栈,看不到内核侧原因;不加
-g可能只看到libc调用,误判为应用层问题 - 确认内核已启用
CONFIG_CONTEXT_SWITCH_TRACER(主流发行版默认开启,但某些定制内核可能关了)
别被“多核切换数”误导,实际要查的是单核压力分布
Linux 没有“每核上下文切换数”这个指标。vmstat 的 cs 是全核总和,除以 CPU 数毫无意义。你要解决的实际问题是:“哪颗 CPU 正在被反复抢占?”
得组合使用:taskset -cp <code>PID 看线程绑核情况,ps -o pid,comm,psr -T -p <code>PID 看当前运行在哪颗 CPU(psr 列),再配合 pidstat -w -T ALL 1(-T ALL 开启线程级统计)观察高 nvcswch/s 线程是否始终落在同一颗 CPU 上。
- 若某线程
nvcswch/s > 500且psr总是 = 0 → CPU 0 是瓶颈,不是“多核切换不均”,而是单核被压垮 -
perf record -e context-switches -a抓全系统切换后,用awk '{print $NF}' | sort | uniq -c可粗略看出哪颗 CPU 切换最多,但不如psr+pidstat -w -T ALL直观可靠
真正容易被忽略的是中断上下文切换:它不计入 pidstat -w,却大幅推高 vmstat cs。如果 in 和 cs 同步暴涨,先查 /proc/interrupts 看中断是否集中打在某颗 CPU 上,而不是一头扎进应用代码里找 bug。











