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_time 和 start_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可能会变,周期可能会调。所以,我建议在代码里留一个参数监控接口,运行时能实时查看任务的实际执行情况。别等到系统崩了才去翻日志。
好,这一章就到这儿。任务模型搞清楚了,后面讲调度算法你才能听懂。下一章咱们聊可调度性分析——怎么判断你的任务集能不能跑得起来。