2. RTOS基础概念:任务、调度器、临界区、上下文切换、实时性指标
好,咱们正式开始啃RTOS的核心概念。说实话,很多初学者一上来就被这些术语吓住了。其实没那么玄乎,你把它想象成一个公司运作,就全明白了。
2.1 任务(Task)—— 干活的人
任务是什么?说白了,就是一段独立执行的代码。在裸机编程里,我们只有一个main函数在那死循环。但在RTOS里,我们可以创建多个任务,让它们看起来像“同时”在跑。
每个任务都有自己的栈空间、优先级和状态。我习惯把任务想象成公司里的员工:有的负责前台接待(低优先级),有的负责处理紧急订单(高优先级)。
任务的状态机:一个任务在生命周期里会经历这些状态:
- 就绪(Ready):万事俱备,只等CPU
- 运行(Running):正在占用CPU干活
- 阻塞(Blocked):等某个事件,比如等延时、等信号量
- 挂起(Suspended):被强制暂停
我在做灌溉控制器时遇到过一个问题:有个任务负责读取土壤湿度传感器,另一个任务负责控制电磁阀。如果湿度读取任务被阻塞了,阀门控制任务就得干等着。嗯,这里要注意——任务之间的依赖关系一定要理清楚。
3.2 调度器(Scheduler)—— 项目经理
调度器是RTOS的大脑。它决定“下一个该谁干活”。常见的调度策略有两种:
| 调度策略 | 特点 | 适用场景 |
|---|---|---|
| 抢占式调度 | 高优先级任务可以打断低优先级任务 | 实时性要求高的系统 |
| 时间片轮转 | 同优先级任务轮流使用CPU | 多个同等重要的任务 |
我个人更偏爱抢占式调度。为什么?因为灌溉控制器里,水位报警任务必须第一时间响应,不能等阀门控制任务慢慢悠悠跑完。你想想看,如果水都漫出来了,还在那处理温度数据,那不就出大事了?
我的经验:在FreeRTOS里配置调度策略很简单,在FreeRTOSConfig.h里设置configUSE_PREEMPTION为1就行。但记住,优先级别设太多,一般8-16级就够了。我曾经见过有人设了32级,结果自己都搞不清哪个任务该跑哪个优先级。
2.3 临界区(Critical Section)—— 会议室
临界区这个概念,说白了就是“这段代码一次只能一个人进”。为什么需要它?因为多个任务可能会访问同一个全局变量或硬件寄存器。
举个例子:灌溉控制器里有个全局变量记录“今日总灌溉量”。任务A在加水,任务B在记录数据。如果两个任务同时修改这个变量,数据就乱了。
我曾经踩过的坑:有一次在临界区里调用了延时函数,结果整个系统卡死了。后来才明白——临界区里不能做阻塞操作!因为临界区通常会关中断,你延时的时候中断进不来,系统就死在那了。
保护临界区的常用方法:
- 关中断:最简单粗暴,但会影响实时性
- 使用互斥量(Mutex):更优雅,但要注意优先级反转
- 使用信号量(Semaphore):适合资源计数场景
2.4 上下文切换(Context Switch)—— 换人干活
上下文切换,就是CPU从一个任务切换到另一个任务的过程。它要保存当前任务的“现场”(寄存器、栈指针等),再恢复下一个任务的“现场”。
这个过程是RTOS的核心开销。切换一次大概需要多少时间?嗯,在Cortex-M3上,FreeRTOS的切换时间大约在1-3微秒。听起来很快对吧?但如果你的系统里每秒切换几千次,这个开销就不可忽视了。
上下文切换的代价:
- 保存/恢复CPU寄存器
- 更新任务控制块(TCB)
- 执行调度算法
- 切换栈指针
我建议:任务切换频率别太高,一般10-100ms切换一次就够了。灌溉控制这种慢速系统,100ms切换一次完全够用。
2.5 实时性指标—— 到底多快才算快?
实时性不是“快”,而是“可预测”。一个实时系统必须保证:在规定的截止时间之前完成任务。
衡量实时性的几个关键指标:
| 指标 | 说明 | 灌溉场景要求 |
|---|---|---|
| 响应时间 | 从事件发生到任务开始处理的时间 | 水位报警:<10ms |
| 截止时间 | 任务必须完成的最晚时间 | 阀门控制:<50ms |
| 抖动(Jitter) | 响应时间的波动范围 | 传感器采样:<5ms |
| 吞吐量 | 单位时间内处理的任务数 | 数据记录:>10条/秒 |
你可能会问:“这些指标怎么测?”我一般用逻辑分析仪抓GPIO引脚的电平变化。在任务入口和出口各翻转一次引脚,一看波形就全明白了。
避坑指南:我曾经在一个项目里,把所有的任务优先级都设成了相同值。结果系统响应时间忽快忽慢,查了两天才发现是时间片轮转导致的抖动。记住:关键任务一定要给高优先级,别偷懒。
好了,这一章的核心概念就这些。任务、调度器、临界区、上下文切换、实时性指标——这五个概念是RTOS的基石。你把这些搞明白了,后面移植和优化就顺风顺水了。