--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第8章 任务相关API函数" author: "尚硅谷研究院" date: 2026-07-17 created: 2026-07-17 --- # 任务状态查询与运行时统计 > **用生活理解**:任务状态查询就像给公司做一次全面盘点——有多少员工(任务数)、每个员工的职级(优先级)、当前在干嘛(运行/就绪/阻塞)、堆栈压力(水位线)一目了然。运行时统计则像月底看考勤——每个员工干了多少活,一目了然。 ## 概念介绍 ### 基本概念 FreeRTOS 提供一套丰富的 API 用于查询任务运行时信息,包括: - **优先级查询与设置**:获取/修改任务的优先级 - **任务数量统计**:系统中当前存在的总任务数 - **任务详细信息**:获取任务名称、编号、状态、优先级等 - **栈空间使用**:查看栈的"水位线"(剩余空间最小值) - **任务状态**:查询任务当前处于运行/就绪/阻塞/挂起/删除态 - **任务列表**:以可读文本格式输出所有任务信息 - **运行时统计**:需要额外硬件定时器,统计各任务占用 CPU 的百分比 ### 工作原理 **运行时统计(Run Time Stats)** 需要额外的硬件定时器作为时钟源: ``` 配置步骤: 1. 启用 configGENERATE_RUN_TIME_STATS = 1 2. 配置一个硬件定时器(如 TIM6/TIM7)产生高频计时 3. 实现 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 初始化定时器 4. 实现 portGET_RUN_TIME_COUNTER_VALUE() 返回定时器计数值 ``` 统计结果以表格形式输出: ``` Task Abs Time % Time -------------------------------- task1 12345 45.2 task2 9876 36.1 IDLE 5120 18.7 ``` ## 实验 ### 需求描述 **P06(状态查询 API)**: - task2 一次性调用所有状态查询 API 并打印结果: - `uxTaskPriorityGet()` / `vTaskPrioritySet()` - `uxTaskGetNumberOfTasks()` - `uxTaskGetSystemState()` / `vTaskGetInfo()` - `xTaskGetCurrentTaskHandle()` / `xTaskGetHandle()` - `uxTaskGetStackHighWaterMark()` - `eTaskGetState()` - `vTaskList()` **P07(运行时统计)**: - task2 每隔 500ms 打印 `vTaskGetRunTimeStats()` 结果 - 需要 TIM6 提供高频计时 ### 代码实现 #### P06:任务状态查询 **代码来源:**03_代码/06_任务状态查询/Core/Src/freertos_demo.c ```c #include "freertos_demo.h" #include "FreeRTOS.h" #include "task.h" #include "LED.h" #include "Key.h" #define START_TASK_STACK 128 #define START_TASK_PRIORITY 1 TaskHandle_t start_task_handle; void start_task(void *pvParameters); #define TASK1_STACK 128 #define TASK1_PRIORITY 2 TaskHandle_t task1_handle; void task1(void *pvParameters); /* task2栈空间更大,因为要调用多个API和打印 */ #define TASK2_STACK 256 #define TASK2_PRIORITY 3 TaskHandle_t task2_handle; void task2(void *pvParameters); void freertos_start(void) { xTaskCreate((TaskFunction_t)start_task, (char *)"start_task", (configSTACK_DEPTH_TYPE)START_TASK_STACK, (void *)NULL, (UBaseType_t)START_TASK_PRIORITY, (TaskHandle_t *)&start_task_handle); vTaskStartScheduler(); } void start_task(void *pvParameters) { taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t)task1, (char *)"task1", (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL, (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle); xTaskCreate((TaskFunction_t)task2, (char *)"task2", (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL, (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle); vTaskDelete(NULL); taskEXIT_CRITICAL(); } void task1(void *pvParameters) { while (1) { printf("task1正在运行...\r\n"); LED_Toggle(LED1_Pin); vTaskDelay(500); } } char task_info[500]; /** * @description: task2——演示所有任务状态查询API的使用 */ void task2(void *pvParameters) { UBaseType_t task_priority = 0; UBaseType_t task_num = 0; TaskStatus_t task_status[10] = {0}; // 最多10个任务的状态信息 TaskStatus_t task_state; TaskHandle_t task_handle = NULL; UBaseType_t task_stack_remain_min = 0; eTaskState e_task_state = 0; /* 1. 获取task1的优先级 */ task_priority = uxTaskPriorityGet(task1_handle); printf("task1当前优先级=%d\r\n", task_priority); /* 2. 设置task1的优先级为4 */ vTaskPrioritySet(task1_handle, 4); task_priority = uxTaskPriorityGet(task1_handle); printf("task1修改后优先级=%d\r\n", task_priority); /* 3. 获取系统中任务总数 */ task_num = uxTaskGetNumberOfTasks(); printf("当前系统中任务数量=%d\r\n", task_num); /* 4. 获取所有任务状态信息 */ uxTaskGetSystemState(task_status, task_num, NULL); printf("任务名\t\t任务号\t\t状态\t\t当前优先级\t\t基础优先级\r\n"); for (uint8_t i = 0; i < task_num; i++) { printf("%s\t\t%d\t\t%d\t\t%d\t\t%d\r\n", task_status[i].pcTaskName, task_status[i].xTaskNumber, task_status[i].eCurrentState, task_status[i].uxCurrentPriority, task_status[i].uxBasePriority); } /* 5. 获取任务详细信息 */ vTaskGetInfo( task1_handle, // 要查询的任务句柄 &task_state, // 用于接收任务信息的结构体 pdTRUE, // 是否获取栈状态 eInvalid); // 任务状态(入参被忽略) printf("任务名称: %s\r\n", task_state.pcTaskName); printf("任务编号: %d\r\n", task_state.xTaskNumber); printf("任务状态: %d\r\n", task_state.eCurrentState); printf("当前优先级: %d\r\n", task_state.uxCurrentPriority); /* 6. 获取当前正在运行的任务句柄 */ task_handle = xTaskGetCurrentTaskHandle(); printf("当前运行任务句柄=%p, task2句柄=%p\r\n", task_handle, task2_handle); /* 7. 通过任务名获取句柄 */ task_handle = xTaskGetHandle("task1"); printf("通过名称获取task1句柄=%p, task1原始句柄=%p\r\n", task_handle, task1_handle); /* 8. 获取栈空间水位线(栈剩余的最小值→反映栈峰值使用量) */ task_stack_remain_min = uxTaskGetStackHighWaterMark(task2_handle); printf("task2栈剩余空间最小值=%d\r\n", task_stack_remain_min); /* 9. 获取任务状态(运行/就绪/阻塞/挂起/删除) */ e_task_state = eTaskGetState(task1_handle); printf("task1当前状态=%d\r\n", e_task_state); /* 10. 打印可读格式的任务列表 */ vTaskList(task_info); printf("%s\r\n", task_info); while (1) { vTaskDelay(500); } } ``` #### P07:运行时统计 **代码来源:**03_代码/07_任务时间统计/Core/Src/freertos_demo.c ```c #include "freertos_demo.h" #include "FreeRTOS.h" #include "task.h" #include "LED.h" #define START_TASK_STACK 128 #define START_TASK_PRIORITY 1 TaskHandle_t start_task_handle; void start_task(void *pvParameters); #define TASK1_STACK 128 #define TASK1_PRIORITY 2 TaskHandle_t task1_handle; void task1(void *pvParameters); #define TASK2_STACK 128 #define TASK2_PRIORITY 3 TaskHandle_t task2_handle; void task2(void *pvParameters); void freertos_start(void) { xTaskCreate((TaskFunction_t)start_task, (char *)"start_task", (configSTACK_DEPTH_TYPE)START_TASK_STACK, (void *)NULL, (UBaseType_t)START_TASK_PRIORITY, (TaskHandle_t *)&start_task_handle); vTaskStartScheduler(); } void start_task(void *pvParameters) { taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t)task1, (char *)"task1", (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL, (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle); xTaskCreate((TaskFunction_t)task2, (char *)"task2", (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL, (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle); vTaskDelete(NULL); taskEXIT_CRITICAL(); } void task1(void *pvParameters) { while (1) { printf("task1正在运行...\r\n"); LED_Toggle(LED1_Pin); vTaskDelay(500); } } char task_time_info[500]; /** * @description: task2——每隔500ms打印各任务运行时间统计 */ void task2(void *pvParameters) { while (1) { printf("task2正在运行...\r\n"); /* 获取各任务的运行时间统计表(需要硬件定时器支持) */ vTaskGetRunTimeStats(task_time_info); printf("%s\r\n", task_time_info); vTaskDelay(500); } } ``` ### 关键配置 **P06 FreeRTOSConfig.h**(启用查询功能): ```c #define configUSE_TRACE_FACILITY 1 // 启用任务跟踪功能 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 启用格式化输出函数 #define INCLUDE_xTaskGetHandle 1 // 启用通过名称获取句柄 #define INCLUDE_uxTaskGetStackHighWaterMark 1 // 启用栈水位线查询 ``` **P07 FreeRTOSConfig.h**(启用运行时统计 + 硬件定时器配置): ```c #define configGENERATE_RUN_TIME_STATS 1 // 生成运行时统计 #if configGENERATE_RUN_TIME_STATS extern volatile unsigned long ulHighFrequencyTimerTicks; // 高频定时器计数值 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL ) // 初始化定时器 #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks // 读取当前计数值 #endif #define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 ``` ## 核心函数速查表 | 函数 | 参数 | 返回值 | 功能简述 | |----------|-----------|--------|-------------| | `uxTaskPriorityGet` | xTask | UBaseType_t | 获取任务优先级 | | `vTaskPrioritySet` | xTask, uxNewPriority | void | 设置任务优先级 | | `uxTaskGetNumberOfTasks` | 无 | UBaseType_t | 获取系统中任务总数 | | `uxTaskGetSystemState` | pxTaskStatusArray, uxArraySize, pulTotalRunTime | UBaseType_t | 获取所有任务的状态 | | `vTaskGetInfo` | xTask, pxTaskStatus, xGetFreeStackSpace, eState | void | 获取任务详细信息 | | `xTaskGetCurrentTaskHandle` | 无 | TaskHandle_t | 获取当前运行任务的句柄 | | `xTaskGetHandle` | pcNameToQuery | TaskHandle_t | 通过任务名获取句柄 | | `uxTaskGetStackHighWaterMark` | xTask | UBaseType_t | 获取栈剩余空间下限(以 word 计) | | `eTaskGetState` | xTask | eTaskState | 获取任务状态(running/ready/blocked/suspended/deleted) | | `vTaskList` | pcWriteBuffer | void | 将任务列表格式化输出为可读字符串 | | `vTaskGetRunTimeStats` | pcWriteBuffer | void | 获取任务运行时统计信息 | ## 常见问题与避坑 1. **configUSE_TRACE_FACILITY 必须开启**:`vTaskList()`、`uxTaskGetSystemState()`、`eTaskGetState()` 等函数都需要此宏为 1。 2. **configUSE_STATS_FORMATTING_FUNCTIONS 与 vTaskList**:`vTaskList()` 需要同时开启 `configUSE_TRACE_FACILITY` 和 `configUSE_STATS_FORMATTING_FUNCTIONS`。 3. **运行时统计需要硬件定时器**:`vTaskGetRunTimeStats()` 依赖一个比 SysTick 更高分辨率的硬件定时器。如果不配置该定时器,统计结果无意义。使用 TIM6/TIM7 等基本定时器即可。 4. **栈水位线(High Water Mark)的含义**:返回的是任务运行以来栈剩余空间的最小值,用这个值可以判断栈配置是否合理。如果返回值接近 0,说明栈空间紧张,需要增大栈大小。 5. **缓冲区大小**:`vTaskList()` 和 `vTaskGetRunTimeStats()` 输出的文本内容随任务数量变化,确保提供的缓冲区足够大(每个任务约 40~60 字节)。 6. **vTaskPrioritySet 修改优先级后的调度**:当提高一个低优先级任务的优先级时,如果新优先级高于当前运行任务的优先级,会立即触发一次任务切换。