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
咱们捋一下:
- 任务是干活的人
- 调度器是排班经理
- 临界区是会议室,一次只进一个人
- 上下文切换是换人干活时的交接手续
- 实时内核就是整个公司的管理体系
你想想看,理解了这五个概念,RTOS就不再是黑盒了。后面咱们讲信号量、消息队列、中断管理,都是在这五个基础上搭积木。
下一章,咱们聊聊任务创建与删除——怎么让任务“生”出来,又怎么让任务“死”得干净。嗯,这里有不少坑等着你。