3. 实时软件架构核心概念
在裸机前后台程序里,代码的执行顺序基本由主循环和中断决定,程序员对“谁先跑、跑多久”有直观感受。一旦引入 RTOS,执行流被拆成多个任务,由内核统一调度,很多初学者会突然失去这种直观感:任务明明写好了,为什么响应会变慢?为什么优先级更高的任务反而没有立刻执行?要回答这些问题,必须把六个核心概念串成一条完整的因果链:任务是被调度的对象,调度决定谁获得 CPU,优先级是调度的依据,抢占是优先级生效的方式,上下文切换是切换任务时付出的代价,而实时性指标是衡量这一切是否达标的尺子。
任务:实时系统的基本执行单元
在 RTOS 中,任务(Task)是一个独立的、可被内核调度的执行流,它拥有自己的栈空间、程序计数器和一组寄存器现场。从代码角度看,任务通常是一个永不返回的循环函数,例如:
void vSensorTask(void *pvParameters)
{
for (;;) {
/* 等待采样周期到达 */
vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(10));
/* 读取 ADC 并写入队列 */
adc_value = Read_ADC();
xQueueSend(xAdcQueue, &adc_value, 0);
}
}
任务与普通函数的本质区别在于:函数是“被调用”的,任务是被“调度”的。任务一旦创建,就进入内核的就绪队列,等待调度器分配 CPU。任务通常分为周期任务(如每 10 ms 采样一次)和事件驱动任务(如收到串口数据后处理),前者由时间驱动,后者由中断或信号量唤醒。
调度与优先级:决定谁先运行
调度器(Scheduler)是内核的核心,它在每次需要重新决定执行流时,从就绪任务中选出一个来运行。选择依据就是优先级。在 STM32 常用的 FreeRTOS 中,优先级数值越大表示优先级越高,例如 configMAX_PRIORITIES 设为 7 时,可用优先级为 0~6,其中 0 是最低优先级,通常留给空闲任务。
调度策略分为可抢占式和不可抢占式。可抢占式调度下,只要有一个更高优先级任务进入就绪态,内核会立即暂停当前任务,转去执行高优先级任务;不可抢占式调度则要等当前任务主动让出 CPU。实时系统几乎都采用可抢占式,因为只有这样才能保证高优先级任务的响应时间可控。
抢占与上下文切换:优先级如何真正生效
抢占(Preemption)是优先级生效的机制。假设系统中有三个任务:
- Task_H:优先级 5,处理紧急故障信号;
- Task_M:优先级 3,周期 10 ms 的控制计算;
- Task_L:优先级 1,负责日志写入。
当 Task_L 正在执行时,如果中断服务程序释放了一个信号量,使 Task_H 进入就绪态,调度器会立刻触发一次上下文切换:保存 Task_L 的寄存器现场到它的栈中,再从 Task_H 的栈中恢复现场,跳转到 Task_H 继续执行。这个过程就是上下文切换(Context Switch)。
上下文切换不是免费的。在 Cortex-M 上,一次切换通常需要保存/恢复 R0-R3、R12、LR、PC、xPSR 以及可能用到的 R4-R11,加上内核调度逻辑,典型开销在几微秒量级。如果切换过于频繁,CPU 大量时间花在“换人”而不是“干活”上,系统有效算力会明显下降。因此任务数量和切换频率都需要控制。
上图展示了一个典型的抢占过程:Task_L 正在运行,中断释放信号量使 Task_H 就绪,内核立即保存 Task_L 现场并切换到 Task_H;Task_H 执行完毕后,Task_L 恢复;随后 Task_M 周期到达,又抢占 Task_L。整个过程体现了“高优先级就绪即抢占”的原则。
实时性指标:响应时间、抖动与截止时间
实时系统的正确性不仅取决于计算结果对不对,还取决于结果是否在允许的时间内给出。三个最关键的指标是:
- 响应时间(Response Time):从事件发生到任务开始执行(或完成)的时间。例如中断触发到 Task_H 第一条指令执行,若为 8 μs,则响应时间为 8 μs。
- 抖动(Jitter):同一周期任务多次执行时,响应时间或执行起点的波动范围。若 Task_M 周期为 10 ms,实际启动时刻在 10.0~10.3 ms 之间波动,则抖动为 0.3 ms。抖动越小,控制系统的采样节拍越稳定。
- 截止时间(Deadline):任务必须完成的最晚时刻。硬实时系统中,超过截止时间即视为失败,可能造成安全事故;软实时系统中,偶尔超时只降低服务质量。
以一个电机控制任务为例:PWM 中断每 100 μs 触发一次,控制任务必须在 80 μs 内完成电流环计算并更新占空比,否则下一次中断到来时数据已过时。这里 80 μs 就是截止时间,响应时间必须小于它,且抖动要足够小,才能保证控制稳定。
把概念落到设计上
理解了这条因果链,任务设计就有了明确依据:先根据事件的时间要求确定截止时间,再据此分配优先级——截止时间越短、越紧急的任务优先级越高;然后估算每个任务的 WCET 和切换开销,检查最坏情况下是否仍能满足截止时间;最后通过测量响应时间和抖动来验证设计。优先级分配不是拍脑袋,而是从实时性指标反推出来的工程决策。