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

好,咱们正式开始啃RTOS的硬骨头。

说实话,很多工程师学RTOS,上来就翻源码、看API。结果呢?越看越迷糊。我当年也是这样,折腾了一个月,连任务切换都没搞明白。

后来我悟了——先搞懂这五个核心概念,后面所有东西都是它们的排列组合。哪五个?任务、调度器、临界区、上下文切换、实时内核。咱们一个一个来。

2.1 任务(Task)—— 你写的每一行代码,最终都是任务

任务是什么?说白了,就是一段独立的程序执行流。

在裸机里,你写个 while(1) 循环,那就是一个任务。但在RTOS里,任务是被“管理”起来的。每个任务有自己的栈、优先级、状态。

任务的状态机,你必须刻在脑子里:

  • 就绪态:任务啥都准备好了,就等CPU来执行它
  • 运行态:CPU正在执行这个任务
  • 阻塞态:任务在等某个事件(比如延时、信号量)
  • 挂起态:被其他任务或中断强制暂停

我见过太多新手犯一个错:任务里写死循环,却不加延时。结果低优先级任务永远得不到CPU。嗯,这就是所谓的“饿死”。

// 错误写法:低优先级任务永远没机会跑
void TaskHigh(void *arg) {
    while(1) {
        // 疯狂占用CPU
    }
}

// 正确写法:主动让出CPU
void TaskHigh(void *arg) {
    while(1) {
        do_something();
        vTaskDelay(10);  // 让其他任务喘口气
    }
}

我的习惯:每个任务里,至少留一个 vTaskDelay() 或等待事件的操作。别让任务“霸占”CPU不放。

2.2 调度器(Scheduler)—— 谁该干活,它说了算

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

常见的调度策略就三种:

调度策略 特点 我常用的场景
抢占式调度 高优先级任务随时可以抢CPU 工业控制、实时性要求高的场合
时间片轮转 同优先级任务轮流用CPU 人机界面、多任务均衡的场景
协作式调度 任务主动让出CPU 极简系统、资源受限的MCU

你想想看,抢占式调度就像公司里的“紧急任务”——老板来了,你手头的事就得先放一放。时间片轮转呢,就像大家轮流发言,每人说30秒。

我曾经踩过的坑:在抢占式调度下,两个任务共享一个全局变量,没加保护。结果高优先级任务改了数据,低优先级任务读到一半的数据就变了。嗯,这就是典型的“竞态条件”。

2.3 临界区(Critical Section)—— 不可打断的代码段

临界区,说白了就是一段不允许被中断或任务切换打断的代码

为什么要搞这个?因为有些操作必须是原子的。比如:

  • 修改全局变量
  • 操作硬件寄存器
  • 更新链表结构

RTOS里保护临界区,通常有两种方式:

// 方式一:关中断(最粗暴,但最有效)
taskENTER_CRITICAL();
// 这里操作共享数据,绝对安全
shared_var++;
taskEXIT_CRITICAL();

// 方式二:用互斥量(更优雅,但开销大)
xSemaphoreTake(mutex, portMAX_DELAY);
shared_var++;
xSemaphoreGive(mutex);

我的建议:临界区代码要短,越快越好。别在里面做延时、打印这种耗时操作。否则实时性就崩了。

2.4 上下文切换(Context Switch)—— 换人干活,得保存现场

上下文切换,就是保存当前任务的“现场”,再恢复下一个任务的“现场”

这个“现场”包括:

  • CPU寄存器(R0-R15、PC、LR、PSP等)
  • 栈指针
  • 浮点寄存器(如果有FPU)

我刚开始学的时候,总觉得上下文切换很神秘。后来看了FreeRTOS的汇编代码,发现其实就是:

; 伪代码示意:保存当前任务
PUSH {R4-R11}      ; 保存通用寄存器
MRS R0, PSP        ; 获取进程栈指针
STR R0, [R1]       ; 保存到任务控制块

; 恢复下一个任务
LDR R0, [R1]       ; 从任务控制块加载栈指针
MSR PSP, R0        ; 恢复进程栈指针
POP {R4-R11}       ; 恢复通用寄存器
BX LR              ; 跳转执行

说白了,就是把当前任务的“快照”存起来,再把下一个任务的“快照”恢复出来。这个过程通常由硬件(PendSV异常)和软件配合完成。

关键点:上下文切换是有开销的。每次切换大概几十到几百微秒。任务切得太频繁,CPU全花在切换上了,实际干活的时间就少了。

2.5 实时内核(Real-Time Kernel)—— 所有概念的集合体

实时内核,就是上面所有东西的集合体。它提供:

  • 任务管理(创建、删除、挂起、恢复)
  • 调度器(决定谁运行)
  • 同步机制(信号量、互斥量、事件组)
  • 内存管理(动态分配、静态分配)
  • 时钟管理(延时、超时)

市面上常见的实时内核有:

内核 特点 适合场景
FreeRTOS 开源、轻量、资料多 绝大多数MCU项目
RT-Thread 国产、组件丰富 IoT、带网络功能的设备
uC/OS-III 商业级、稳定 航空航天、医疗设备
Zephyr Linux基金会、支持蓝牙 可穿戴、无线设备

我曾经犯过的错:选型时只看功能列表,没考虑ROM/RAM占用。结果一个简单的传感器项目,FreeRTOS占了8KB RAM,MCU总共才16KB。嗯,后来换成了极简的RTOS。

2.6 小结——这五个概念,串起来就是RTOS

咱们捋一下:

  1. 任务是干活的人
  2. 调度器是排班经理
  3. 临界区是会议室,一次只进一个人
  4. 上下文切换是换人干活时的交接手续
  5. 实时内核就是整个公司的管理体系

你想想看,理解了这五个概念,RTOS就不再是黑盒了。后面咱们讲信号量、消息队列、中断管理,都是在这五个基础上搭积木。

下一章,咱们聊聊任务创建与删除——怎么让任务“生”出来,又怎么让任务“死”得干净。嗯,这里有不少坑等着你。