FreeRTOS-任务信息查询

  • FreeRTOS中提供了很多函数可以用来获取相应的任务信息,这里我们会深入分析vTaskGetInfo()、uxTaskGetSystemState()和vTaskList()这三个函数以及它们之间的一些联系。FreeRTOS中的任务信息查询函数列举如下:
函数名描述
uxTaskPriorityGet()获取某任务优先级
vTaskPrioritySet()改变某任务优先级
uxTaskGetSystemState()获取系统中所有任务状态信息
vTaskGetInfo()获取某个任务信息
xTaskGetApplicationTaskTag()获取某个任务的标签值
xTaskGetCurrentTaskHandle()获取当前正在运行的任务的任务句柄
uxTaskGetStackHighWaterMark()获取任务堆栈历史剩余最小值
eTaskGetState()获取某任务状态
pcTaskGetName()获取某任务名字
xTaskGetTickCount()获取系统时间计数器值
xTaskGetTickCountFromISR()在中断中获取时间计数器的值
xTaskGetSchedulerState()获取任务调度器的状态
uxTaskGetNumberOfTask()获取当前系统中存在的任务数量
vTaskList()以表格形式输出当前系统中所有信息
vTaskGetRunTimeStats()获取每个任务的运行时间
vTaskSetApplicationTaskTag()设置任务标签值

vTaskGetInfo()

  • 该函数几乎能获取单个任务的所有信息。下面来看一下该函数的函数声明:
	void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, 
	                   BaseType_t xGetFreeStackSpace, eTaskState eState )
	
  • 该函数输入参数有四个,TaskHandle_t xTask为所要查询任务的任务句柄;TaskStatus_t *pxTaskStatus为任务状态结构体,用于存放任务的状态,该结构体需要用户自定义;BaseType_t xGetFreeStackSpace为是否计算任务剩余最小堆栈大小,传入参数pdTRUE表示计算,但需要耗费一些时间;eTaskState eState为是否获取任务状态,输入参数eInvalid时表示获取任务状态,但同样也会消耗一些时间,否则用户指定任务状态,不会消耗时间。下面深入分析一下该函数源码。
	void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState )
	{
	TCB_t *pxTCB;

		/* xTask is NULL then get the state of the calling task. */
		pxTCB = prvGetTCBFromHandle( xTask );------(1)

		pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB;------(2)
		pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName [ 0 ] );------(3)
		pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority;------(4)
		pxTaskStatus->pxStackBase = pxTCB->pxStack;------(5)
		pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber;------(6)

		#if ( INCLUDE_vTaskSuspend == 1 ) ------(7)
		{
			/* If the task is in the suspended list then there is a chance it is
			actually just blocked indefinitely - so really it should be reported as
			being in the Blocked state. */
			if( pxTaskStatus->eCurrentState == eSuspended )
			{
				vTaskSuspendAll();
				{
					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
					{
						pxTaskStatus->eCurrentState = eBlocked;
					}
				}
				xTaskResumeAll();
			}
		}
		#endif /* INCLUDE_vTaskSuspend */

		#if ( configUSE_MUTEXES == 1 ) ------(8)
		{
			pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority;
		}
		#else
		{
			pxTaskStatus->uxBasePriority = 0;
		}
		#endif

		#if ( configGENERATE_RUN_TIME_STATS == 1 ) ------(9)
		{
			pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter;
		}
		#else
		{
			pxTaskStatus->ulRunTimeCounter = 0;
		}
		#endif

		/* Obtaining the task state is a little fiddly, so is only done if the value
		of eState passed into this function is eInvalid - otherwise the state is
		just set to whatever is passed in. */
		if( eState != eInvalid )  ------(10)
		{
			pxTaskStatus->eCurrentState = eState;
		}
		else
		{
			pxTaskStatus->eCurrentState = eTaskGetState( xTask );
		}

		/* Obtaining the stack space takes some time, so the xGetFreeStackSpace
		parameter is provided to allow it to be skipped. */
		if( xGetFreeStackSpace != pdFALSE ) ------(11)
		{
			#if ( portSTACK_GROWTH > 0 )
			{
				pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack );
			}
			#else
			{
				pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack );
			}
			#endif
		}
		else
		{
			pxTaskStatus->usStackHighWaterMark = 0;
		}
	}

(1)、获取该任务的任务句柄
(2)、获取任务句柄,存放在任务状态结构体中
(3)、获取任务名的首地址,存放在任务状态结构体中
(4)、获取当前任务优先级,存放在任务状态结构体中
(5)、获取任务堆栈首地址,存放在任务状态结构体中
(6)、获取任务标号,存放在任务状态结构体中
(7)、这段宏定义完成的任务就是,如果任务处于挂起状态,那么有可能它是被无限期阻塞所导致的,所以此时判断是否是这种可能,如果是则将它的状态反馈为阻塞态,而非挂起态。
(8)、这段条件编译是判断用户是否使用了互斥量,因为当用户使用了互斥量时,互斥信号量有可能会导致低优先级的任务优先级会拉高,这点在以后会讲述。所以这里如果使用了互斥量,那么就会获取其最初始的优先级大小,然后存放在任务状态结构体中
(9)、这段条件编译如果用户配置了任务事件信息统计,则会获取当前任务的运行时间。当配置configGENERATE_RUN_TIME_STATS == 1时需要用户自己再定义两个函数,具体配置会在后面的一个章节中讲述,这里只需要知道vTaskGetInfo()函数是可以获取任务运行时间的,不过本章没有用到。
(10)、判断用户传入的状态参数是否是无效的,如果是则获取当前任务状态但会耗费一些时间,否则将用户传入的状态参数作为当前任务状态。
(11)、是否获取任务历史最小剩余堆栈,如果传入参数pdTRUE,则会获取任务历史最小剩余堆栈同样会耗费一些时间,否则不获取。

  • 总结一下vTaskGetInfo()函数可以获取的任务信息有:
    1.任务句柄 xHandle
    2.任务名 pcTaskName
    3.任务当前优先级 uxCurrentPriority
    4.任务堆栈基地址 pxStackBase
    5.任务编号 xTaskNumber
    6.当前任务状态 eCurrentState
    7.任务初始优先级 uxBasePriority
    8.任务运行时间 ulRunTimeCounter
    9.任务历史剩余堆栈最小值 usStackHighWaterMark

函数用法示例:

	TaskStatus_t  TaskStatus;
	
	vTaskGetInfo(QueryTask_Handler,&TaskStatus,pdTRUE,eInvalid);
	
	printf("------------Get Task Info------------\r\n");
	printf("eCurrentState = %d\r\n",TaskStatus.eCurrentState);
	printf("pcTaskName = %s\r\n",TaskStatus.pcTaskName);
	printf("pxStackBase = %#x\r\n",(int)TaskStatus.pxStackBase);
	printf("usStackHighWaterMark = %d\r\n",TaskStatus.usStackHighWaterMark);
	printf("uxBasePriority = %ld\r\n",TaskStatus.uxBasePriority);
	printf("uxCurrentPriority = %ld\r\n",TaskStatus.uxCurrentPriority);
	printf("xTaskNumber = %ld\r\n",TaskStatus.xTaskNumber);
	printf("xHandle = %#x\r\n",(void *)TaskStatus.xHandle);
	printf("QueryTask_Handler= %#x\r\n",(void *)QueryTask_Handler);
	printf("------------Get Task Info------------\r\n");

uxTaskGetSystemState()

  • vTaskGetInfo()函数能够为我们自动获取绝大部分任务信息了,但是当任务比较多的时候,一个一个获取任务信息会比较麻烦,所以FreeRTOS为我么提供了更快捷的函数即uxTaskGetSystemState(),该函数与 vTaskGetInfo()函数区别在与, vTaskGetInfo()函数仅仅是获取某一任务的信息,而该函数是获取系统中所有任务的信息,其获取任务的信息数量和 vTaskGetInfo()函数是一样的,因为在uxTaskGetSystemState()中就是通过调用 vTaskGetInfo()函数来获取任务的。下面是其函数声明如下:
	UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, 
	                                  const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime )
	
  • 函数共有三个输入参数,TaskStatus_t * const pxTaskStatusArray为用户为存放系统所有任务信息申请的结构体数组;const UBaseType_t uxArraySize为结构体数组大小;uint32_t * const pulTotalRunTime用于存放任务的运行时间。下面简要分析一下该函数的源码。
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime )
	{
	UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;

		vTaskSuspendAll();-----(1)
		{
			/* Is there a space in the array for each task in the system? */
			if( uxArraySize >= uxCurrentNumberOfTasks )-----(2)
			{
				/* Fill in an TaskStatus_t structure with information on each
				task in the Ready state. */
				do-----(3)
				{
					uxQueue--;
					uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady );

				} while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

				/* Fill in an TaskStatus_t structure with information on each
				task in the Blocked state. */
				uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked );-----(4)
				uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked );-----(5)

				#if( INCLUDE_vTaskDelete == 1 )-----(6)
				{
					/* Fill in an TaskStatus_t structure with information on
					each task that has been deleted but not yet cleaned up. */
					uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted );
				}
				#endif

				#if ( INCLUDE_vTaskSuspend == 1 )-----(7)
				{
					/* Fill in an TaskStatus_t structure with information on
					each task in the Suspended state. */
					uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended );
				}
				#endif

				#if ( configGENERATE_RUN_TIME_STATS == 1)-----(8)
				{
					if( pulTotalRunTime != NULL )
					{
						#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
							portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) );
						#else
							*pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
						#endif
					}
				}
				#else
				{
					if( pulTotalRunTime != NULL )
					{
						*pulTotalRunTime = 0;
					}
				}
				#endif
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		( void ) xTaskResumeAll();-----(9)

		return uxTask;
	}

(1)、挂起任务调度器,停止任务调度,以免任务打断,或任务状态信息改变。
(2)、判断定义的结构体数组大小是否比当前系统所有任务数量,即判断是否能够存储得下系统中所有任务的信息。
(3)、获取处于准备态任务的信息。uxQueue表示任务的优先级,按照高优先级到低优先级的顺序来获取任务信息。prvListTasksWithinSingleList()函数是用来获取单个任务的信息,在该任务中调用了vTaskGetInfo()函数,该函数返回值为同一优先级同一状态的任务数量。uxTask用以统计获得信息的任务个数。
(4)、获取处于阻塞态延时列表中任务的信息。
(5)、获取处于阻塞态延时溢出列表中任务信息,因为任务阻塞时可能会产生溢出,所以用这两个列表共同表示阻塞态任务。
(6)、获取未完全删除的任务信息。
(7)、获取处于挂起态任务的信息。
(8)、获取任务运行时间。本章节没有用到该信息
(9)、调度器解挂。

函数用法示例:

   TaskStatus_t* StatusArray;
	UBaseType_t ArraySize ;
	ArraySize = uxTaskGetNumberOfTasks();
	StatusArray = pvPortMalloc(ArraySize * sizeof(TaskStatus_t));//动态申请空间
	
	printf("------------Get System State------------\r\n");
	if (StatusArray != NULL)
	{
		ArraySize = uxTaskGetSystemState(StatusArray, ArraySize, &pulTotalRunTime);
		//printf("TaskName\t\tPriority\tTaskNumber\truntime\r\n");
		for (i=0; i<ArraySize; i++)
		{
			
		  printf("%d\t%s\t%#x\t%d\t%ld\t%ld\t%#x\t%ld\t\r\n",
			       StatusArray[i].eCurrentState,
						 StatusArray[i].pcTaskName,
						 StatusArray[i].pxStackBase,
						 StatusArray[i].usStackHighWaterMark,
			       StatusArray[i].uxBasePriority,
						 StatusArray[i].uxCurrentPriority,
			       StatusArray[i].xHandle,
			       StatusArray[i].xTaskNumber
			  );
		}			
	}	

uxTaskGetNumberOfTasks()除了可以获得与vTaskGetInfo()函数相同的信息外还可以获取系统中任务总数,此任务总数为该函数的返回值。

vTaskList()

  • 前面介绍的两个函数功能都比较强大,可以获取任务的绝大部分信息,但是很多时候,我们并不需要这么多信息,我们只需要几个常用的关键信息即可。所以FreeRTOS又为我们提供了vTaskList(),该函数不仅可以获取关键信息,而且用法简单。下面是其函数声明:
void vTaskList( char * pcWriteBuffer )

该函数的函数声明很简单,输入形参就一个字符指针。该指针需要用户自定义,用以存放任务信息。函数源码这里就不分析了。我们直接给出其用法示例:

  char InfoBuffer[200];
  vTaskList(InfoBuffer);
  printf("taskName\ttaskState\ttaskPrio\ttaskStack\ttaskNum\r\n");
  printf("%s",InfoBuffer);

下图是其输出信息
在这里插入图片描述
从上图可以看出,该函数可以获取任务状态,优先级,剩余堆栈大小,任务序号这四项信息。


其他的一些API函数比较简单,或多或少都有这三个函数的影子,在此就不一 一列举说明了

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐