第3章:优先级的基本概念
各位工程师朋友,咱们今天聊聊RTOS里最基础、也最容易踩坑的一个概念——优先级。
说实话,我见过太多人在优先级上翻车了。有一次,一个同事调试了三天,最后发现是优先级数值搞反了。嗯,这种事其实挺常见的。
3.1 静态优先级 vs 动态优先级
先说说两种优先级模式。静态优先级,就是任务创建时定死的,运行期间不能改。动态优先级呢,系统运行时可以调整。
我个人习惯,简单场景用静态优先级。为什么?因为好理解,好调试。你想想看,一个任务从生到死优先级不变,逻辑上多清晰。
但有些场景,静态优先级搞不定。比如一个低优先级任务偶尔需要紧急处理数据,这时候动态调整就派上用场了。
关键区别:
- 静态优先级:任务创建时设定,运行期间固定不变
- 动态优先级:系统运行时可以调整,常见于优先级继承、优先级反转处理等场景
我在项目中遇到过一个问题:一个数据采集任务,平时优先级很低,但一旦缓冲区快满了,必须立刻提升优先级去处理。这就是典型的动态优先级应用。
3.2 优先级数值的对应关系
这里有个大坑,我必须重点说。
不同RTOS对优先级数值的定义完全相反。有的系统数值越小优先级越高,有的则相反。你换一个RTOS,可能整个优先级设计都得重来。
| RTOS | 数值越小优先级 | 典型范围 |
|---|---|---|
| FreeRTOS | 越低 | 0 ~ configMAX_PRIORITIES-1 |
| uC/OS-II | 越高 | 0 ~ 63(0最高) |
| RT-Thread | 越高 | 0 ~ RT_THREAD_PRIORITY_MAX-1 |
| VxWorks | 越高 | 0 ~ 255(0最高) |
你看,FreeRTOS是数值越小优先级越低,而uC/OS-II正好相反。我刚开始用FreeRTOS时,习惯性地把最高优先级任务设成0,结果它老是不运行。查了半天才发现,原来0在FreeRTOS里是最低优先级。
避坑指南:
我曾经在一个项目里,团队一半人用FreeRTOS,另一半人用uC/OS-II。结果代码合并时,优先级数值全乱套了。后来我们统一加了个宏定义,比如#define HIGHEST_PRIORITY 0,再根据RTOS做映射。这招挺管用,推荐给你。
3.3 优先级数值的设计原则
说完了概念,聊聊实际怎么用。
我个人建议,优先级数值不要用满。比如系统支持32级优先级,你只用16级。为什么?留点余量给后续扩展。我见过一个产品,刚开始只用了4个优先级,后来加了新功能,发现优先级不够用了,整个重构,那叫一个痛苦。
我的经验:
- 关键任务(如心跳、安全监测)用最高优先级
- 实时性要求高的任务(如传感器采集)用中高优先级
- 普通任务(如数据显示)用中低优先级
- 后台任务(如日志记录)用最低优先级
还有一点,优先级数值最好用宏定义,别直接写数字。你想想看,代码里到处都是osTaskCreate(task, NULL, 5),过两个月你自己都看不懂5是什么意思。
// 推荐写法
#define PRIO_HEARTBEAT 0 // 最高优先级
#define PRIO_SENSOR 2
#define PRIO_DISPLAY 4
#define PRIO_LOGGER 6 // 最低优先级
// 不推荐写法
osTaskCreate(heartbeatTask, NULL, 0);
osTaskCreate(sensorTask, NULL, 2);
osTaskCreate(displayTask, NULL, 4);
osTaskCreate(loggerTask, NULL, 6);
嗯,这样写,别人看代码一目了然。而且以后调整优先级,改一个宏定义就行,不用满世界找数字。
3.4 优先级反转与动态优先级
说到动态优先级,不得不提优先级反转。这是个经典问题。
简单说,就是低优先级任务占着资源不放,高优先级任务等着,结果中优先级任务插进来把低优先级任务抢走了CPU。高优先级任务反而被中优先级任务阻塞了。
解决这个问题,常用方法就是优先级继承——低优先级任务临时提升到高优先级任务的级别,等资源释放了再降回去。这就是动态优先级的一个典型应用。
优先级反转的典型场景:
- 任务A(高优先级)等待任务C(低优先级)释放互斥锁
- 任务B(中优先级)抢占任务C,任务C无法运行
- 任务A被任务B间接阻塞,优先级反转发生
我在一个电机控制项目里就遇到过。一个高优先级的控制任务,因为一个低优先级的日志任务占着I2C总线,死活拿不到数据。后来加了优先级继承,问题就解决了。
好了,优先级的基本概念就聊到这儿。记住两点:第一,搞清楚你的RTOS里数值和优先级的关系;第二,留好余量,别把优先级用满。下节课咱们聊聊优先级分配的具体策略。