3、任务优先级与抢占:静态优先级、动态优先级、优先级反转问题

各位同学,咱们今天聊点硬核的——任务优先级与抢占。这东西说白了,就是操作系统里谁先干活、谁后干活的问题。我做了十几年嵌入式系统,见过太多因为优先级没搞明白,导致系统崩溃的案例。嗯,咱们一个一个来拆解。

3.1 静态优先级:简单粗暴,但够用

静态优先级,顾名思义,就是任务创建时定死的优先级,运行期间雷打不动。我个人习惯在简单系统里用这个,比如一个温度采集器,采集任务优先级高,显示任务优先级低,跑起来稳稳当当。

特点:

  • 优先级在任务创建时设定,运行期间不变
  • 调度器每次选择最高优先级的就绪任务执行
  • 实现简单,开销小

核心要点:静态优先级适合任务数量固定、实时性要求明确的场景。比如我做过的一个智能家居网关,传感器数据采集优先级最高,网络通信次之,日志记录最低,用静态优先级完全够用。

但静态优先级有个坑——你想想看,如果高优先级任务一直不阻塞,低优先级任务就永远没机会运行。这就是所谓的“饥饿”问题。我曾经在一个项目里,把按键扫描任务设成了最低优先级,结果系统忙的时候,按了键半天没反应,用户投诉说产品坏了。后来我把按键任务优先级提了一级,问题就解决了。

3.2 动态优先级:灵活,但别滥用

动态优先级就高级一些了。任务的优先级可以在运行过程中调整。为什么要这么干?说白了,就是为了应对复杂场景。

常见的动态优先级策略:

  • 时间片轮转+优先级:每个任务先按优先级调度,同优先级任务轮转执行
  • 优先级老化:等待时间越长的任务,优先级逐渐提升,防止饥饿
  • 基于截止时间的调度:离截止时间越近,优先级越高

我记得在做一个视频处理项目时,用了动态优先级。解码任务在数据充足时优先级高,数据不足时自动降低优先级,让编码任务多跑一会儿。这样系统整体吞吐量提升了30%。

我的经验:动态优先级虽然灵活,但别滥用。每次调整优先级都有开销,调整太频繁反而降低效率。我一般建议:调整频率不要高于任务执行周期的1/10。

3.3 优先级反转:一个让人头疼的问题

优先级反转,这名字听着就吓人。但说白了,就是一个高优先级任务,被一个低优先级任务间接阻塞了。为什么会这样?

来看个经典场景:

  • 任务A(高优先级)和任务C(低优先级)共享一个互斥锁
  • 任务B(中优先级)不共享这个锁
  • 任务C先拿到锁,然后被任务B抢占
  • 任务A想拿锁,但锁在任务C手里,而任务C又被任务B抢占了
  • 结果:高优先级的任务A,被中优先级的任务B间接阻塞了

我曾经在一个无人机飞控项目里遇到过这个问题。姿态控制任务(高优先级)被一个日志记录任务(低优先级)持有的锁阻塞了,中间还插了个通信任务(中优先级)在那跑。结果无人机在空中晃了一下,差点炸机。嗯,那次之后,我对优先级反转就特别敏感。

3.4 解决方案:优先级继承与优先级天花板

怎么解决优先级反转?业界有两个主流方案。

方案一:优先级继承

当一个低优先级任务持有高优先级任务需要的锁时,低优先级任务临时“继承”高优先级任务的优先级。这样它就能尽快执行完,释放锁。

方案二:优先级天花板

每个锁都有一个“天花板优先级”,任何任务持有这个锁时,优先级自动提升到天花板级别。这比优先级继承更简单,但需要提前规划好。

方案 优点 缺点
优先级继承 灵活,只在必要时提升优先级 实现复杂,可能发生链式继承
优先级天花板 实现简单,可预测性强 可能过度提升优先级,浪费CPU

避坑指南:我曾经在一个项目里用了优先级继承,但忘了处理嵌套锁的情况。结果三个任务互相继承优先级,形成了一个优先级继承链,调度器差点崩溃。后来我改用优先级天花板,虽然保守了点,但系统稳定多了。

3.5 知识体系总览

下面这张图,是我梳理的本章知识结构。你把它存下来,以后做系统设计时拿出来对照着看。

任务优先级与抢占:知识体系 静态优先级 创建时固定,运行不变 实现简单,开销小 ⚠ 饥饿问题 动态优先级 运行时调整优先级 时间片轮转+优先级 优先级老化/截止时间 优先级反转 高优先级被低优先级阻塞 共享资源竞争导致 优先级继承 优先级天花板 核心原则 简单系统用静态优先级,复杂系统用动态优先级 共享资源时务必考虑优先级反转,用继承或天花板方案

嗯,这张图把咱们今天讲的内容串起来了。静态优先级、动态优先级、优先级反转,以及对应的解决方案。你设计系统时,先想清楚你的场景适合哪种策略,再动手写代码。

最后说一句:优先级不是越高越好,也不是越动态越好。适合你的系统,才是最好的。我见过有人把每个任务都设成不同优先级,结果调度器光切换任务就占了30%的CPU。你想想看,这合理吗?


可可精品资料库