CPU调度与优先级机制
做性能优化的人,多半绕不开调度器。你写的程序能不能抢到CPU时间片,什么时候被踢出去,全看它脸色。这一节我就把调度器的底牌翻出来,说说它怎么干活、优先级怎么排,以及普通策略和实时策略到底差在哪。
调度器的工作原理
调度器说白了就是操作系统的“排班经理”。CPU只有一个(多核咱先不管),但进程有成百上千个。谁上CPU,谁去睡觉,由调度器说了算。
它的核心机制其实不复杂:
- 时间片轮转:每个进程分到一个固定时长的时间片。时间片用完后,调度器强行切换。
- 上下文切换:保存当前进程的寄存器、栈、页表等状态,加载下一个进程的状态。这个操作本身有开销。
- 调度队列:系统维护多个队列,比如就绪队列、阻塞队列。实时进程和普通进程分开放。
我记得早年做嵌入式调试时,在中断里加了个延迟打印,结果整个系统像踩了刹车。查了半天才发现——中断服务函数里的耗时操作挤占了调度时机,普通进程几乎拿不到时间片。你想想看,就那几毫秒的耽误,帧率直接往下掉。
调度器的具体行为,取决于它用的调度策略和优先级。咱们往下拆。
优先级范围:从 -20 到 +99
Linux里优先级分两套:普通优先级和实时优先级。
| 类别 | 范围 | 含义 |
|---|---|---|
| 普通优先级 (nice值) | -20 ~ +19 | 值越小优先级越高。默认0。 |
| 实时优先级 | 0 ~ 99 | 值越大优先级越高。99最高。 |
这里有个关键点:实时优先级永远高于普通优先级。也就是说,只要有一个实时优先级为1的进程就绪,所有nice值-20的进程都得靠边站。
我在项目中遇到过,一个后台服务被错误设成了SCHED_FIFO策略、优先级50,结果前台UI线程卡得像幻灯片。明明UI线程的nice值已经是-20,但优先级50压死一切。所以,搞帧率优化的人一定要搞清楚:你的渲染线程到底是什么调度策略?
普通策略 vs 实时策略
Linux常用的调度策略就三种:SCHED_OTHER(默认普通)、SCHED_FIFO(先入先出实时)、SCHED_RR(轮转实时)。区别非常大,我直接放对比表:
| 策略 | 抢占条件 | 时间片 | 适用场景 |
|---|---|---|---|
| SCHED_OTHER | 自动降优先、时间片耗尽 | 动态调整(约10ms±) | 绝大多数用户进程、后台任务 |
| SCHED_FIFO | 仅被更高优先级实时进程打断,或自行yield | 无时间片,一直运行到主动让出 | 对延迟极度敏感、短事务的任务(如音频处理) |
| SCHED_RR | 同等优先级轮流,被更高优先级打断 | 固定时间片(默认100ms) | 需要公平但实时性要求较高的场景 |
你可能会问:“为什么不把所有渲染线程都设成SCHED_FIFO?那不是最优先吗?”我的经验是千万别这么干。SCHED_FIFO的进程一旦跑起来就不会主动让出CPU,如果里面有个死循环或者阻塞了,整个系统都跟着完蛋。我见过一哥们把GPU驱动线程设成SCHED_FIFO,结果驱动程序没处理好,直接卡黑屏——连ssh都进不去。
核心结论:
- 普通策略(SCHED_OTHER)依赖nice值和动态优先级,适合大多数任务。
- 实时策略(SCHED_FIFO / SCHED_RR)适合短时、可控的延迟敏感任务。
- 千万不要随意给长周期任务设置实时策略,否则你会被性能工程师追着打的。
如何查看和修改调度策略
实战中你肯定需要查当前进程的调度信息。用这个命令:
ps -eo pid,cls,pri,ni,cmd | grep your_process
cls列显示调度策略:TS表示SCHED_OTHER,FF表示SCHED_FIFO,RR表示SCHED_RR。pri列显示内核优先级(100~139)。ni就是nice值。
修改策略可以用chrt命令:
chrt -f -p 50 1234 # 将pid为1234的进程设为SCHED_FIFO,优先级50
chrt -o -p 0 1234 # 恢复为SCHED_OTHER,优先级0(即nice值0)
小技巧:如果只想让渲染线程的优先级稍微高一点,别直接用实时策略。调低nice值就行了,比如 nice -n -10 ./my_app。这样既能提升响应,又不会搞崩系统。
我把调度策略、优先级与时间片的关系画了一张图,帮你一眼看明白:
曾经有个坑:我在优化一个视频播放器时,把解码线程设成了SCHED_FIFO优先级60,结果CPU被解码占满,渲染线程一直抢不到时间片,画面直接卡死。改成SCHED_RR后设置相同优先级,才恢复正常。所以记住了——实时策略不是万能药,用错比不用更糟。
总结一下:调度策略决定了抢CPU的规则,优先级决定了抢的顺序。帧率抖动排查中,重点看渲染线程是不是被更高优先级的后台任务抢了,或者本身策略设置不当。下一块我们要聊的,正是怎么用工具把这些优先级和调度行为抓出来。嗯,你先消化这张图,后面细说。