任务状态机:任务的一生经历哪几个阶段?
在上一章中,我们构建了任务控制块(TCB)这个静态骨架。它像一份详尽的个人档案,记录了任务的栈位置、优先级和链表节点。但一个真正的任务绝不是静止不动的。它会随着代码的执行和外部事件的发生,反复地在“能够运行”、“正在运行”和“等待某件事”之间来回切换。
驱动这一切动态切换的,就是任务状态机。如果说TCB是任务的“档案袋”,那么状态机就是任务的“人生轨迹图”。理解状态机,是理解调度器如何管理多任务、以及每个任务为什么能“在该来时来,在该等时等”的关键。
1.任务的五种经典状态
在绝大多数RTOS内核中,一个任务从创建到销毁,其生命周期中会经历五种最基本的状态。我们用一张状态图来宏观勾勒它们之间的联系。

下面我们逐一拆解这五种状态的具体含义,并解释它们转换的触发条件。
休眠态(Dormant)
休眠态指的是任务尚未创建,或者已经被显式删除的状态。此时内核没有为该任务分配TCB和栈空间,任务“不存在”于内核的任务管理链表中。休眠态是所有任务的起点和终点。
就绪态(Ready)
就绪态是任务最渴望进入的状态之一。处于就绪态的任务意味着:它已经具备了运行所需的一切条件,唯一欠缺的就是CPU这个执行资源。所有就绪的任务的TCB会被内核挂载到就绪链表(Ready List)上,等待调度器的临幸。
运行态(Running)
在单核处理器上,任何时刻有且仅有一个任务处于运行态。该任务正是当前占用CPU的那一位。它的TCB从就绪链表中被移出,处理器执行它的代码,而它的pxTopOfStack也随时准备好在下一次切换时被更新。
阻塞态(Blocked)
这是嵌入式系统中最常见也最重要的等待状态。当任务调用 vTaskDelay()、xSemaphoreTake()、xQueueReceive()等API主动放弃CPU并等待某个事件(如延时到期、信号量可用、数据到达)时,它会从运行态转入阻塞态。阻塞态的任务会从就绪链表中移出,并挂入特定的阻塞链表(比如延时链表或某个内核对象的等待链表)。一旦等待的事件发生,内核会自动将任务从阻塞态唤醒,移回就绪链表。
挂起态(Suspended)
挂起态和阻塞态不同。阻塞是任务“自愿”等待,而挂起通常是被其他任务强制暂停。处于挂起态的任务除非被其他任务调用vTaskResume()显式唤醒,否则永远不会被调度器选中。挂起态常用于调试或紧急冻结某些任务。
2.用代码感受一次状态流转
理论描述终究有点抽象。让我们通过一段精简的FreeRTOS风格伪代码,来感受一个典型的传感器任务在生命周期中的状态变换过程。
// 任务入口函数 void vSensorTask(void *pvParameters) { uint16_t sensorValue; while(1) { // 状态:运行态 // 等待一个信号量,表示传感器数据已就绪 // 如果信号量不可用,本任务进入【阻塞态】 // 调度器选择其他就绪任务运行 if (xSemaphoreTake(xSensorSemaphore, pdMS_TO_TICKS(1000)) == pdTRUE) { // 信号量成功获取,本任务回到【运行态】 sensorValue = Read_ADC(); // 将数据发送到消息队列 xQueueSend(xDataQueue, &sensorValue, 0); } else { // 超时,说明传感器可能故障 // 记录错误日志 } // 主动让出CPU,进入【阻塞态】100毫秒,实现周期性采集 vTaskDelay(pdMS_TO_TICKS(100)); // 100ms后,任务重新回到【就绪态】,等待调度器再次选中 } }
在这个while(1)循环的每一次迭代中,任务都在运行→阻塞→就绪→运行的状态环中反复流转。阻塞态让任务在没有工作需要时完全“静默”,不占用任何CPU时间,而其他真正需要运行的任务就可以在这段时间内自由驰骋。
3.LiteOS中的状态实现细节
理论模型在不同RTOS的工程实现中会有些许差异,但核心思想高度一致。我们看一下华为 Huawei LiteOS 是如何具体处理任务状态的。
在LiteOS中,任务的状态不是用离散的常量(如0=就绪,1=阻塞)表示,而是采用位掩码方式,将多个状态属性组合在一个状态字中。例如:
/* LiteOS 任务状态位定义 */ #define OS_TASK_STATUS_RUNNING 0x0001 // 运行态 #define OS_TASK_STATUS_READY 0x0002 // 就绪态 #define OS_TASK_STATUS_PEND 0x0004 // 阻塞态(等待信号量/队列等) #define OS_TASK_STATUS_SUSPEND 0x0010 // 挂起态 #define OS_TASK_STATUS_DELAY 0x0020 // 延时态(也是阻塞态的一种)
这种设计带来一个重要的工程优势:一个任务可以同时处于挂起态和阻塞态。比如,一个任务可能因为vTaskDelay()进入了延时阻塞态,同时又被其他任务调用了vTaskSuspend()挂起。当延时到期时,内核检测到该任务还被挂起,就不会将其移入就绪链表,而是保持阻塞状态直到挂起被解除。这种精细的控制在复杂的物联网终端逻辑中非常有用。
在华为云物联网方案中,大量设备端运行着LiteOS内核,它们通过MQTT协议与云端通信。设备端通常会有多个任务并行:
-
一个传感器采集任务:周期性读取传感器,状态在就绪与阻塞(延时)间循环。
-
一个云通信任务:等待消息队列中的数据,一旦有数据就从阻塞态转为就绪,最终运行,将数据打包上传至华为云IoT平台。
-
一个指令执行任务:阻塞在信号量上,等待来自云端的下行命令,一有命令就立刻进入运行态执行动作。
正是状态机与调度器的高效配合,保证了这些任务在单MCU上能够流畅协作,既满足了本地控制的实时性要求,也确保了云端数据上报的可靠性。可以说,没有阻塞态,就没有嵌入式系统的低功耗和高效并发。
4.小结与下节预告
到本章为止,我们拥有了TCB这个静态档案,也理清了任务在其生命周期中五种状态的动态切换规律。接下来我们将直面调度器的核心决策逻辑:当同时有多个任务就绪时,CPU到底该给谁? 这就是下一章要深入剖析的调度算法——优先级抢占和时间片轮转。届时,我们将在LiteOS的调度策略中找到这个问题的答案,敬请期待。
- 点赞
- 收藏
- 关注作者
评论(0)