FreeRTOS任务栈查看与运行时间统计(ESP-IDF框架为例)


在FreeRTOS中,监控任务的堆栈使用情况和运行时间统计是优化系统性能、排查资源问题的重要手段。一开始在创建任务时需要分配的栈大小,一般都是用自己的经验,但是任务栈空间的实际使用量会随着任务执行和中断处理过程上下浮动

分配栈过小会溢出,导致触发看门狗一直复位,如果分配过大则又导致资源的浪费。这时就需查看任务线程栈的大小了,使用栈情况,进行针对优化

检查任务栈最高水位

有一个函数可以查看任务剩余栈的最高水位,也就是还有多少空余的栈空间

原理是创建任务时候,栈固定的填充了0xa5(FreeRTOS的作法,这里无需自己实现),从栈底往栈顶逐个字节地判断,它们的值持续是 0xa5 就表示它是空闲的

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

任务运行时、任务被切换时,都会用到栈。栈里原来值(0xa5)就会
被覆盖。逐个函数从栈的尾部判断栈的值连续为 0xa5 的个数,它就是任务运行过程中空闲内存容量的最小值。注意:假设从栈尾开始连续为 0xa5 的栈空间是 N 字节,返回值是 N/4。

返回值为这个任务栈使用的最高水位线,所谓最高水位线即自任务开始运行以来,任务所使用的栈最大时所剩余的栈大小。因此此值越大,代表任务栈剩余量越大

例如

typedef struct
{
    uint32_t highWater;  // the high water mark in words
    uint32_t remainByte; // this is the number of byte
    uint8_t remainPercent;
} TaskStackCheckInfoStruct;


/**
 * ******************************************************************************
 * @brief 	: 更新任务堆栈信息
 * @param 	: taskName - 任务名称
 * @param 	: taskHandle - 任务句柄
 * @param 	: pStackInfo - 用于存储堆栈信息的结构体指针
 * @param 	: taskStackSize - 任务堆栈大小
 * @note	: None
 * ******************************************************************************
 */
static void taskStackInfoUpdate(char *taskName,
                                TaskHandle_t taskHandle,
                                TaskStackCheckInfoStruct *pStackInfo,
                                uint32_t taskStackSize)
{
     // check if the parameter is available
    if (NULL == taskHandle || NULL == taskName ||
        NULL == pStackInfo || 0 >= taskStackSize)
    {
        return;
    }
    
    uint32_t highWater = uxTaskGetStackHighWaterMark(taskHandle);
    // make sure that an integer overflow does not occur when calculating remain percent
    if (taskStackSize > UINT32_MAX / highWater)
    {
     	return;
    }
    pStackInfo->highWater     = highWater;
    pStackInfo->remainPercent = (highWater * 100) / taskStackSize;
    pStackInfo->remainByte    = highWater * 4;

    LOG_INFO(MOD_TAG, "task:%s,remain percent:%u%%", taskName, pStackInfo->remainPercent);
    LOG_INFO(MOD_TAG, "task:%s,remain byte:%u byte", taskName, pStackInfo->remainByte);
}

系统所有任务运行情况统计

FreeRTOS通过vTaskGetRunTimeStats函数统计每个任务的CPU占用率,其本质是记录任务在调度器中处于运行状态的总时间。实现需要两个前提:

  1. 高精度定时器:统计任务运行时长的基准时钟(建议使用ESP32的CPU时钟)
  2. 运行时状态记录:内核在每个任务切换时记录时间戳差值

注意:如果是stm32,需要自己实现上述,具体网上有其他资料可参考,如果是esp-idf,则已经集成,打开宏定义即可使能使用

配置关键宏定义

需在menuconfig中开启以下配置:

宏定义作用配置路径
configUSE_TRACE_FACILITY启用内核跟踪功能Component config → FreeRTOS → Enable FreeRTOS trace facility
configUSE_STATS_FORMATTING_FUNCTIONS启用统计格式化函数同上路径下勾选该选项
configGENERATE_RUN_TIME_STATS启用运行时统计生成Component config → FreeRTOS → Enable FreeRTOS run time stats

如图所示
在这里插入图片描述

代码实现全任务统计输出

核心API函数

/* 生成任务状态列表(含堆栈信息) */
void vTaskList(char *pcWriteBuffer);

/* 生成CPU运行时统计(百分比形式) */ 
void vTaskGetRunTimeStats(char *pcWriteBuffer);

统计任务实现

static void memoryInfoTaskEntry(void *pParams)
{
    (void)pParams;

    static uint32_t lastWakeTime;
    lastWakeTime = xTaskGetTickCount();

    uint8_t infoBuffer[512];
    uint8_t timeBuffer[512];
    for (;;)
    {
        memset(infoBuffer, 0, sizeof(infoBuffer));
        vTaskList((char *)&infoBuffer);
        ESP_LOGI(MOD_TAG, "\r\n-name-     -status- -priority- -stack- -num- -core-\r\n%s", infoBuffer);

        memset(timeBuffer, 0, sizeof(timeBuffer));
        vTaskGetRunTimeStats((char *)&timeBuffer);
        ESP_LOGI(MOD_TAG, "\r\n-name-         -runTime-       -percent-\r\n%s", timeBuffer);

        vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(2000);
    }
}

效果:
在这里插入图片描述

  • 第一列,任务名称
  • 第二列,任务状态
  • 第三列,任务优先级(数值越大,优先级越高)
  • 第四列,任务栈,数值越大,代表剩余的任务栈空间越大,单位Byte,注意此值为当前剩余栈大小,而不是峰值剩余栈大小
  • 第五列,任务号,代表任务创建顺序
    第六列,任务内核,代表任务所在内核ID

查询任务运行时间占用率

效果:
在这里插入图片描述

优化

  • 堆栈优化
    经验值法:初始分配时采用保守值(如4KB)
    水位检测法:通过uxTaskGetStackHighWaterMark监测实际使用量
  • CPU负载优化
    目标值:单任务CPU占比一般不超过70%
    异常排查:持续100%占用 → 任务中缺少阻塞调用(如vTaskDelay),多个任务高占比 → 考虑提升CPU主频或优化算法
  • 注意事项
    内存开销:启用统计功能会增加约5-10KB的ROM占用
    统计间隔:建议5秒以上间隔,高频统计可能影响系统实时性
    线程安全:vTaskList和vTaskGetRunTimeStats不是原子操作,建议在独立任务中调用

通过结合栈水位检测与运行时统计,可以精准把控系统资源使用情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值