4、任务调度机制:抢占式调度、时间片轮转、协作式调度,优先级反转问题

各位同学,咱们今天聊点硬核的——任务调度。

调度机制,说白了就是操作系统怎么决定「下一个该谁跑」。这玩意儿在车端尤其重要。你想想看,刹车信号来了,结果系统还在那儿慢悠悠处理空调温度,那还得了?

我个人习惯把调度机制分成三大类:抢占式时间片轮转协作式。另外,优先级反转这个坑,我当年在项目中可是实打实踩进去过。今天一并讲清楚。

4.1 抢占式调度:谁急谁先上

抢占式调度,是车端用得最多的方式。它的核心逻辑就一句话:高优先级任务可以打断低优先级任务

举个例子。你在开车,突然仪表盘亮起「发动机故障」的警告灯。这个警告任务优先级最高,它必须立刻执行。哪怕此时系统正在处理车窗升降,也得先停下来,让警告任务先跑。

这就是抢占。高优先级任务来了,低优先级任务乖乖让路。

抢占式调度的特点:

  • 实时性好——高优先级任务响应快
  • 适合硬实时场景——比如安全气囊、刹车控制
  • 需要合理分配优先级——否则低优先级任务可能饿死

我在项目中遇到过一个问题:某个低优先级的诊断任务,因为优先级设得太低,结果在高负载下一直得不到执行。最后客户投诉说「诊断功能偶尔失效」。查了半天,发现是抢占太频繁,低优先级任务被活活饿死了。

嗯,这里要注意:抢占式调度不是万能的。优先级分配不合理,照样出问题。

4.2 时间片轮转:大家轮流来

时间片轮转,说白了就是「一人跑一会儿,谁也别独占」。

这种调度方式,适合那些优先级差不多的任务。比如车机上的多个后台服务——导航、音乐、语音识别。它们没有谁特别紧急,那就每人分一个时间片,轮流跑。

时间片的大小很关键。太短了,频繁切换上下文,CPU 开销大。太长了,用户体验差——你按一下屏幕,半天没反应。

时间片长度 优点 缺点
1-5 ms 响应快,交互流畅 上下文切换开销大
10-20 ms CPU 利用率高 响应稍慢,可能卡顿
50 ms 以上 切换开销极小 用户体验差,不推荐

我个人习惯,在车端应用里,时间片设在 5-10 ms 之间。既能保证交互流畅,又不会让 CPU 累死。

小技巧:如果某个任务特别重要,但又不想用抢占式,可以给它分配更多的时间片。比如导航任务分 3 个时间片,音乐任务分 1 个。这样导航跑得更久,但不会完全霸占 CPU。

4.3 协作式调度:自觉排队

协作式调度,跟抢占式正好相反。它不打断,而是让任务自己主动让出 CPU。

你想想看,这就像一群人排队打饭。每个人自觉打完就走,不插队。如果有人一直占着窗口不走,后面的人就饿着。

协作式调度在车端用得少,但也不是没有。比如一些简单的传感器采集任务,它们跑得快,跑完就主动让出 CPU。这种情况下,协作式反而更高效——省去了上下文切换的开销。

但是,协作式调度有个致命缺点:如果某个任务写了个死循环,或者执行时间过长,整个系统就卡死了。

警告:在车端,协作式调度只适合那些「你确定它不会跑太久」的任务。比如读取一个温度传感器,几微秒就完事。千万别把复杂逻辑放在协作式任务里。

我曾经在一个项目中,看到有人把 CAN 报文解析放在协作式任务里。结果某个报文解析逻辑出了 bug,卡在循环里出不来。整个系统直接死机。嗯,从那以后,我再也不敢在协作式任务里放复杂逻辑了。

4.4 优先级反转:一个经典大坑

优先级反转,是实时系统里最经典的坑之一。我当年第一次遇到时,查了整整三天才找到原因。

什么叫优先级反转?简单说就是:高优先级任务被低优先级任务间接阻塞了

举个例子。有三个任务:

  • 任务 A:优先级最高,负责刹车控制
  • 任务 B:优先级中等,负责车窗升降
  • 任务 C:优先级最低,负责空调温度

假设任务 C 先拿到了某个共享资源(比如一个全局变量)。然后任务 A 来了,它也要用这个资源。但资源被 C 占着,A 只能等。

这时候,任务 B 来了。B 不需要那个资源,所以它直接抢占 C,开始执行。结果呢?A 在等 C,C 被 B 抢占了。A 的优先级最高,却要等 B 跑完才能执行。

这就是优先级反转。高优先级任务,被中等优先级任务间接拖住了。

优先级反转的后果:

  • 实时性无法保证——高优先级任务响应变慢
  • 严重时导致系统崩溃——比如刹车信号延迟

4.5 如何解决优先级反转?

解决优先级反转,主要有两种方法:优先级继承优先级天花板

优先级继承:当高优先级任务被低优先级任务阻塞时,低优先级任务临时继承高优先级任务的优先级。这样它就不会被中等优先级任务抢占了。

还是刚才的例子。任务 C 占着资源,任务 A 在等。这时候,C 临时继承 A 的优先级。任务 B 来了,发现 C 的优先级比它高,抢不了。于是 C 赶紧跑完,释放资源,A 立刻接手。

优先级天花板:给每个共享资源设定一个「天花板优先级」。任何任务访问这个资源时,它的优先级自动提升到天花板级别。

这两种方法,我个人更推荐优先级继承。因为它更灵活,只在必要时才提升优先级。优先级天花板虽然简单粗暴,但可能会造成不必要的优先级提升,影响系统效率。

避坑指南:我曾经在一个项目中,用了优先级天花板,结果把天花板设得太高。导致一个本来不紧急的任务,因为访问了一个共享资源,优先级被提得老高,反而影响了其他紧急任务。后来改成优先级继承,问题就解决了。

4.6 小结

好了,今天的内容就到这里。咱们总结一下:

  • 抢占式调度:谁急谁先上,适合硬实时场景。但要注意优先级分配,别饿死低优先级任务。
  • 时间片轮转:大家轮流跑,适合优先级差不多的任务。时间片设在 5-10 ms 比较合适。
  • 协作式调度:自觉让出 CPU,适合简单快速的任务。别放复杂逻辑,否则系统会卡死。
  • 优先级反转:高优先级被低优先级间接阻塞。用优先级继承或优先级天花板来解决。

下一章,咱们聊聊任务间通信。信号量、消息队列、邮箱——这些东西在车端怎么用,有哪些坑。到时候见。