3. 任务状态查询与统计信息获取

任务说明

本案例实现SafeRTOS任务状态查询与运行时间统计功能。系统创建两个不同优先级的任务:vTaskA周期性延时(阻塞态),vTaskB持续运行(运行态/就绪态)。主程序通过uxTaskGetSystemState()获取所有任务的状态信息,并通过vTaskGetRunTimeStats()获取各任务CPU运行时间占比,将结果通过串口输出。

核心API与数据结构

  • uxTaskGetSystemState():获取任务状态快照,填充TaskStatus_t数组
  • vTaskGetRunTimeStats():生成各任务运行时间统计表(需配置configGENERATE_RUN_TIME_STATS为1)
  • TaskStatus_t:包含任务句柄、任务名、任务号、状态、栈高水位、运行时间计数等字段

完整实现代码

/* FreeRTOS内核配置要求(FreeRTOSConfig.h中):
   #define configGENERATE_RUN_TIME_STATS 1
   #define configUSE_TRACE_FACILITY    1
   #define configUSE_STATS_FORMATTING_FUNCTIONS 1
   并实现 portGET_RUN_TIME_COUNTER_VALUE() 和 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
*/

#include "FreeRTOS.h"
#include "task.h"
#include "stdio.h"

/* 任务句柄 */
TaskHandle_t xTaskAHandle = NULL;
TaskHandle_t xTaskBHandle = NULL;

/* 任务A:周期性延时,进入阻塞态 */
void vTaskA(void *pvParameters)
{
    for (;;)
    {
        vTaskDelay(pdMS_TO_TICKS(500));  /* 阻塞500ms */
    }
}

/* 任务B:持续运行,占用CPU */
void vTaskB(void *pvParameters)
{
    volatile uint32_t ulCounter = 0;
    for (;;)
    {
        ulCounter++;  /* 模拟计算负载 */
    }
}

/* 查询并打印任务状态 */
void vPrintTaskStats(void)
{
    TaskStatus_t xTaskStatusArray[2];
    UBaseType_t uxArraySize = 2;
    uint32_t ulTotalRunTime = 0;
    uint32_t ulStatsAsPercentage;
    UBaseType_t uxIndex;
    char *pcTaskName;

    /* 获取任务状态快照 */
    uxTaskGetSystemState(xTaskStatusArray, uxArraySize, &ulTotalRunTime);

    printf("\r\n=== 任务状态 ===\r\n");
    printf("任务名\t状态码\t栈高水位(字)\t运行计数\r\n");

    for (uxIndex = 0; uxIndex < uxArraySize; uxIndex++)
    {
        pcTaskName = xTaskStatusArray[uxIndex].pcTaskName;
        printf("%s\t%d\t%u\t\t%lu\r\n",
               pcTaskName,
               xTaskStatusArray[uxIndex].eCurrentState,
               xTaskStatusArray[uxIndex].usStackHighWaterMark,
               (unsigned long)xTaskStatusArray[uxIndex].ulRunTimeCounter);
    }

    /* 打印运行时间统计(百分比) */
    printf("\r\n=== CPU运行时间统计 ===\r\n");
    for (uxIndex = 0; uxIndex < uxArraySize; uxIndex++)
    {
        if (ulTotalRunTime > 0)
        {
            ulStatsAsPercentage =
                (xTaskStatusArray[uxIndex].ulRunTimeCounter * 100UL) / ulTotalRunTime;
        }
        else
        {
            ulStatsAsPercentage = 0;
        }

        printf("%-10s: %lu%%\r\n",
               xTaskStatusArray[uxIndex].pcTaskName,
               (unsigned long)ulStatsAsPercentage);
    }
}

int main(void)
{
    /* 硬件初始化(串口、时钟等,由平台相关代码完成) */
    prvSetupHardware();

    /* 创建两个测试任务 */
    xTaskCreate(vTaskA, "TaskA", 128, NULL, 1, &xTaskAHandle);
    xTaskCreate(vTaskB, "TaskB", 128, NULL, 2, &xTaskBHandle);

    /* 启动调度器 */
    vTaskStartScheduler();

    /* 正常不会执行到这里 */
    return 0;
}

运行与验证

  1. 配置要求:在FreeRTOSConfig.h中确认已定义configGENERATE_RUN_TIME_STATS=1、configUSE_TRACE_FACILITY=1、configUSE_STATS_FORMATTING_FUNCTIONS=1,并实现运行时间计数器(如使用DWT周期计数器或定时器)。
  2. 编译烧录:将代码编译后烧录至目标板,打开串口终端(波特率与prvSetupHardware()中配置一致)。
  3. 观察输出:串口应周期性打印任务状态表与CPU占用百分比。状态码含义:1=就绪,2=运行,3=阻塞。
  4. 判定通过:TaskA状态码为3(阻塞),TaskB状态码为1或2(就绪/运行);TaskB的CPU占用百分比显著高于TaskA(因TaskA大部分时间阻塞)。若输出符合上述逻辑,则验证通过。

预期输出示例

=== 任务状态 ===
任务名  状态码  栈高水位(字)  运行计数
TaskA   3       117          12345
TaskB   1       98           987654

=== CPU运行时间统计 ===
TaskA     : 1%
TaskB     : 99%

以上输出为逻辑推导的示例值,实际数值取决于运行时长与任务调度时序。