FreeRTOS踩坑小记——vTaskList函数的使用
前言
在 FreeRTOS 中,vTaskList() 是一个用于调试的实用函数,用于输出系统中所有任务的详细状态信息。我在使用中遇到了坑,今天分享出来,希望大家不会踩到。
一、vTaskList函数的简要介绍
1.1 函数原型与作用
void vTaskList( char *pcWriteBuffer );
- 作用:将系统中所有任务的状态、堆栈使用情况等信息格式化为字符串,存储到
pcWriteBuffer中。 - 依赖配置:需在
FreeRTOSConfig.h中定义:#define configUSE_TRACE_FACILITY 1 // 启用追踪功能 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 启用状态格式化
1.2 输出信息格式
vTaskList() 会生成一个表格,包含以下字段:
Name State Priority Stack Num
<任务名称> <状态> <优先级> <剩余栈> <编号>
- 字段说明:
- Name:任务名称(创建时通过
pcName参数指定)。 - State:任务状态缩写:
R= 运行态(Running)B= 阻塞态(Blocked)D= 删除态(Deleted,等待清理)S= 挂起态(Suspended)R= 就绪态(Ready)
- Priority:任务当前优先级。
- Stack:任务堆栈的剩余空间(字节),反映堆栈使用情况。
- Num:任务编号(唯一标识符,由内核分配)。
- Name:任务名称(创建时通过
二、踩坑概述
我在FreeRTOS中创建了2个任务,一个任务负责写入队列,另一个是任务负责读取队列。读取队列的任务优先级较高。我在写队列的任务函数中调用了 vTaskList(),结果导致HardFault。
下面是代码:
这个任务函数做的是在队列里写数据。
static void vSenderTask(void *pvParameters) // 发送任务
{
BaseType_t xStatus = pdFAIL;
const TickType_t xTicksToWait = pdMS_TO_TICKS(100UL);
char InfoBuffer[100]; //保存信息的数组
vTaskList(InfoBuffer); //获取所有任务的信息
printf("%s\r\n",InfoBuffer); //通过串口打印所有任务的信息
printf("B 阻塞态\tR 就绪态\tS 挂起态\tD 删除态\tR 也是运行态\r\n");
for(;;)
{
if(NULL != xQueue)
{
xStatus = xQueueSendToBack(xQueue, pvParameters, xTicksToWait);
}
else
{
printf("NULL == xQueue.\r\n");
}
if(xStatus != pdPASS)
{
printf("Can not send to the queue.\r\n");
}
}
}
队列的创建和任务的创建过程如下:
typedef enum
{
eMotorSpeed,
eSpeedSetPoint
}ID_t;
typedef struct Data_Queue
{
ID_t ID;
uint32_t DataValue;
}Data_t;
static const Data_t DataArray[] =
{
{eMotorSpeed, 100}, /*CAN 任务发送的数据*/
{eSpeedSetPoint, 200}, /*HMI任务发送的数据*/
};
static QueueHandle_t xQueue = NULL;/*队列句柄*/
xQueue = xQueueCreate(5, sizeof(Data_t));
xTaskCreate(vSenderTask, "CAN Task Sender", 100, (void *)&(DataArray[0]), 2, CAN_Sender_Handler);
xTaskCreate(vReceiverTask, "Receiver", 100, NULL, 1, Receive_Handler);
看到这里的童鞋不知道有没有看出来问题?
三、问题及解决
3.1 问题1:在任务的执行函数中定义了大数组导致栈溢出

在 C 语言中,函数内定义的局部变量(包括数组)存储在栈上。在 FreeRTOS 中,每个任务有独立的栈空间,创建任务时通过 xTaskCreate() 的 usStackDepth 参数指定:

很明显,栈空间一共才100字节,怎么可以在里面开辟100字节的空间呢?在任务函数中定义大数组,会直接消耗栈空间,可能导致以下问题:
-
栈溢出风险
- 后果:栈溢出会覆盖相邻内存(如任务控制块 TCB),导致 HardFault、任务崩溃或系统不稳定。
- 调试提示:若任务频繁崩溃或进入 HardFault,可能是栈溢出。
-
内存碎片化
每个任务的栈空间在创建时固定分配。若任务栈过大:- 系统总内存消耗增加,可能导致后续任务创建失败。
- 内存碎片化加剧,影响动态内存分配效率。
3.1.1 解决问题
- 方法一:动态内存分配
使用pvPortMalloc()在堆上分配内存:
char * InfoBuffer= (char *)pvPortMalloc(1000 * sizeof(char));
if (InfoBuffer != NULL)
{
vTaskList(InfoBuffer); //获取所有任务的信息
printf("%s\r\n",InfoBuffer); //通过串口打印所有任务的信息
printf("B 阻塞态\tR 就绪态\tS 挂起态\tD 删除态\tR 也是运行态\r\n");
vPortFree(InfoBuffer); // 使用后释放
}
- 方法二:增加任务栈深度
计算数组和局部变量总大小,适当增加usStackDepth:
// 为任务分配更大的栈空间
xTaskCreate(vSenderTask, "CAN Task Sender", 1000, (void *)&(DataArray[0]), 2, CAN_Sender_Handler);
- 方法三:使用静态全局数组
将数组定义为static或全局变量,存储在数据段而非栈上:
static char InfoBuffer[1000]; // 全局静态数组,不占用栈空间
static void vSenderTask(void *pvParameters) // 发送任务
{
vTaskList(InfoBuffer); //获取所有任务的信息
printf("%s\r\n",InfoBuffer); //通过串口打印所有任务的信息
printf("B 阻塞态\tR 就绪态\tS 挂起态\tD 删除态\tR 也是运行态\r\n");
//其他代码
}
- 检测栈溢出
在 FreeRTOSConfig.h 中定义宏:
#define configCHECK_FOR_STACK_OVERFLOW 1 //大于0时启用堆栈溢出检测功能,如果使用此功能
实现钩子函数:
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
printf("Stack overflow detected in task: %s\n", pcTaskName);
// 处理溢出(如重启系统)
}
3.2 问题2:分配给vTaskList函数的参数buffer太小

3.2.1 解决问题
需要至少分配512字节的空间,如果任务很多的话,需要更多空间。
char InfoBuffer[1000]; //保存信息的数组
四、调用 vTaskList() 导致硬 fault其他错误可能的原因
若调用 vTaskList() 导致硬 fault(HardFault),通常是以下原因:
- 缓冲区溢出:增大
buffer大小。 - 栈空间不足:增加调用该函数的任务的栈大小。
- 未启用追踪功能:检查
FreeRTOSConfig.h配置。 - 中断中调用:确保只在任务上下文中调用。
五、总结
vTaskList() 是 FreeRTOS 中强大的调试工具,通过输出任务状态和资源使用情况,帮助开发者快速定位系统问题。但需注意需要给调用的任务足够的栈空间,尤其是任务很多的话,需要分配更多字节。另外还需要给vTaskList() 函数准备足够的InfoBuffer。
此外,调用 vTaskList() 时会暂停所有任务,可能影响系统实时性,仅用于调试。

1515

被折叠的 条评论
为什么被折叠?



