4. 任务状态切换与阻塞
任务说明
本案例在FreeRTOS中创建两个任务,通过vTaskDelay()使任务主动释放CPU进入阻塞态,并借助uxTaskGetSystemState()读取各任务实时状态,观察任务在运行态→阻塞态→就绪态之间的切换过程。目标是通过实际代码理解任务状态迁移的触发条件与调度行为。
核心实现
#include "FreeRTOS.h"
#include "task.h"
#include "stdio.h"
/* 任务句柄 */
TaskHandle_t xTask1Handle = NULL;
TaskHandle_t xTask2Handle = NULL;
/* 任务1:运行后主动阻塞 */
void vTask1(void *pvParameters)
{
for (;;)
{
/* 进入阻塞态,让出CPU */
vTaskDelay(pdMS_TO_TICKS(200));
}
}
/* 任务2:运行后主动阻塞 */
void vTask2(void *pvParameters)
{
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(300));
}
}
/* 打印任务状态 */
void vPrintTaskStatus(void)
{
TaskStatus_t xTaskStatusArray[2];
UBaseType_t uxArraySize = 2;
UBaseType_t uxIndex;
/* 获取系统任务状态快照 */
uxArraySize = uxTaskGetSystemState(xTaskStatusArray,
uxArraySize,
NULL);
printf("--- Task States ---\r\n");
for (uxIndex = 0; uxIndex < uxArraySize; uxIndex++)
{
const char *pcState;
switch (xTaskStatusArray[uxIndex].eCurrentState)
{
case eRunning: pcState = "Running"; break;
case eReady: pcState = "Ready"; break;
case eBlocked: pcState = "Blocked"; break;
case eSuspended: pcState = "Suspended"; break;
case eDeleted: pcState = "Deleted"; break;
default: pcState = "Invalid"; break;
}
printf("Task: %-10s State: %s\r\n",
xTaskStatusArray[uxIndex].pcTaskName,
pcState);
}
}
/* 监控任务:周期性打印状态 */
void vMonitorTask(void *pvParameters)
{
for (;;)
{
vPrintTaskStatus();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
int main(void)
{
/* 创建两个工作任务 */
xTaskCreate(vTask1, "Task1", 128, NULL, 1, &xTask1Handle);
xTaskCreate(vTask2, "Task2", 128, NULL, 1, &xTask2Handle);
/* 创建监控任务,优先级最高以便抢占查看状态 */
xTaskCreate(vMonitorTask, "Monitor", 128, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 正常不会执行到这里 */
for (;;);
return 0;
}
状态迁移分析
两个工作任务优先级相同(均为1),监控任务优先级最高(2)。调度器启动后:
- 监控任务最先进入运行态,调用
vPrintTaskStatus()打印当前状态快照; - 当监控任务调用
vTaskDelay(100ms)进入阻塞态后,Task1和Task2按时间片轮转进入运行态; - Task1运行后立即调用
vTaskDelay(200ms),从运行态切换到阻塞态; - Task2同样运行后调用
vTaskDelay(300ms)进入阻塞态; - 当两个任务均阻塞时,空闲任务运行;
- 延时到期后,任务从阻塞态回到就绪态,等待调度器分配CPU。
状态观测点
编译与运行
# 在FreeRTOS Demo工程中添加上述源文件
# 确保FreeRTOSConfig.h中配置:
# configUSE_TRACE_FACILITY 1 (使能uxTaskGetSystemState)
# configUSE_STATS_FORMATTING_FUNCTIONS 1
# configSUPPORT_DYNAMIC_ALLOCATION 1
# 编译(以GCC/CMake为例)
cmake -B build
cmake --build build
# 运行(模拟器或开发板)
./build/rtos_demo
验证方法
运行程序后,通过串口终端观察vPrintTaskStatus()周期性输出的状态信息。判定标准:
- 输出中能同时看到
Blocked和Ready状态的任务; - Task1和Task2不会同时处于Running状态;
- Monitor任务每次打印时,至少有一个工作任务处于Blocked状态(因为工作任务的延时时间短于Monitor的打印周期,当Monitor从阻塞中唤醒时,工作任务可能已回到Ready状态);
- 状态名称与代码中
vTaskDelay()设置的延时逻辑一致:Task1阻塞约200ms,Task2阻塞约300ms。
若串口输出中能看到任务状态在Running→Blocked→Ready之间循环切换,且无系统崩溃或断言失败,则本案例验证通过。