任务状态机
判别规则(核心,唯一要记的)
- 任务"在跑" = Running;"排队等 CPU" = Ready;"主动等事件/延时" = Blocked(自动恢复、有超时);"被外部挂起" = Suspended(手动恢复、无超时)
- 转换:抢占/时间片 ↔ Running↔Ready;阻塞 API → Running→Blocked;vTaskSuspend/vTaskResume ↔ Suspended
一句话:"在跑是 Running,排队是 Ready,等闹钟是 Blocked,等别人叫是 Suspended。"
抽象描述(一句话本质)
任务任何时候处于四种状态之一。核心判别:"这个任务现在为什么没在跑?"——排队等 CPU = Ready,自己等事件/延时 = Blocked(自动醒),被外部挂起 = Suspended(手动醒),在跑 = Running。
状态转换表
| 从 → 到 |
触发 |
| Running → Ready |
时间片用完(tick 到点) / 被高优先级抢占 |
| Running → Blocked |
调阻塞 API(vTaskDelay / 等信号量 / 队列 / 事件组) |
| Ready → Running |
调度器选中它 |
| Running → Suspended |
vTaskSuspend 挂起 |
| Suspended → Ready |
vTaskResume 恢复 |
| Ready → Suspended |
不能直接(先 Running 或 Blocked 再挂起) |
正反例(建立直觉)
- 正例1:正在执行代码 = Running
- 正例2:调
vTaskDelay(100) 后 = Blocked(主动等延时)
- 正例3:被高优先级任务抢占后 = Ready(排队等 CPU)
- 正例4:执行
vTaskSuspend(NULL) 后 = Suspended
- 反例1:以为延时是 Suspended → 错,vTaskDelay 是 Blocked
- 反例2:以为 Running 会一直跑 → 会被更高优先级抢占回 Ready
易混对比
|
Blocked |
Suspended |
| 原因 |
主动等待事件(延时/信号量/队列) |
被外部挂起 |
| 怎么恢复 |
事件来了自动回 Ready |
必须 vTaskResume |
| 有超时吗 |
有(可设超时上限) |
无 |
变体验证(3 题,全过=学会)
- 陷阱题:任务等一个永不发生的事件 → 永久 Blocked,永远轮不到执行(死等/饥饿,RTOS 经典 bug 场景)
- 陷阱题:阻塞事件到达 → 先回 Ready,再等调度器选,不直接 Running(不能插队,保证调度器统一决策)
- 同优先级两个任务 → 时间片轮转:tick 到点,Running 任务用完时间片回 Ready 队尾,下一个 Ready 变 Running
口述要点(面试怎么讲)
- 结论先行:四种状态 + 一个核心问题"为什么没在跑"
- 为什么事件到达先回 Ready 不直接 Running:先进就绪队列让调度器统一决策,保证"正在运行的永远是最高优先级就绪任务"这条核心不变量不被绕过。直接 Running 会破坏抢占一致性
- vTaskDelay vs vTaskDelayUntil:vTaskDelay = 相对延时(从调用起算,总周期 = 函数体 + 延时,如函数体 200ms + 延时 500ms → 周期约 700ms);vTaskDelayUntil = 绝对延时(按固定周期醒来,周期固定 500ms)。周期固定/硬实时周期任务 → DelayUntil(前提:函数体时间 < 周期)
- 时间片轮转:不是"任务执行完成"才轮换,是时间片用完(tick 到点)就轮换,任务没做完也让位,下次接着跑
- 易错点:延时是 Blocked 不是 Suspended;事件到达不直接 Running;轮换靠 tick 不靠任务完成
关系网络
| 相邻概念 |
和本主题的关系 |
孤立理解会犯的错 |
| 抢占 vs 时间片 |
Running↔Ready 的两条路径:高优先级抢占 / 同优先级时间片 |
以为时间片只在"任务完成后"发生 |
| 信号量/队列 |
Blocked 的进入/唤醒全靠它们 |
以为 Blocked 只能靠延时 |
| 临界区 |
关中断期间调度不运行 |
以为临界区里任务还会被切走 |
| 任务通知 |
最轻量的唤醒机制 |
以为唤醒必须用队列 |
学习日期
2026-08-03
每主题一页,复习时只翻本目录。