外观
1.6 调度器
这节要搞懂:好几个任务都想跑,谁说了算。 需要基础:1.5 ⭐ 本章重点
一、什么是调度器
调度器(Scheduler)是操作系统内核的核心部件,负责决定在每一时刻由哪个任务占用 CPU。FreeRTOS 采用基于优先级的抢占式调度:始终运行处于就绪状态中优先级最高的任务。
打个比方:调度器就是排班主管。所有员工都想上工,由他一个人说了算。
他的规则只有一句话:
谁的优先级最高,而且现在能跑,就让谁跑。
二、什么是优先级
优先级(Priority)表示任务的重要程度。数值越大,越优先获得 CPU。
在 ESP32 上范围是 0 ~ 24:
| 优先级 | 给谁用 |
|---|---|
| 0 | 系统保留(空闲任务专用),你别用 |
| 1 – 4 | 不着急的事:打日志、统计、上报数据 |
| 5 – 9 | ⭐ 平时就填这个范围:显示、传感器、按键 |
| 10 以上 | 系统的 WiFi、网络已经在用,别去挤 |
:
拿不准就全填 5。等哪个功能反应慢了,再把它调到 6。
三、什么是抢占
抢占(Preemption)指的是:当一个优先级更高的任务变为就绪状态时,内核会立即中断当前正在运行的任务,转而执行高优先级任务。
举个例子:
- 任务 A:优先级 3,正在慢慢打印日志
- 任务 B:优先级 6,在等按键
你一按按键,任务 B 立刻醒来,把正在跑的任务 A 一脚踹开,自己上:
这就是为什么按键任务要给高优先级 —— 重要的事总能第一时间响应。
四、优先级一样怎么办
时间片轮转:优先级相同的任务,内核会让它们轮流执行,每人一小段时间。
你一下我一下,看起来就像同时在跑。
五、什么是空闲任务
空闲任务(Idle Task)是调度器启动时自动创建的一个优先级为 0 的任务。当没有任何其他任务可运行时,CPU 就执行它。
它负责回收被删除任务占用的内存,所以必须保证它有机会运行。
六、高优先级任务的责任 ⭐
最经典的翻车现场
高优先级的任务,绝对不能一直不"睡"。
c
void task_high_priority(void *pvParameters)
{
while (1) {
do_something();
// ❌ 这里没有 vTaskDelay!
}
}后果:它会一直霸占 CPU,所有优先级比它低的任务全部饿死,包括负责回收内存的空闲任务。
系统会打印这句话提醒你: Task watchdog got triggered. The following tasks did not reset the watchdog in time
看到这句话,就去检查是不是哪个任务忘了 vTaskDelay。
七、时间是怎么算的
Tick(时钟节拍) 是 FreeRTOS 的时间基本单位。内核靠一个硬件定时器周期性产生 tick 中断,所有的延时和超时都按 tick 计算。
你不需要关心一个 tick 是多久,只要永远用这个宏来换算:
c
vTaskDelay(pdMS_TO_TICKS(500)); // ✅ 睡 500 毫秒,这样写就对了
vTaskDelay(500); // ❌ 别这么写!这是 500 个 tick,不是 500 毫秒本节要点
- 调度器 = 决定谁用 CPU 的部件,规则是"优先级最高且能跑的先跑"
- 优先级新手统一填 5,需要快速响应的填 6
- 抢占 = 高优先级任务一就绪,立刻打断当前任务
- 优先级相同则轮流执行
- ⭐ 高优先级任务必须会"睡",否则饿死所有人
- 延时永远写
pdMS_TO_TICKS(毫秒数)
动手试试
- 建三个任务,优先级分别是 3、5、7,各自打印自己的名字,观察输出规律。
- 把优先级 7 的那个任务的
vTaskDelay删掉,看看另外两个还能不能打印。 - 把两个任务设成同样的优先级,验证它们是不是在轮流打印。