上下文切换是什么
上下文切换是 CPU 从一个任务切到另一个任务的过程:保存当前任务的寄存器与栈状态,载入下一个任务的状态,并因为换了一套工作集而损失缓存局部性。切换分两类:自愿切换(任务主动让出 CPU,比如等锁、等 I/O、睡眠)与非自愿切换(被调度器抢占,说明运行队列上有别人在竞争)。数量本身没有绝对的健康区间,关键是与该机器的业务规模和历史基线比较。
基本信息
| 观测点 | /proc/stat 的 ctxt 累计计数、vmstat 的 cs 列、pidstat -w 的进程级统计 |
|---|---|
| 切换类型 | 自愿(等锁、等 I/O)与非自愿(被抢占) |
| 参考量级 | 多种正常负载下数万到数十万次每秒都可能出现 |
| 系统级视图 | vmstat 的 cs 与 r 队列联读 |
| 进程级视图 | pidstat -w 的 cswch/s 与 nvcswch/s |
| 线程级视图 | pidstat -wt 展开到线程维度 |
典型使用场景
- 定位 CPU 虚高:sy 占比高且切换频繁时,问题常在锁竞争而非业务计算
- 区分等待与争抢:自愿切换高说明任务在等资源,非自愿切换高说明 CPU 不够分
- 压测分析:并发线程数远超核数时切换量激增,吞吐反而下降
- 容量评估:为线程池与并发度设定合理上限,避免过度拆分
- 故障复盘:把切换量与延迟曲线对齐,确认抖动是否由调度引起
观测与定位
观测与定位
vmstat 1 5 # cs 列看系统级切换量,r 列看排队
pidstat -w 1 3 # 进程级自愿与非自愿切换
pidstat -wt 1 3 | head -40 # 展开到线程,定位具体线程
grep ctxt /proc/stat # 累计切换次数,作差换算速率
mpstat -P ALL 1 3 # 逐核使用率,确认是否单核竞争指标对照
| 指标 | 含义 | 判读要点 |
|---|---|---|
| vmstat cs | 每秒上下文切换次数 | 突增才值得关注,需与基线比较 |
| cswch/s | 进程自愿切换速率 | 偏高说明频繁等待锁或 I/O |
| nvcswch/s | 进程被抢占的速率 | 偏高说明 CPU 争抢激烈,线程数可能过多 |
| vmstat r | 可运行队列长度 | 持续大于核数说明排队已发生 |
只看切换总量无法得出结论:必须结合进程级统计与运行队列长度,才能判断是某几个进程异常还是全系统普遍竞争。定位顺序通常是先看 vmstat 的 cs 与 r,再用 pidstat 找具体进程与线程。
高切换多数时候根因在应用而非内核参数:阻塞式锁、忙等逻辑、过细的线程拆分都会放大切换量。调内核参数无法解决这类问题,方向应当是优化并发模型。