3. 任务状态机与状态迁移:就绪态、阻塞态、挂起态、延迟态详解
各位同学,咱们今天聊聊VxWorks里任务的状态机。说实话,搞嵌入式这些年,我见过太多因为状态搞混而翻车的案例了。任务状态机这东西,说白了就是操作系统的“交通规则”——任务什么时候能跑,什么时候得等着,全看它当前在哪个状态。
3.1 四大核心状态
VxWorks里任务的状态,我习惯把它分成四种:就绪态、阻塞态、挂起态和延迟态。嗯,有些资料会把延迟态归到阻塞态里,但我个人觉得分开讲更清楚。
| 状态 | 英文 | 含义 |
|---|---|---|
| 就绪态 | READY | 任务已准备好,只等CPU空闲 |
| 阻塞态 | PEND | 任务在等某个资源或事件 |
| 挂起态 | SUSPEND | 任务被强制暂停,不参与调度 |
| 延迟态 | DELAY | 任务主动休眠一段时间 |
核心要点:只有就绪态的任务才能被调度器选中执行。其他状态的任务,CPU连看都不看一眼。
3.2 状态迁移图实战分析
先画个简单的迁移图在脑子里:
创建任务 → 就绪态
就绪态 → 运行态(被调度器选中)
运行态 → 就绪态(时间片用完或被抢占)
运行态 → 阻塞态(等待信号量/消息队列)
运行态 → 延迟态(调用taskDelay)
运行态 → 挂起态(被taskSuspend)
阻塞态 → 就绪态(资源可用)
延迟态 → 就绪态(延时到期)
挂起态 → 就绪态(被taskResume)
你想想看,这个图其实就回答了三个问题:谁让我停?谁让我走?我停在哪?
3.3 就绪态详解
就绪态是最“清白”的状态。任务不欠任何资源,也不欠时间,纯粹是CPU没空理它。我在项目中遇到过一个问题:系统里明明有20个任务,但CPU占用率才30%,系统却卡得要死。查了半天,发现是优先级设计不合理,高优先级任务一直在就绪队列里排队,低优先级任务根本轮不到。
我的习惯:调试时用i命令查看所有任务状态。如果发现大量任务处于READY态,说明CPU确实忙不过来。如果READY态任务很少但系统卡,那八成是优先级反转或者死锁了。
3.4 阻塞态详解
阻塞态,说白了就是任务在“等东西”。等信号量、等消息、等内存。我曾经犯过一个低级错误:一个任务在等一个永远不会来的信号量,结果整个系统都挂在那了。嗯,这就是典型的死锁。
阻塞态有个特点:它不消耗CPU。任务进入阻塞态后,调度器会直接跳过它,去调度其他就绪任务。所以阻塞态其实是好事——它让CPU能去干更有意义的事。
避坑指南:我曾经调试过一个系统,发现某个任务一直处于PEND态,但信号量明明已经给了。后来发现是任务在等信号量时被挂起了,信号量释放后任务还在挂起态,没人去恢复它。记住:taskResume和信号量释放是两回事,别搞混了。
3.5 挂起态详解
挂起态比较特殊。它不是任务自己主动进的,而是被别的任务强制暂停的。我习惯把挂起态叫做“任务监狱”——任务还在,但调度器完全无视它。
挂起态常用于调试场景。比如你想暂停某个任务看看它的堆栈,或者想临时禁掉一个高优先级任务来测试低优先级任务的响应。用taskSuspend和taskResume这对组合拳就行。
/* 挂起任务 */
taskSuspend(taskId);
/* 恢复任务 */
taskResume(taskId);
注意:挂起态可以叠加在其他状态上。比如一个任务既在阻塞态又在挂起态,那它得先被恢复,再等到资源可用,才能进入就绪态。这个嵌套关系,我当年可是踩过坑的。
3.6 延迟态详解
延迟态是任务主动调用taskDelay或taskDelayUntil后进入的状态。说白了就是任务自己说:“我累了,让我歇会儿”。
延迟态和阻塞态的区别在于:阻塞态是等外部事件,延迟态是等时间流逝。我建议你尽量用taskDelayUntil而不是taskDelay,因为前者能保证周期任务的精确性。
/* 延迟100个tick */
taskDelay(100);
/* 精确周期延迟 */
taskDelayUntil(&lastTime, 100);
我的经验:延迟态的任务如果被挂起,延迟计时器会暂停。等任务恢复后,延迟计时器继续走。这个细节很多人不知道,结果导致任务醒来时间比预期晚了很多。
3.7 状态迁移实战案例
咱们看一个实际场景。假设有三个任务:
- TaskA:高优先级,负责采集数据
- TaskB:中优先级,负责处理数据
- TaskC:低优先级,负责日志输出
运行过程是这样的:
- TaskA就绪,被调度执行。采集完数据后,它把数据放到队列里,然后继续采集。
- TaskB从阻塞态变成就绪态(因为队列里有数据了),但TaskA优先级更高,所以TaskB继续等。
- TaskA采集完一轮后调用
taskDelay进入延迟态,TaskB终于被调度。 - TaskB处理完数据后,调用
taskSuspend挂起自己(因为暂时没活干),TaskC开始执行日志输出。 - TaskC输出完日志后,也进入延迟态。系统空闲。
你看,整个过程中,每个任务都在不同的状态间跳来跳去。这就是状态机的魅力——它让系统有序地运转。
总结一句话:状态机不是理论,是实战工具。你调试系统时,第一件事就是看任务状态。哪个任务卡住了,哪个任务在空转,一眼就能看出来。
好了,这一章就到这里。下一章咱们聊聊优先级反转和优先级继承——这可是面试必考题,也是实际项目中容易翻车的地方。