4、任务调度算法入门:时间片轮转调度、优先级调度、抢占式调度、非抢占式调度
各位同学,咱们今天聊聊任务调度算法。说实话,这是RTOS里最核心的东西,也是很多新手觉得头疼的地方。
我刚开始做嵌入式那会儿,总觉得调度算法就是“谁先来谁先跑”。后来在智能手表项目上栽了跟头,才明白这里面的门道有多深。嗯,咱们一个一个来看。
4.1 非抢占式调度:老实人排队
非抢占式调度,说白了就是“一个任务不跑完,其他任务别想上”。任务一旦获得CPU,就会一直运行到主动放弃(比如调用延时函数、等待事件),或者运行结束。
这种调度方式实现简单,资源开销小。我在早期的低功耗蓝牙手环上用过,因为任务少,逻辑简单,完全够用。
特点:
- 任务主动让出CPU
- 没有优先级的概念(或者说所有任务平等)
- 适合任务少、实时性要求不高的场景
注意:我曾经在一个项目中,有个任务里写了死循环,结果整个系统卡死了。非抢占式调度下,一个任务出问题,整个系统就崩了。所以写任务代码一定要小心,别搞死循环。
4.2 抢占式调度:谁急谁先上
抢占式调度就灵活多了。高优先级的任务可以打断低优先级任务的执行。你想想看,智能手表上,用户按了一下按键,你总不能让正在刷屏的任务先跑完再响应按键吧?那体验得多差。
抢占式调度的核心是:任何时候,只要高优先级任务就绪,就立即抢占CPU。
我的经验:在智能手表项目中,我把触摸响应任务的优先级设得最高,UI刷新任务次之,后台数据同步任务优先级最低。这样用户操作永远是最流畅的。
4.3 优先级调度:给任务排个队
优先级调度,就是给每个任务分配一个优先级数字。数字越小优先级越高(或者反过来,看具体RTOS实现)。
这里有个坑:优先级反转。我记得在FreeRTOS上调试一个智能手表项目时,低优先级任务占用了某个资源,中优先级任务一直在跑,高优先级任务等资源等得干着急。这就是典型的优先级反转。
| 优先级 | 任务 | 状态 |
|---|---|---|
| 高(1) | 触摸响应 | 等待资源 |
| 中(2) | UI刷新 | 运行中 |
| 低(3) | 数据同步 | 持有资源 |
解决办法?用优先级继承或者优先级天花板协议。嗯,这个后面章节会细讲。
4.4 时间片轮转调度:大家轮流玩
时间片轮转,就是给每个任务分配固定的时间片,时间到了就切换到下一个任务。大家轮流用CPU,谁也不吃亏。
这种调度方式适合同优先级的任务。比如智能手表上,多个传感器数据采集任务,优先级相同,用时间片轮转就很合适。
// 伪代码示例:时间片轮转调度
void scheduler() {
while(1) {
current_task = get_next_task();
set_timer(TIME_SLICE); // 设置时间片
switch_to(current_task);
// 时间片到了,自动切换
}
}
关键点:
- 时间片大小要合理:太短导致频繁切换,浪费CPU;太长又失去轮转意义
- 我一般设10-50ms,具体看任务切换开销
4.5 实际项目中的选择
在智能手表项目中,我通常这样组合使用:
- 高优先级任务(抢占式):触摸响应、按键处理、紧急报警
- 中优先级任务(时间片轮转):UI刷新、传感器数据采集
- 低优先级任务(非抢占式):数据同步到手机、日志记录
为什么会这样?你想想看,用户按按键,必须立即响应,所以用抢占式。多个传感器轮流采集数据,用时间片轮转公平分配。后台同步任务不着急,用非抢占式,有空就跑跑。
避坑指南:我曾经把蓝牙通信任务设成了最高优先级,结果触摸响应被卡住了。用户按了没反应,体验极差。后来才明白,交互相关的任务优先级一定要高于通信任务。
4.6 小结
这四种调度算法,没有绝对的好坏。关键看你的应用场景。智能手表这种对交互响应要求高的设备,抢占式+优先级调度是主流。但如果你在做简单的传感器节点,非抢占式可能更省心。
嗯,下一章咱们会深入讲抢占式调度的实现细节,包括上下文切换、临界区保护这些硬核内容。到时候我会拿FreeRTOS的源码来拆解,大家做好准备。