vTaskList() 介绍
使用 ESP32
/ESP8266
进行开发时,读者可通过 vTaskList()
来协助分析操作系统当前 task 状态,以帮助优化内存,帮助定位栈溢出问题,帮助理解和学习操作系统原理相关知识。
读者若想深入了解
vTaskList()
, 可参考 vTaskList() 英文原版介绍 相关文档。
vTaskList() 使用
注意:
使用 vTaskList()
前需使能:
make menuconfig
->Component config
->FreeRTOS
->Enable FreeRTOS trace facility
make menuconfig
->Component config
->FreeRTOS
->Enable FreeRTOS trace facility
->Enable FreeRTOS stats formatting functions
通过上面配置,等同于使能
FreeRTOSConfig.h
中如下两个宏:
configUSE_TRACE_FACILITY
和configUSE_STATS_FORMATTING_FUNCTIONS
参考代码
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
void esp_print_tasks(void)
{
char* pbuffer = (char*) malloc(2048);
memset(pbuffer, 0x0, 2048);
printf("--------------- heap:%u ---------------------\r\n", esp_get_free_heap_size());
vTaskList(pbuffer);
printf("%s", pbuffer);
printf("----------------------------------------------\r\n");
free(pbuffer);
}
void test_task(void* param)
{
while(1) {
esp_print_tasks();
vTaskDelay(3000 / portTICK_RATE_MS);
}
}
打印一次 task 情况可调用:
esp_print_tasks();
循环打印 task 情况可调用:
xTaskCreate(test_task, "test_task", 2048, NULL, 5, NULL);
vTaskList() 结果和高能预警
笔者可以去
task.c
中查看vTaskList()
相关实现逻辑,或更有助于读者理解。
例如: 基于 ESP8266, release/v3.3, commit:4c38ff31
, 在 app_main()
中调用 vTaskList()
结果如下:
-
第一列: task name
即xTaskCreate
创建该 task 时第二个参数。
如果名称过长,会根据configMAX_TASK_NAME_LEN
截断。 -
第二列: task 当前状态
X
: runningB
: blockedR
: readyD
: deletedS
: suspended
-
第三列: task 优先级
即xTaskCreate
创建该 task 时第四个参数。
数字越大,优先级越高,建议客户设置 task 优先级在 1-9 之间,慎行! -
第四列: 最小剩余 task 栈空间,字节为单位
在xTaskCreate
创建 task 时,给定的第三个参数值代表该 task 调度和运行过程中,最大可用 task 栈空间,以字节为单位(读者可以修改portSTACK_TYPE
宏来决定是否以字节还是4字节为单位);
Task
如果 API 调用比较深,则使用的栈空间越大,也就意味着最小剩余 task 栈空间越小。高能预警,做好笔记,熟读三遍:
A:
当某个 task 最小剩余 task 栈空间比较大时,适当减小xTaskCreate
创建该 task 时给定的第三个参数值,可节约 DRAM,以优化系统内存。B:
当某个 task 最小剩余 task 栈空间比较小时,适当增大xTaskCreate
创建该 task 时给定的第三个参数值,可降低 task 栈溢出风险。C:
如果对 SDK 没有深入了解,不要修改 系统 task 优先级和分配的最大可用栈空间。D:
减少应用代码 task 的个数,以优化内存。
例如:
多个socket
数据流,可以通过select()
放在同一个 task 里处理;
而不是一个socket
数据流,一个 task;
更不要一个socket
数据流,居然三个 task (接收 task, 发送 task, 处理 task), it is amazing!E:
占用空间较大的变量,尽可能通过 malloc/calloc 等动态申请释放,以提高栈空间利用率。 -
第五列: task 创建顺序