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 挂起态详解

挂起态比较特殊。它不是任务自己主动进的,而是被别的任务强制暂停的。我习惯把挂起态叫做“任务监狱”——任务还在,但调度器完全无视它。

挂起态常用于调试场景。比如你想暂停某个任务看看它的堆栈,或者想临时禁掉一个高优先级任务来测试低优先级任务的响应。用taskSuspendtaskResume这对组合拳就行。

/* 挂起任务 */
taskSuspend(taskId);

/* 恢复任务 */
taskResume(taskId);

注意:挂起态可以叠加在其他状态上。比如一个任务既在阻塞态又在挂起态,那它得先被恢复,再等到资源可用,才能进入就绪态。这个嵌套关系,我当年可是踩过坑的。

3.6 延迟态详解

延迟态是任务主动调用taskDelaytaskDelayUntil后进入的状态。说白了就是任务自己说:“我累了,让我歇会儿”。

延迟态和阻塞态的区别在于:阻塞态是等外部事件,延迟态是等时间流逝。我建议你尽量用taskDelayUntil而不是taskDelay,因为前者能保证周期任务的精确性。

/* 延迟100个tick */
taskDelay(100);

/* 精确周期延迟 */
taskDelayUntil(&lastTime, 100);

我的经验:延迟态的任务如果被挂起,延迟计时器会暂停。等任务恢复后,延迟计时器继续走。这个细节很多人不知道,结果导致任务醒来时间比预期晚了很多。

3.7 状态迁移实战案例

咱们看一个实际场景。假设有三个任务:

  • TaskA:高优先级,负责采集数据
  • TaskB:中优先级,负责处理数据
  • TaskC:低优先级,负责日志输出

运行过程是这样的:

  1. TaskA就绪,被调度执行。采集完数据后,它把数据放到队列里,然后继续采集。
  2. TaskB从阻塞态变成就绪态(因为队列里有数据了),但TaskA优先级更高,所以TaskB继续等。
  3. TaskA采集完一轮后调用taskDelay进入延迟态,TaskB终于被调度。
  4. TaskB处理完数据后,调用taskSuspend挂起自己(因为暂时没活干),TaskC开始执行日志输出。
  5. TaskC输出完日志后,也进入延迟态。系统空闲。

你看,整个过程中,每个任务都在不同的状态间跳来跳去。这就是状态机的魅力——它让系统有序地运转。

总结一句话:状态机不是理论,是实战工具。你调试系统时,第一件事就是看任务状态。哪个任务卡住了,哪个任务在空转,一眼就能看出来。

好了,这一章就到这里。下一章咱们聊聊优先级反转和优先级继承——这可是面试必考题,也是实际项目中容易翻车的地方。