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。这样既能提升响应,又不会搞崩系统。

我把调度策略、优先级与时间片的关系画了一张图,帮你一眼看明白:

CPU调度策略与优先级结构 Linux调度器 调度队列 实时队列 普通队列 调度策略 SCHED_OTHER 动态时间片 + nice值 SCHED_FIFO 无时间片,直到yield SCHED_RR 固定时间片轮转 优先级范围 普通优先级: nice -20 ~ +19(值越小优先级越高) 实时优先级: 0 ~ 99(值越大优先级越高)

曾经有个坑:我在优化一个视频播放器时,把解码线程设成了SCHED_FIFO优先级60,结果CPU被解码占满,渲染线程一直抢不到时间片,画面直接卡死。改成SCHED_RR后设置相同优先级,才恢复正常。所以记住了——实时策略不是万能药,用错比不用更糟。

总结一下:调度策略决定了抢CPU的规则,优先级决定了抢的顺序。帧率抖动排查中,重点看渲染线程是不是被更高优先级的后台任务抢了,或者本身策略设置不当。下一块我们要聊的,正是怎么用工具把这些优先级和调度行为抓出来。嗯,你先消化这张图,后面细说。