分时调度抢占延迟指sched_normal下可运行进程从就绪到获得cpu执行的时间差,由不可抢占区域、中断或自旋锁导致;可通过/proc/[pid]/schedstat第二列获取累计等待时间,或用getdelays -d -p pid查看单次平均抢占延迟(如10.068ms)。

什么是分时调度抢占延迟
分时调度(SCHED_NORMAL)下,抢占延迟指的是一个可运行进程从就绪到真正获得 CPU 执行之间的时间差。它不等于调度延迟(scheduler latency),而是更窄的定义:内核本该切换到你,却因不可抢占区域、高优先级中断或自旋锁持有而被推迟执行的时间。这个值在普通业务场景中常被忽略,但对延迟敏感服务(如高频交易网关、实时音视频转发)很关键。
怎么用 /proc/[pid]/schedstat 看基础抢占等待时间
/proc/[pid]/schedstat 是最轻量、无需额外工具的观测入口,每行三个数字分别代表:
- 进程在 CPU 上实际运行的纳秒数(
sum_exec_runtime) - 在就绪队列中等待被调度的总纳秒数(
sum_wait_runtime) - 被迁移(migrate)到其他 CPU 的次数
其中第二项就是你关心的「等待调度」时间总和,但它不是单次抢占延迟,而是累计值。要观察趋势,得周期性采集:
watch -n 1 'awk "{print \$2}" /proc/1234/schedstat'
注意:1234 换成目标进程 PID;该文件只在内核启用 CONFIG_SCHEDSTATS=y 时存在(主流发行版默认开启);非 root 进程只能读自己 PID 的该文件。
怎么用 getdelays 抓取单次抢占延迟分布
getdelays 是唯一能输出单次抢占延迟直方图的用户态工具,依赖内核 CONFIG_DELAY_ACCT 和 CONFIG_TASK_DELAY_ACCT。它给出的是「delayacct」统计,含真实抢占延迟(CPU 维度中的 delay average):
./getdelays -d -p 1234
输出中关键字段:
-
CPU count:该进程被调度执行的次数 -
delay total:所有抢占等待时间总和(纳秒) -
delay average:平均每次抢占延迟(如10.068ms)
⚠️ 容易踩的坑:
- 必须用
-d参数启用 delayacct,否则只输出空值 - 进程启动前未开启 delayacct,历史数据不回溯
- 输出单位是毫秒,但精度取决于内核定时器频率(
HZ),1000 HZ 下理论最小分辨率为 1ms
为什么 top/htop 看不到抢占延迟
top 和 htop 显示的 %CPU 是采样窗口内的平均占用率,它掩盖了瞬时抢占行为。比如一个进程每 10ms 被抢占 2ms,%CPU 可能只显示 20%,但实际每次响应都卡顿 2ms——这种抖动无法从 top 数字里识别。真正的抢占延迟必须靠内核原生统计(schedstat 或 delayacct)或 tracing(如 perf sched)捕获。
复杂点在于:抢占延迟本身是“不可见事件”,没有日志、不报错、不触发告警,只在 getdelays 的 delay average 或 perf sched latency 的直方图里悄悄累积。一旦发现平均值 > 1ms,就得查是否开了太多实时进程、有没有长持有自旋锁、或者 sched_rt_runtime_us 配得太激进。











