一、资源调度概述:嵌入式系统资源类型、调度问题定义、优化目标与约束条件
各位同学,咱们今天聊聊资源调度。说实话,我做了十几年嵌入式,踩过的坑有一半都跟资源调度有关。你想想看,一个系统跑着跑着突然卡死,或者任务莫名其妙丢数据,十有八九是资源没管好。
嵌入式系统不像PC,资源就那么点。CPU就那么几个核,内存就几兆甚至几百K,外设还得排队用。怎么把这些有限的资源分给多个任务,让它们都能按时完成——这就是资源调度的核心问题。
1.1 嵌入式系统的资源类型
我先问大家一个问题:嵌入式系统里到底有哪些资源需要调度?
我个人习惯把资源分成三类:
- 计算资源:CPU时间片、GPU、DSP等。说白了就是谁用处理器、用多久。
- 存储资源:RAM、ROM、Flash、Cache。我记得有个项目,就因为一个任务偷偷多占了2K的栈空间,导致系统在高温下随机崩溃,查了整整两周。
- I/O与通信资源:总线(I2C、SPI、CAN)、DMA通道、中断控制器、外设(ADC、定时器)。这些资源往往有互斥要求,不能同时被两个任务使用。
重点提醒:很多新手只盯着CPU调度,忽略了外设和内存的竞争。我在项目中遇到过,两个任务同时抢一个SPI总线,结果数据全乱了。从那以后,我每个外设都配一个互斥锁。
还有一种资源容易被忽视——电源。电池供电的设备,功耗预算就是硬约束。你调度得再好,电流超了,芯片直接掉电重启。
1.2 调度问题的定义
好,资源类型搞清楚了。那调度问题到底是什么?
用一句话概括:给定一组任务和一组资源,找到一个分配方案,让所有任务在满足约束的前提下,尽可能达到优化目标。
嗯,这里要注意,调度问题有三个要素:
- 任务集:每个任务有执行时间、周期(如果是周期任务)、截止时间、资源需求。
- 资源集:每种资源的容量、访问方式(独占还是共享)、切换开销。
- 调度策略:决定哪个任务在什么时候使用哪个资源。
举个例子。我曾经做过一个汽车ECU项目,有5个周期任务:
| 任务 | 周期(ms) | 执行时间(ms) | 截止时间(ms) | 所需资源 |
|---|---|---|---|---|
| T1 | 10 | 2 | 10 | CPU, CAN总线 |
| T2 | 20 | 4 | 20 | CPU, ADC |
| T3 | 50 | 8 | 50 | CPU, SPI, Flash |
| T4 | 100 | 10 | 100 | CPU, DMA |
| T5 | 200 | 15 | 200 | CPU, I2C |
你看,光看CPU利用率,加起来才(2/10 + 4/20 + 8/50 + 10/100 + 15/200) = 0.2 + 0.2 + 0.16 + 0.1 + 0.075 = 0.735,才73.5%。按理说CPU够用。但问题出在资源冲突上——T1和T3都要用SPI?不对,T1用CAN,T3用SPI,不冲突。但T2和T4都要用DMA?嗯,这里就有问题了。
避坑指南:我曾经犯过一个低级错误——只算了CPU利用率,没算资源冲突。结果T2和T4同时申请DMA通道,T4优先级高抢到了,T2被阻塞,导致ADC采样延迟,整个控制环路失稳。从那以后,我每次做调度分析,都会画一张资源冲突矩阵。
1.3 优化目标
调度不是随便排个序就完事了。你得明确:你想优化什么?
常见的优化目标有这几个:
- 最小化响应时间:任务从就绪到完成的时间越短越好。实时系统最看重这个。
- 最大化吞吐量:单位时间内完成的任务数。数据采集系统常用。
- 最小化功耗:在满足实时性的前提下,尽量省电。电池设备的核心指标。
- 最小化资源浪费:比如CPU空闲时间、内存碎片。说白了就是别让资源闲着。
- 最大化公平性:每个任务都能分到合理的资源,别让低优先级任务饿死。
我个人的经验是:实时系统优先保证截止时间,非实时系统优先保证吞吐量。你不可能同时优化所有目标,得做取舍。
1.4 约束条件
优化目标定好了,还得看约束条件。没有约束的调度,那是理想情况,现实中不存在。
约束条件分几类:
- 时间约束:截止时间、周期、最小执行时间、最大执行时间。比如一个任务必须在10ms内完成,这就是硬约束。
- 资源约束:资源独占(互斥)、资源容量(比如内存池大小)、资源访问顺序(比如必须先获取锁A才能获取锁B)。
- 依赖约束:任务之间有先后顺序。比如T1必须等T2算完才能开始。这就是有向无环图(DAG)调度问题。
- 优先级约束:高优先级任务可以抢占低优先级任务。但要注意优先级反转问题——我有个项目就因为这个,低优先级任务占着锁不放,高优先级任务活活被饿死。
小技巧:遇到优先级反转,可以用优先级继承协议。简单说就是:低优先级任务在持有高优先级任务需要的资源时,临时提升到高优先级。这样就能避免死锁。我当年在RT-Thread里实现过这个,效果不错。
1.5 调度问题的分类
调度问题有很多种,我简单列一下常见的分类方式:
- 按任务特性:周期调度 vs 非周期调度;硬实时 vs 软实时 vs 非实时。
- 按资源数量:单处理器调度 vs 多处理器调度;单资源 vs 多资源。
- 按调度时机:静态调度(编译时确定) vs 动态调度(运行时决策)。
- 按抢占性:可抢占调度 vs 非可抢占调度。
你想想看,实际项目中往往是混合的。比如一个系统里,既有周期任务(传感器采样),又有非周期任务(按键响应)。静态调度保证周期任务的确定性,动态调度处理突发任务。我建议初学者先从单处理器、周期任务、可抢占、静态调度入手,这个模型最经典,也最容易理解。
1.6 一个简单的调度示例
光说不练假把式。咱们看一个最简单的例子:两个任务,一个CPU。
任务A:执行时间3ms,周期10ms,截止时间10ms。
任务B:执行时间2ms,周期5ms,截止时间5ms。
如果用速率单调调度(RMS)——周期越短优先级越高——那么B的优先级高于A。调度顺序是:
时间轴:0 2 3 5 7 8 10 12 13 15 ...
任务B: [--] [--] [--] [--]
任务A: [----] [----] [----]
你看,B每5ms执行一次,A在B的空隙里执行。两个任务都能在截止时间前完成。CPU利用率 = 3/10 + 2/5 = 0.3 + 0.4 = 0.7,70%。
但如果反过来,A优先级高于B,会怎样?
时间轴:0 3 5 8 10 13 15 ...
任务A: [------] [------]
任务B: [--] [--] [--]
B的第一次执行被推迟到3ms之后,虽然还是能在5ms截止时间前完成(3+2=5),但第二次执行被推迟到8ms,而它的截止时间是10ms(第二个周期从5ms开始,截止到10ms),8+2=10,刚好赶上。再往后,B的第三次执行从13ms开始,截止时间是15ms,13+2=15,又是刚好。这种调度叫临界调度——稍微多一点干扰就超时。
结论:RMS在CPU利用率不超过某个上限时(单处理器是ln2 ≈ 69.3%),能保证所有任务满足截止时间。超过这个上限,就得用更复杂的调度算法,或者增加处理器。
好了,这一章就到这里。资源调度是个大话题,后面我们会一步步深入。下一章讲实时调度算法,我会带大家手撕RMS和EDF的代码实现。