2. RTOS基础概念:任务、调度器、临界区、上下文切换

好,咱们正式开始啃RTOS的核心概念。说实话,我刚入行那会儿,觉得RTOS就是个“高级while循环”,后来被现实狠狠教育了一顿。今天我把这四个最基础也最重要的概念掰开揉碎了讲,你吃透了它们,后面移植工作就顺了。

2.1 任务(Task)—— RTOS里的“独立线程”

任务是什么?说白了,就是一段独立的程序执行流。每个任务都有自己的栈空间、优先级和状态。我习惯把任务想象成一个“小工人”,各自干各自的活,互不干扰。

在RTOS里,任务通常是个无限循环的函数:

void myTask(void *param) {
    while (1) {
        // 干活
        vTaskDelay(100);  // 让出CPU
    }
}

嗯,这里要注意:任务函数不能返回。一旦返回,RTOS会认为任务结束了,会把它清理掉。我在项目中遇到过有人把任务写成普通函数,执行完就return了,结果系统直接崩了——因为RTOS试图切换到一个已经不存在的任务。

任务的状态机:每个任务在运行中会经历四种状态——就绪、运行、阻塞、挂起。你想想看,这就像你排队买奶茶:

  • 就绪:排着队,等叫号
  • 运行:正在点单
  • 阻塞:等奶茶做好(等资源)
  • 挂起:暂时不买了(被暂停)

2.2 调度器(Scheduler)—— 谁干活,谁歇着

调度器是RTOS的大脑。它的工作就是决定:此时此刻,哪个任务该跑在CPU上。

常见的调度策略有两种:

调度策略 特点 适用场景
抢占式调度 高优先级任务随时可以抢CPU 火控系统这种实时性要求高的
时间片轮转 同优先级任务轮流跑固定时间 普通嵌入式,多个任务平等

我个人习惯在火控系统里用抢占式调度。为什么?因为导弹来了,你不能让“显示温度”的任务占着CPU,而“计算弹道”的任务干等着。优先级高的任务必须立刻抢到CPU。

避坑指南:我曾经在调度器里犯过一个低级错误——把两个关键任务的优先级设成一样。结果它们互相抢CPU,导致火控响应延迟了200微秒。200微秒在火控系统里,可能就是生与死的区别。记住:关键任务优先级一定要唯一。

2.3 临界区(Critical Section)—— 别让数据乱掉

临界区,说白了就是一段“不允许被打断”的代码。比如你在更新一个全局变量,突然被高优先级任务抢走了CPU,等回来时数据已经乱了。

RTOS里保护临界区最常用的方法是关中断:

taskENTER_CRITICAL();
// 这里操作共享数据,不会被中断打断
sharedVar++;
taskEXIT_CRITICAL();

嗯,这里有个坑:临界区里不要做耗时操作。我见过有人把整个传感器读取放在临界区里,结果中断被关了100毫秒,系统直接超时。临界区应该短小精悍,只保护真正需要保护的那几行代码。

警告:临界区嵌套要小心。有些RTOS支持嵌套,但你必须保证enter和exit成对出现。我曾经调试过一个bug,就是因为enter了两次但只exit了一次,导致中断永远关着——整个系统像死了一样。

2.4 上下文切换(Context Switch)—— 换人干活

上下文切换,就是RTOS把当前任务的状态保存起来,再恢复另一个任务的状态。这个过程包括:保存寄存器、栈指针、状态字等。

你想想看,这就像你正在写代码,突然被叫去开会。你得保存好当前编辑的文件(保存上下文),开完会再恢复回来(恢复上下文)。

上下文切换的典型流程:

  1. 触发切换(定时器中断或任务主动让出)
  2. 保存当前任务的寄存器到它的栈里
  3. 找到下一个要运行的任务
  4. 从它的栈里恢复寄存器
  5. 跳转到新任务的PC指针

我在移植RTOS时,最头疼的就是上下文切换的汇编代码。不同CPU架构的寄存器布局不一样,你得对着芯片手册一行一行写。我记得有一次在ARM Cortex-M上移植,因为没处理好浮点寄存器的保存,导致任务切换后浮点运算全错了——那个bug我调了整整两天。

上下文切换的代价:每次切换大概需要几十到几百微秒。如果你的系统有100个任务,每秒切换1000次,那光切换就占用了10%的CPU。所以任务数量不是越多越好,够用就行。

2.5 四个概念的关系

这四个概念其实是环环相扣的:

  • 任务是干活的主体
  • 调度器决定谁干活
  • 临界区保护干活时的数据
  • 上下文切换是换人干活的具体操作

说白了,RTOS就是一套“多任务管理+资源保护”的框架。你理解了这四个概念,后面看RTOS的源码就会轻松很多。下一章咱们开始讲RTOS的启动流程,到时候你会看到这些概念是怎么串起来的。

个人建议:初学者可以先在PC上模拟跑一个RTOS,比如FreeRTOS的Windows模拟版。把任务创建、调度、临界区都跑一遍,比光看书强十倍。我当年就是这么入门的。