概述
任务状态小节
前述已经学习了 FreeRTOS 的任务的创建、延时、挂起、任务通知的相关知识。本小节对任务的状态进行总结,并给出使用标准的 API 查询系统中所有任务状态的方法。这不仅对加深我们对 Task 的理解有重要意义,也是系统出现问题时,查询系统bug的重要方法。
当前,相关的文档将 RTOS 中的任务状态分为五种:
- 运行状态:当前正在执行的任务的状态,只可能会一个当前正在执行的任务。初运行态外,下述的情况都可以归类到 非运行 态。
- 就绪状态:随时可以运行的任务的状态,就绪状态的任务随时等待任务调度器调度,是在被任务调度器赋予 CPU 使用权之前的状态。刚创建的任务立即进入就绪状态,比如系统中正在运行优先级为 2 的任务,优先级为1 的任务因为优先级低而未进入运行状态时,就处于就绪状态。
- 阻塞状态:任务因为某些原因暂时不能被任务调度器赋予 CPU 使用权状态。一般情况下正在等待某些事件的发生比如调用了 xTaskNotifyWait() 等待任务通知的任务,或者等待延时时间结束的事件比如调用了 xTaskDelay() 的任务,在一段时间内任务会被阻塞,在这些事件达成后任务会自动回到就绪状态。
- 挂起状态:vTaskSuspend()函数会让任务进入挂起状态,这时候这个任务不会执行。调用xTaskResume()函数才能让这些任务回到就绪状态
- 删除状态:一个任务被使用vTaskDelete()函数后被删除,处于删除状态。
我在RTOS 任务状态总结中将 RTOS 的系统状态分为五种:未创建态、就绪态、运行态、挂起态、延时态五种状态。将阻塞(因延时而阻塞、因等待通知而阻塞)的两种情况划分为延时态。其实大致意思是相同的,大家按照最好理解的方式理解记忆就可以了。
典型的让任务进入几种状态的方法是:
API | Status |
---|---|
xTaskCreate() | 调用后进入就绪态 |
xTaskDelay() | 调用后进入阻塞态,等待计时结束 |
xTaskNotifyWait() | 调用后进入阻塞态,等待任务通知 |
vTaskSuspend() | 调用后进入挂起态,需使用 xTaskResume() 才能解除挂起 |
vTaskDelete() | 调用后进入删除态 |
细心的读者可能已经发现了,没有 API 可以让 任务进入运行态,这是因为任务是否进入运行态,是调度器自动进行管理的,关于调度器的基本介绍,读者可回顾RTOS 中的任务调度与三种任务模型。
使用 API 查询任务的状态
1)可以通过 vTaskGetInfo()
获取某个任务当前的信息:
void vTaskGetInfo(TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState)
该函数将任务的信息写到 TaskStatus 类型的变量中,该变量包含了任务的所有信息:
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char * pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See https://www.FreeRTOS.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
StackType_t * pxStackBase; /* Points to the lowest address of the task's stack area. */
configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */
#if configTASKLIST_INCLUDE_COREID
BaseType_t xCoreID; /*!< Core this task is pinned to (0, 1, or -1 for tskNO_AFFINITY). This field is present if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID is set. */
#endif
} TaskStatus_t;
2)相比 vTaskGetInfo()
,vTaskList()
可以获取当前系统中所有任务的状态信息。
void vTaskList(char *pcWriteBuffer)
需求及功能解析
示例通过函数打印当前系统中各个任务的状态。
示例解析
示例中通过上述介绍的函数打印当前系统中各任务的状态,相关 log:
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, Minimum free heap size: 295172 bytes
TASK3: task3_flag = 0
app_main task status: xTaskNumber=4 eCurrentState=0 uxCurrentPriority=1
Task Name Status Prio HWM Task# Affinity
task4 X 24 1296 10 -1
main R 1 2368 4 0
IDLE R 0 1068 6 1
IDLE R 0 1108 5 0
task3 B 22 472 9 -1
esp_timer S 22 3656 3 0
ipc1 B 24 1116 2 1
task2 S 23 1600 8 -1
task1 S 24 1656 7 -1
ipc0 B 24 1076 1 0
Task Name Status Prio HWM Task# Affinity
task4 X 24 768 10 -1
IDLE R 0 1068 6 1
IDLE R 0 1108 5 0
task3 B 22 472 9 -1
ipc1 B 24 1116 2 1
task2 S 23 1600 8 -1
task1 S 24 1656 7 -1
ipc0 B 24 1076 1 0
esp_timer S 22 3656 3 0
以上 log 打印了一次 main task 的相关信息,即 main task 的任务序号为4,执行该打印时的状态为X(正在执行),优先级为1。不了解这个 main task 的可以参考该博客。
以上 log 打印了两次 系统中所有任务 的状态。包括、任务的运行状态(Status)、优先级(Prio)、高水位线(HWM)、任务序号(Task#,通常是任务被创建的顺序)、CPU核心(Affinity)。
其中任务状态中相关的字母略缩写含义为:blocked (‘B’), ready (‘R’), deleted (‘D’),suspended (‘S’),Execute(X).
讨论
负责打印 Task Status 的 任务的优先级该如何安排?
总结
1)任务可以分为几种状态:运行态、非运行态(包括就绪、阻塞(等待延时结束的阻塞和等待事件到来的阻塞)、挂起、删除)。
2)查看任务的状态可以使用函数:vTaskGetInfo()
,vTaskList()
。
作为入门,当前对 RTOS 中任务的相关介绍先写到这里,后续待介绍完更多的基础知识,将通过 任务-高级篇 介绍更多 RTOS 任务使用的知识和方法。
资源链接
1)Learning-FreeRTOS-with-esp32 系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)
3)下一篇:RTOS 任务同步与消息通信篇概述