1. 抢占式调度概述

大家好,我是你们的讲师。今天咱们来聊聊RTOS里最核心的一个概念——抢占式调度。说实话,我做了十几年嵌入式开发,见过太多因为调度策略选错而翻车的项目。嗯,咱们先从最基础的说起。

什么是抢占式调度?

抢占式调度,说白了就是:高优先级的任务可以随时打断低优先级任务的执行。你想想看,就像你在写代码时,老板突然过来让你处理紧急bug——你得立刻停下手头的工作,先搞定bug再说。

在RTOS里,每个任务都有一个优先级。调度器会根据优先级决定谁先跑。抢占式调度意味着:

  • 任务A正在运行,优先级是3
  • 任务B就绪了,优先级是5(更高)
  • 调度器立刻暂停任务A,切换到任务B
  • 任务B跑完或阻塞后,再回来继续任务A

关键点:这种切换不是任务主动让出CPU,而是调度器强制执行的。这就是“抢占”二字的含义。

与协作式调度的对比

协作式调度就完全不一样了。我记得刚入行时,第一个项目用的就是协作式调度。那时候觉得挺简单,后来才发现坑不少。

特性 抢占式调度 协作式调度
任务切换时机 任意时刻(由调度器决定) 任务主动让出CPU时
实时性 高,可预测 低,依赖任务自觉
代码复杂度 较高(需处理临界区) 较低
资源开销 需要定时器中断支持 无需额外硬件
典型应用 工业控制、汽车电子 简单嵌入式系统

协作式调度有个致命问题:如果某个任务不主动让出CPU,整个系统就卡死了。我曾经在一个物联网项目里遇到过——某个传感器驱动里有个死循环,结果所有任务都跑不了。排查了整整两天才找到问题。

避坑指南:如果你用协作式调度,一定要确保每个任务里没有长时间阻塞的操作。我曾经见过有人把延时函数写在协作式任务里,结果整个系统响应延迟从1ms变成了500ms。

抢占式调度的核心优势

为什么现在绝大多数RTOS都用抢占式调度?说白了,就三个字:实时性

具体来说:

  1. 响应时间可预测:高优先级任务能在确定时间内得到响应。比如你按下急停按钮,系统必须在10ms内做出反应。抢占式调度能保证这一点。
  2. CPU利用率高:低优先级任务可以利用空闲时间跑,高优先级任务随时可以打断。不像协作式,低优先级任务跑着,高优先级任务只能干等。
  3. 系统设计更灵活:你可以把紧急任务设高优先级,普通任务设低优先级。调度器自动帮你管理。

个人经验:我在做电机控制项目时,电流环必须每100μs执行一次。如果用协作式调度,其他任务稍微拖一下,电流环就超时了。换成抢占式后,电流环设最高优先级,其他任务随便跑,完全不影响。

你可能会问:抢占式调度有没有缺点?当然有。最大的问题是资源共享的复杂性。因为任务随时可能被中断,访问共享数据时必须加锁或关中断。否则就会出现数据不一致的问题。

举个例子:

// 两个任务共享一个全局变量
int shared_data = 0;

// 任务A:写入数据
void task_A(void) {
    shared_data = 100;  // 刚执行完这行,被任务B抢占了
    // 此时shared_data = 100,但任务B不知道
}

// 任务B:读取数据
void task_B(void) {
    if (shared_data == 100) {
        // 执行某些操作
    }
}

嗯,这里要注意。如果任务A刚写完shared_data就被任务B抢占了,任务B读到的值是100。但如果任务A只写了一半呢?比如shared_data是个32位变量,而CPU是16位的——那就可能读到一半新值一半旧值。这就是典型的竞态条件

所以,抢占式调度虽然强大,但需要开发者对临界区保护有清晰的认识。我建议初学者先从简单的协作式开始理解调度原理,再逐步过渡到抢占式。

好了,这一章就讲到这里。下一章咱们深入看看抢占式调度的具体实现——任务控制块和就绪队列是怎么设计的。