4、任务调度机制:抢占式调度、时间片轮转调度、调度点与上下文切换、空闲任务

各位同学,今天我们来聊聊RTOS的核心——任务调度。说白了,调度就是决定「下一个该谁跑」的问题。我做了这么多年嵌入式,见过太多因为调度没搞明白导致系统崩溃的案例。嗯,咱们一步步来拆解。

4.1 抢占式调度:谁急谁先跑

抢占式调度,是现在绝大多数RTOS采用的方式。它的核心思想很简单:高优先级的任务可以打断低优先级任务的执行

举个例子。你在写代码,突然领导说「这个bug今天必须修完」。你肯定放下手头的事,先处理这个紧急任务。这就是抢占。

抢占式调度的关键特征:

  • 每个任务都有固定优先级
  • 高优先级任务就绪时,立即获得CPU
  • 低优先级任务被强制挂起,等下次轮到它

我在项目中遇到过一个问题:一个数据采集任务优先级设得太高,结果它一直占着CPU,低优先级的显示任务永远得不到执行。屏幕直接卡死。后来我把采集任务优先级降了一级,加了适当的延时,问题就解决了。

避坑指南:我曾经见过有人把所有任务都设成同一个优先级,那抢占式调度就退化成轮询了。你想想看,那还不如用裸机呢。

4.2 时间片轮转调度:大家轮流来

时间片轮转,说白了就是「一人玩一会儿」。每个任务分到一个固定长度的时间片,时间到了就换下一个。

这种调度方式适合同等优先级的任务。比如你有三个传感器数据采集任务,重要性一样,那就让它们轮流跑。

调度方式 适用场景 缺点
抢占式 任务优先级差异大 优先级反转问题
时间片轮转 同优先级任务 实时性不如抢占式

我个人的习惯是:关键任务用抢占式,普通任务用时间片轮转。比如电机控制用抢占式,日志打印用时间片轮转。这样既保证了实时性,又不会让某个任务饿死。

小技巧:时间片长度一般设为1-10ms。太短了切换开销大,太长了响应不及时。我一般用5ms,大多数场景都够用。

4.3 调度点与上下文切换:切换的时机

调度不是随时都能发生的。系统只在特定的「调度点」才会检查是否需要切换任务。常见的调度点有:

  • 任务主动挂起:调用delay、pend等API时
  • 中断返回前:中断处理完后,检查是否有更高优先级任务就绪
  • 时间片用完:SysTick中断触发时间片轮转
  • 任务创建/删除:任务状态变化时

上下文切换,就是保存当前任务的「现场」,恢复下一个任务的「现场」。这个现场包括:

// 上下文切换保存的内容(以ARM Cortex-M为例)
- R0-R12 通用寄存器
- PC 程序计数器
- LR 链接寄存器
- PSP 进程栈指针
- 浮点寄存器(如果启用FPU)

为什么会这样?因为CPU只有一个,但任务有很多个。切换时如果不保存现场,回来时数据就全乱了。

我记得有一次调试一个bug,任务A跑着跑着突然跳到任务B去了。查了两天才发现,是中断里触发了调度,但上下文保存不完整。从那以后,我每次写中断服务程序都会检查:这个中断会不会触发调度?保存完整了吗?

注意:上下文切换是有开销的。每次切换大约需要几十到几百微秒。如果你的任务只跑1ms就切换,那切换开销占比就太大了。我建议任务运行时间至少是切换时间的10倍以上。

4.4 空闲任务:系统最后的守护者

当所有任务都处于阻塞或挂起状态时,谁来跑?答案是空闲任务

空闲任务是系统自动创建的,优先级最低。它平时就干两件事:

  • 统计CPU使用率:空闲任务跑的时间越多,说明CPU越闲
  • 执行低功耗模式:让CPU进入睡眠,省电

你想想看,如果没有空闲任务,系统就不知道该跑谁了。那就会进入一种「空转」状态,白白浪费电能。

我在做物联网终端时,电池供电的设备特别看重功耗。我在空闲任务里加了WFI指令,让CPU在无事可做时进入深度睡眠。实测下来,功耗从50mA降到了5μA。效果非常明显。

空闲任务的典型实现(伪代码):

void idle_task(void *arg) {
    while(1) {
        // 统计CPU空闲时间
        cpu_idle_count++;
        
        // 进入低功耗模式
        __WFI();  // Wait For Interrupt
    }
}

避坑指南:我曾经在空闲任务里加了打印日志的代码,结果系统功耗暴增。因为每次空闲都要打印,CPU根本睡不了。记住:空闲任务里不要做任何耗时操作,它就该安安静静地待着。

4.5 三种调度方式的对比

特性 抢占式 时间片轮转 空闲任务
优先级 支持 同优先级 最低
实时性
公平性 -
典型应用 电机控制、中断响应 数据采集、界面刷新 功耗管理、统计

嗯,到这里任务调度机制就讲完了。我个人建议:先搞清楚抢占式调度,这是RTOS的基石。时间片轮转和空闲任务都是锦上添花的东西。你只要把抢占式调度玩明白了,其他自然就通了。

下一章我们聊聊任务同步与通信,那是另一个容易踩坑的地方。


公众号:蓝海资料掘金营,微信deep3321