2、RTOS基础概念:任务、调度器、临界区、上下文切换、实时性指标
好,咱们正式开始啃RTOS的核心概念。说实话,很多初学者一上来就被这些术语吓住了。什么任务啊、调度啊、临界区啊……听着挺唬人。但你别急,我带你一个一个拆开看,其实没那么玄乎。
2.1 任务(Task)—— RTOS里的“独立线程”
任务,说白了就是一段独立执行的代码。在裸机编程里,我们通常写一个超级循环:
void main() {
while(1) {
read_sensor();
update_display();
check_communication();
}
}
但有了RTOS,每个功能可以拆成独立的任务。每个任务有自己的栈空间、优先级和状态。我习惯把任务想象成“一个能独立干活的小人”。
任务通常有四种状态:
- 运行态——CPU正在执行这个任务
- 就绪态——任务准备好了,等CPU来执行
- 阻塞态——任务在等某个事件(比如延时、信号量)
- 挂起态——任务被暂停了,不参与调度
2.2 调度器(Scheduler)—— 谁来决定下一个跑谁?
调度器是RTOS的大脑。它决定哪个任务该获得CPU的使用权。常见的调度策略有两种:
| 调度策略 | 特点 | 适用场景 |
|---|---|---|
| 优先级抢占式 | 高优先级任务随时可以打断低优先级任务 | 实时性要求高的系统 |
| 时间片轮转 | 同等优先级任务轮流执行固定时间片 | 多个同等重要任务 |
我个人更偏爱优先级抢占式调度。为什么?因为HUD系统里,显示刷新必须优先于日志记录。你想想看,如果日志任务占着CPU不放,显示画面卡了,那用户体验得多糟糕。
2.3 临界区(Critical Section)—— 共享资源的“安全屋”
临界区,说白了就是一段不允许被打断的代码。当多个任务访问同一个全局变量或硬件寄存器时,就可能出问题。
举个例子:
// 任务A
counter++;
// 任务B
counter--;
如果任务A读到counter=5,正要加1,突然被任务B抢占了。任务B把counter减到4。然后任务A继续执行,把4加1变成5。你看,本来应该是5+1-1=5,结果变成了4。这就是典型的“竞态条件”。
解决办法就是加临界区保护:
taskENTER_CRITICAL();
counter++;
taskEXIT_CRITICAL();
嗯,这里要注意:临界区里不要做耗时操作。我见过有人把延时函数放在临界区里,结果整个系统卡死了。那叫一个惨。
2.4 上下文切换(Context Switch)—— 任务的“换人”过程
上下文切换,就是CPU从一个任务切换到另一个任务时,保存和恢复现场的过程。保存的内容包括:
- CPU寄存器(R0-R15、PC、LR等)
- 栈指针(SP)
- 浮点寄存器(如果有FPU)
每次切换都有开销。我记得在Cortex-M3上,一次上下文切换大约需要几十到几百个时钟周期。听起来不多,但如果每秒切换几千次,那CPU时间就浪费了不少。
2.5 实时性指标—— 怎么衡量系统“快不快”?
实时性不是“快”,而是“可预测”。HUD系统里,显示刷新必须在16.67ms内完成(60Hz刷新率)。如果偶尔超时,画面就会撕裂。
常用的实时性指标有:
| 指标 | 含义 | 典型值 |
|---|---|---|
| 响应时间 | 从事件发生到任务开始处理的时间 | <1ms |
| 截止时间 | 任务必须完成的最晚时间 | 16.67ms(60fps) |
| 抖动 | 响应时间的波动范围 | <100μs |
| 吞吐量 | 单位时间内处理的任务数 | 视场景而定 |
我曾经遇到一个坑:系统平时跑得好好的,但一旦开启Wi-Fi,显示任务就开始抖动。后来发现是Wi-Fi中断优先级太高,频繁打断显示任务。解决办法是把显示任务的中断优先级调高,同时给Wi-Fi中断加一个“可延迟处理”的机制。
好了,这一章的内容就这些。任务、调度器、临界区、上下文切换、实时性指标——这几个概念是RTOS的基石。你理解了它们,后面学信号量、消息队列、中断管理就会轻松很多。下一章我们聊聊任务间通信,那可是多任务协作的关键。