FreeRTOS遍历所有任务的TCB并获得栈的最大使用量

FreeRTOS的任务栈的大小应该如何设置才能尽可能地不浪费内存呢?最直接的方法当然是运行一段时间任务,然后看看任务所使用的最大堆栈大小为多少,然后以此为基准多设置一点点。那么应该怎么实现呢?

1 原理

在我们创建一个任务的时候,有一个宏定义tskSET_NEW_STACKS_TO_KNOWN_VALUE,这里将其打开,这样在初始化一个任务的时候,它的任务栈的默认都填充为tskSTACK_FILL_BYTE,这样就可以方便我们判断栈溢出、获得栈的最大使用等情况。

#define tskSET_NEW_STACKS_TO_KNOWN_VALUE	1
#define tskSTACK_FILL_BYTE	( 0xa5U )
xTaskCreate
	prvInitialiseNewTask
		#if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
		{
			/* Fill the stack with a known value to assist debugging. */
			( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
		}
		#endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */

接下来我们就根据这个原理来输出任务栈的最大使用情况。FreeRTOS提供了一个函数uxTaskGetStackHighWaterMark,使用这个函数需要把宏定义INCLUDE_uxTaskGetStackHighWaterMark打开。

#define INCLUDE_uxTaskGetStackHighWaterMark     1
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )

这个函数的原理是从栈的最后开始往前数,直到数到第一个不为tskSTACK_FILL_BYTE的数,这样就是没有使用的栈空间大小,再用总的栈大小减去这个剩余的栈空间大小,就得到已经使用的栈空间的大小。

2 实现

从上面的分析可知,我们只需要提供各个任务的任务句柄TaskHandle_t,然后调用uxTaskGetStackHighWaterMark即可。但是我们有很多个任务,难道每个任务的任务句柄都要再写一个函数或者extern声明吗?任务句柄一般用在获取任务相关信息、挂起/恢复任务等操作上,实际上大部分任务是不需要使用任务句柄的,即在创建任务的时候填入NULL。

所以我们的目的就是写一个函数,它能遍历所有的任务并找到它保存在系统内部的TaskHandle_t


那么我们应该从何入手呢?我们知道系统中肯定有保存在readysuspend等状态下的任务的链表。首先来看一下uxTaskGetStackHighWaterMark函数找找思路

#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) )

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
	...
	TCB_t *pxTCB;
	pxTCB = prvGetTCBFromHandle( xTask );
	...
  • TaskHandle_t TCB_t *是同一个数据类型

也就是如何参数是NULL,就取当前正在运行的任务的TCB获取WaterMark。所以pxCurrentTCB就保存了当前运行任务的句柄,在它的定义处也找到了其它状态的链表变量声明。

/*lint -save -e956 A manual analysis and inspection has been used to determine
which static variables must be declared volatile. */
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;

/* Lists for ready and blocked tasks. --------------------
xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but
doing so breaks some kernel aware debuggers and debuggers that rely on removing
the static qualifier. */
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList1;						/*< Delayed tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList2;						/*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;				/*< Points to the delayed task list currently being used. */
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;		/*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t xPendingReadyList;						/*< Tasks that have been readied while the scheduler was suspended.  They will be moved to the ready list when the scheduler is resumed. */

在后面也看见了suspend状态的链表

#if ( INCLUDE_vTaskSuspend == 1 )
	PRIVILEGED_DATA static List_t xSuspendedTaskList;					/*< Tasks that are currently suspended. */
#endif

其中

typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
	volatile UBaseType_t uxNumberOfItems;
	ListItem_t * configLIST_VOLATILE pxIndex;			/*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
	MiniListItem_t xListEnd;							/*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
	listSECOND_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

在这些链表中肯定就有我们想要的TaskHandle_t ,所以我们要做到的就是遍历这些链表,但是我们还不知道这个链表的数据结构的哪一项保存了TCB,也不想再去细细研究这个结构体中的每个成员是怎么被赋值的。但我们知道系统肯定要从pxReadyTasksLists中取出最高优先级的任务来调度,函数如下:

	#define taskSELECT_HIGHEST_PRIORITY_TASK()															\
	{																									\
	UBaseType_t uxTopPriority = uxTopReadyPriority;														\
																										\
		/* Find the highest priority queue that contains ready tasks. */								\
		while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )							\
		{																								\
			configASSERT( uxTopPriority );																\
			--uxTopPriority;																			\
		}																								\
																										\
		/* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of						\
		the	same priority get an equal share of the processor time. */									\
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );			\
		uxTopReadyPriority = uxTopPriority;																\
	} /* taskSELECT_HIGHEST_PRIORITY_TASK */

我们也发现pxReadyTasksLists和其它状态的链表不一样,它是一个List_t 数组,我们就根据上面这个宏定义先写pxReadyTasksLists的遍历,代码如下:

UBaseType_t uxTopPriority = uxTopReadyPriority;
TCB_t *pTCB_TMP;
while(uxTopPriority-- != 0)
{
	if(!listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ))
	{
		listGET_OWNER_OF_NEXT_ENTRY( pTCB_TMP, &( pxReadyTasksLists[ uxTopPriority ] ) );
		/* 在此函数中调用water mark函数并输出 */
		log_stack_info(pTCB_TMP);
	}
}

接着就是其它的任务了,遍历流程都一样,这里以xSuspendedTaskList为例进行分析,因为大部分任务都在这里面。那又要怎么实现呢?很明显前面是通过listGET_OWNER_OF_NEXT_ENTRY将List中的TaskHandle取出来的,所以我们看一下这个宏定义:

#define listGET_OWNER_OF_NEXT_ENTRY(pxTCB, pxList)               \
{                                                                \
	List_t *const pxConstList = (pxList);                        \
	(pxConstList)->pxIndex = (pxConstList)->pxIndex->pxNext;     \
	if ((void *)(pxConstList)->pxIndex ==                        \
        (void *)&((pxConstList)->xListEnd))                      \
	{                                                            \
		(pxConstList)->pxIndex = (pxConstList)->pxIndex->pxNext; \
	}                                                            \
	(pxTCB) = (pxConstList)->pxIndex->pvOwner;                   \
}

也就是说链表的下一项在List_tpxIndex->pxNext中,而TCB在pxIndex->pvOwner中。我们注意到List_t中有一个成员项uxNumberOfItems即为该链表中的任务的个数,现在我们就可以来遍历了。

TCB_t *pTCB_TMP;
ListItem_t *pListCur;
pListCur = xDelayedTaskList1.pxIndex;
for(int i=0; i<xDelayedTaskList1.uxNumberOfItems; i++)
{
	pListCur = pListCur->pxNext;
	pTCB_TMP = pListCur->pvOwner;
	log_stack_info(pTCB_TMP);
}

这样我们就完成了对所有任务TCB的获取,在log_stack_info函数中获取watermark并输出即可:

staticvoid log_stack_info(TCB_t *pTmp)
{
	uint32_t total, mark;
	mark = uxTaskGetStackHighWaterMark((TaskHandle_t)pTmp);
	total = pTmp->pxEndOfStack - pTmp->pxStack;  /* portSTACK_GROWTH=-1 */
	mark = total - mark;
	log_d(......);  //自行格式化输出
}

注意:从各个状态的链表中获取这些TaskHandle的同时,任务有可能正好进行状态切换,所以执行上面函数时应该进入临界区。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tilblackout

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值