2. 任务模型与参数:任务周期、执行时间、截止时间、释放时间、优先级、关键性

好,咱们进入第二章。这一章讲的是任务模型,说白了就是——你要调度的对象长什么样?

我见过不少刚入行的朋友,一上来就研究调度算法,结果连任务参数都没搞清楚。嗯,这就像你连菜都没洗就开始炒菜,肯定翻车。所以咱们先把任务模型掰扯清楚。

2.1 任务周期(Period)

任务周期,就是任务每隔多久被激活一次。比如一个传感器采集任务,每10ms跑一次,那它的周期就是10ms。

我个人习惯用 T 表示周期。在实时系统里,周期任务是最常见的。你想想看,大部分嵌入式系统都是循环干活的——读传感器、算数据、发指令,周而复始。

关键点:周期任务必须满足 T_i = 常数。如果周期变了,那就不是周期任务,而是零星任务(Sporadic Task)。

我在项目中遇到过一个问题:有个电机控制任务,周期设成了10ms,但实际执行时因为中断干扰,有时候9.8ms,有时候10.2ms。结果电机抖得跟筛子似的。后来我加了周期抖动检测,才把问题压住。

2.2 执行时间(Execution Time)

执行时间,就是任务跑完一次需要多少CPU时间。注意,我说的是CPU时间,不是墙上时间。墙上时间可能包含被抢占、被中断的时间。

执行时间通常用 C 表示。它有三个值:

  • 最坏情况执行时间(WCET)——这是调度分析用的核心参数
  • 最好情况执行时间(BCET)——一般用不上
  • 平均执行时间(ACET)——性能评估时参考

注意:千万不要用平均执行时间做硬实时调度分析!我曾经见过一个团队,用平均执行时间算出来的调度方案,上线第一天就崩了。因为最坏情况一来,任务全超时。

WCET怎么测?我建议用静态分析工具,或者用逻辑分析仪抓实际运行波形。别光靠估算,你想想看,分支预测、缓存命中、DMA抢占,这些因素都能让执行时间翻倍。

2.3 截止时间(Deadline)

截止时间,就是任务必须在什么时间之前完成。用 D 表示。

这里有个常见的坑:截止时间和周期不一定相等。很多人默认 D = T,但实际项目中,截止时间可能比周期短,也可能比周期长。

关系 含义 例子
D = T 截止时间等于周期 最常见的模型
D < T 截止时间小于周期 传感器必须在下一帧数据到来前处理完
D > T 截止时间大于周期 少见,但存在(比如某些批处理任务)

我个人习惯把截止时间分为两种:

  • 硬截止时间——错过就是系统故障
  • 软截止时间——错过可以接受,但质量下降

2.4 释放时间(Release Time)

释放时间,就是任务什么时候可以开始执行。用 r 表示。

为什么要有释放时间?因为任务不是一创建就能跑的。比如:

  • 任务A依赖传感器数据,数据没到就不能跑
  • 任务B需要等DMA传输完成
  • 任务C被某个硬件事件触发

释放时间和就绪时间(Ready Time)不一样。释放时间是「可以开始」的时间,就绪时间是「真正开始」的时间。中间可能差一个调度延迟。

小技巧:在代码里,我习惯用 release_timestart_time 两个变量区分。别混用,否则调试的时候你会疯掉。

2.5 优先级(Priority)

优先级,就是任务在资源竞争时的「话语权」。优先级高的任务先执行。

优先级分两种:

  • 静态优先级——系统设计时就定死了,运行中不变
  • 动态优先级——运行时可以调整(比如EDF算法)

我曾经在一个项目中,把优先级设成了1到255,结果发现优先级反转问题搞得一塌糊涂。后来我学乖了:优先级层级越少越好,3到5级就够用。

经验之谈:优先级不是越高越好。优先级太高,低优先级任务可能永远得不到CPU,这叫「饥饿」。我见过一个系统,把看门狗任务优先级设成最高,结果其他任务全饿死了,系统反而更不稳定。

2.6 关键性(Criticality)

关键性,是任务对系统安全的重要程度。注意,关键性和优先级不是一回事。

优先级解决的是「谁先跑」,关键性解决的是「谁不能挂」。举个例子:

  • 安全气囊触发任务——关键性极高,但优先级可能不高(因为不常触发)
  • UI刷新任务——关键性低,但优先级可能高(为了用户体验)

在混合关键性系统(Mixed-Criticality System)里,关键性决定了:

  • 任务是否需要冗余备份
  • 任务是否需要独立的内存保护
  • 任务超时后的处理策略

重要提醒:不要把关键性和优先级混为一谈。我见过一个项目,把关键性最高的任务设成了最高优先级,结果这个任务频繁触发,把其他关键任务全堵死了。关键性应该影响的是「容错策略」,而不是「调度顺序」。

2.7 任务参数之间的关系

这几个参数不是孤立的。它们之间有个核心约束:

对于任何任务 i:
  C_i ≤ D_i ≤ T_i   (在 D ≤ T 的情况下)
  且 r_i + C_i ≤ d_i  (任务必须在截止时间前完成)

这个约束不满足,调度就是空谈。你想想看,如果执行时间比截止时间还长,那不管用什么调度算法,任务都会超时。

我个人习惯在系统设计阶段,先列一个任务参数表:

任务名 T (ms) C (ms) D (ms) 优先级 关键性
传感器采集 10 2 8
控制算法 20 5 18 最高
日志记录 100 1 90

这张表一出来,调度分析就有底了。下一章我们会用这张表做可调度性分析。

最后说一句:任务参数不是一成不变的。系统运行过程中,WCET可能会变,周期可能会调。所以,我建议在代码里留一个参数监控接口,运行时能实时查看任务的实际执行情况。别等到系统崩了才去翻日志。

好,这一章就到这儿。任务模型搞清楚了,后面讲调度算法你才能听懂。下一章咱们聊可调度性分析——怎么判断你的任务集能不能跑得起来。