FreeRTOS内核源码解读之-------列表和列表项(一)

最近一直在看关于FreeRTOS内核源码,里面涉及很多列表和列表项的内容,因此需要好好研究一下。这部分内容在FreeRTOS中起着很重要的作用,如果把FreeRTOS比作一个人的话,那么列表和列表项就相当于人的神经系统,管理整个FreeRTOS系统有条不紊的运行。
首先,要明确列表项和列表是两个不同的概念,在FreeRTOS中是两个不同的数据结构。这一点,我在刚开始接触的时候经常混淆。通俗点说,列表是用来表示一条条双向链表,列表项就是在链表中的每一项。类比一下:列表就是一条条铁链子,列表项就是一条铁链子上面的铁环。
将用两篇文章解读FreeRTOS中关于列表项和列表的知识,第一篇主要介绍源码中关于列表项和列表数据结构,以及操作函数。

一、关于结构体

1、列表项数据结构

列表项数据结构如下图所示:
列表项数据结构

struct xLIST_ITEM
{
	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/*< 捕捉已知的数值在应用程序运行期间是否被损坏,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES非0时有效,此时为TickType_t xListItemIntegrityValue1; */
	configLIST_VOLATILE TickType_t xItemValue;			/* 列表项数值,一般对列表项进行排序时会依据此数值。由于使用的是32位的MCU,因此该数据项的类型为uint32_t */
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;		/*< 列表项是采用双向链表的形式连接在链表中的,该数据项用于指向前一个列表项 */
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;	/* 指向后一个列表项 */
	void * pvOwner;										/* 一般是指向TCB指针,用来指向包含本列表向对象的那一个线程控制块(TCB)指针,线程控制块对象和列表项对象之间是双向连接(你指向我,我指向你)。*/
	void * configLIST_VOLATILE pvContainer;				/* 用来指向该列表项对象挂接的列表对象的指针,用来说明具体挂接在哪个列表上面 */
	listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE			/* 同listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE,用于判断运行期间数据是否被破坏 */
};
typedef struct xLIST_ITEM ListItem_t;					/* For some reason lint wants this as two separate definitions. */
2、mini列表项数据结构

mini列表项数据结构如下图所示:
mini列表项数据结构

struct xMINI_LIST_ITEM
{
	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/*捕捉已知的数值在应用程序运行期间是否被损坏,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES非0时有效,此时为TickType_t xListItemIntegrityValue1;  */
	configLIST_VOLATILE TickType_t xItemValue;  /*列表项数值,一般对列表项进行排序时会依据此数值。由于使用的是32位的MCU,因此该数据项的类型为uint32_t */
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;  /*< 列表项是采用双向链表的形式连接在链表中的,该数据项用于指向前一个列表项 */
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  /* 指向后一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
3、列表数据结构

列表数据结构如下图所示:
列表数据结构

typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*捕捉已知的数值在应用程序运行期间是否被损坏,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES非0时有效, TickType_t xListIntegrityValue1 */
	configLIST_VOLATILE UBaseType_t uxNumberOfItems;  /* 记录列表中含有列表项的个数 */
	ListItem_t * configLIST_VOLATILE pxIndex;			/*< 用于遍历列表。 指向通过调用listGET_OWNER_OF_NEXT_ENTRY()返回的下一项。 */
	MiniListItem_t xListEnd;							/*<列表最后一个列表项,该列表项是一个mini列表项 */
	listSECOND_LIST_INTEGRITY_CHECK_VALUE				/*<捕捉已知的数值在应用程序运行期间是否被损坏,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES非0时有效, TickType_t xListIntegrityValue2 */
} List_t;

二、关于操作函数

1、listSET_LIST_ITEM_OWNER
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )		( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) ) //用于设置一个列表项数据结构中的pxOwner项,一般是一个任务控制块指针。
2、listGET_LIST_ITEM_OWNER
#define listGET_LIST_ITEM_OWNER( pxListItem )	( ( pxListItem )->pvOwner ) //用于获得列表项所有者,实际上获得某一个列表是属于哪一个任务控制块
3、listSET_LIST_ITEM_VALUE
//用于设置列表项结构体的xItemValue项,该数值用来表示每一个列表项的数值,一般列表项依据此值进行列表的插入排序。
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )	( ( pxListItem )->xItemValue = ( xValue ) )
4、listGET_LIST_ITEM_VALUE
//得到列表项的数值,即xItemValue域的数值。该数值表示多种意义,可能是任务的优先级或者解除任务阻塞的时间,具体代表的意义与挂接的列表有关。
#define listGET_LIST_ITEM_VALUE( pxListItem )	( ( pxListItem )->xItemValue )
5、listGET_ITEM_VALUE_OF_HEAD_ENTRY
//得到列表中第一个列表项的xItemValue值。
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext->xItemValue )
6、listGET_HEAD_ENTRY
//得到列表中第一个列表项,注意这里返回是一个指针。
#define listGET_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext )
7、listGET_NEXT
//得到某一个列表项的指向的前一个列表项。
#define listGET_NEXT( pxListItem )	( ( pxListItem )->pxNext )
8、listGET_END_MARKER
//返回列表中最后一个列表项,注意原来最后一个列表项为xMINI_LIST_ITEM型的列表项,返回值强制转换为xLIST_ITEM
#define listGET_END_MARKER( pxList )	( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
9、listLIST_IS_EMPTY
//判断列表是否有没有包含列表项
#define listLIST_IS_EMPTY( pxList )	( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )
10、listCURRENT_LIST_LENGTH
//返回列表中包含的列表项个数
#define listCURRENT_LIST_LENGTH( pxList )	( ( pxList )->uxNumberOfItems )
11、listGET_OWNER_OF_NEXT_ENTRY
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )										\
{	// pxTCB返回任务控制块,查找的列表																						\
List_t * const pxConstList = ( pxList );													\
	/* Increment the index to the next item and return the item, ensuring */				\
	/* we don't return the marker used at the end of the list.  */							\
	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;							\//指向下一个列表项
	if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )	\//是否到达最后一个列表项
	{																						\
		( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;						\//双向链表,指向第一个列表项
	}																						\
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;											\返回任务控制块指针
}

用于获取列表中下一个列表项的所有者(即列表项与哪一个任务控制块建立双向连接)。这个主要是用在就绪任务优先级查找。举一个情况:当系统中最高优先级下多个任务同时就绪,这个时候就需要多个任务分时段执行,这是决定哪一个任务执行就是通过listGET_OWNER_OF_NEXT_ENTRY进行决定的。

12、listGET_OWNER_OF_HEAD_ENTRY
//用于获取列表中第一个列表项的所有者(列表项中链接的任务控制块)。
#define listGET_OWNER_OF_HEAD_ENTRY( pxList )  ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
13、listIS_CONTAINED_WITHIN
//检查列表项是否在列表中。 列表项维护一个pvContainer指针,该指针指向它所在的列表。此宏所做的只是检查容器和列表是否匹配。
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) )
14、listLIST_ITEM_CONTAINER
//返回列表项挂接在哪个列表上。
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer )
15、listLIST_IS_INITIALISED
//这提供了一种粗略的方法来知道列表是否已初始化,因为pxList-> xListEnd.xItemValue通过vListInitialise()函数设置为portMAX_DELAY。
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
16、函数vListInitialise

列表初始化函数,该函数主要做的任务是初始化列表结构体的各数据项;并且将列表中的最后一个列表项内的数据项初始化。具体看下面代码注释。

void vListInitialise( List_t * const pxList )
{
	/* 列表结构体中含有一个mini列表项,作为该列表的最后一个列表项,将其赋值给pxIndex。这样做可以节省内存 */
	pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );			/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */

	/* 将列表中的最后列表项的列表数值赋值为portMAX_DELAY(0xffffffff)。一般列表中的列表项是按照.xItemValue的值从小到大排序的 */
	pxList->xListEnd.xItemValue = portMAX_DELAY;

	/* 由于初始化的时候,仅有一个列表项,而且列表是一个双向链表结构。因此,要将列表项的 pxNext 和pxPrevious 指向自身 */
	pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );	
	pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); 
	pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//设置列表含有列表项的个数,初始化为0

	/* 前面说过用于检查该列表项内存是否被覆盖,主要受宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES控制,如果设置为非零,那么将会给( pxList )->xListIntegrityValue1 和( pxList )->xListIntegrityValue2 设置为0x5a5a5a5aUL */
	listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
	listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
17、函数vListInitialiseItem

列表项初始化函数,主要是用来将列表项pvContainer设置为NULL。此时就表示初始化了列表项,其他的数据项不进行操作

void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* 确保列表项为记录在列表中 */
	pxItem->pvContainer = NULL;// pvContainer表示本列表项挂接在具体哪一个列表上

	/*前面说过用于检查该列表项内存是否被覆盖,主要受宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES控制,如果设置为非零,那么将会给( pxItem )->xListItemIntegrityValue1 和( pxItem )->xListItemIntegrityValue2 设置为0x5a5a5a5aUL*/
	listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
	listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
18、函数vListInsertEnd

默认列表如下图所示:
在这里插入图片描述末尾插入值为50的列表项:
在这里插入图片描述

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;

	/* 用于检查内存是否被覆盖 */
	listTEST_LIST_INTEGRITY( pxList );
	listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

	/* 下面四步是进行双向链表的插入操作 */
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;

	/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();

	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/* 记录列表项挂接的列表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	( pxList->uxNumberOfItems )++;//列表中记录列表项个数+1
}
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )

这两个用于检查列表和列表项数据是否被覆盖过,同样这两项受宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES开关的影响,只有此宏设置为非0时才有效。由于我们在进行列表和列表项初始化的时候对相应的数据项设置了固定值,因此在这里只需检查数据是否被修改过即可。这个是不一定的,这里所谓的末尾要根据列表的成员变量pxIndex 来确定的!前面说了列表中的pxIndex 成员变量是用来遍历列表的,pxIndex 所指向的列表项就是要遍历的开始列表项,也就是说pxIndex 所指向的列表项就代表列表头!由于是个环形列表,所以新的列表项就应该插入到pxIndex 所指向的列表项的前面。

19、函数vListInsert

对于列表项的插入操作,是要是通过从后向前遍历列表,比较每个列表中每个列表项数值与需要插入列表项数值的大小,找到现有列表中的第一个列表项数值小于被插入列表项数值的地方,然后进行列表项插入操作。始终要保持列表中列表项按xItemValue由小到大顺序排列。
列表中插入一个列表项,值为40,如下图所示:
在这里插入图片描述列表插入一个列表项,值为60,如下图所示:
在这里插入图片描述列表插入一个值为50的列表项,如下图所示:
在这里插入图片描述

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	/*用于检查内存是否被覆盖*/
	listTEST_LIST_INTEGRITY( pxList );
	listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
	if( xValueOfInsertion == portMAX_DELAY )//存放列表项的数值等于最大值portMAX_DELAY(0xffffffff)
	{
		pxIterator = pxList->xListEnd.pxPrevious;
	}
	else
	{

		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /
		{//对上面的for循环进行解释:
			//从列表的倒数第二项列表项进行遍历每个列表项的数值(最后一项是mini列表项,在列表初始化中自带的)
			// pxIterator = ( ListItem_t * ) &( pxList->xListEnd )指向倒数第二个列表项
			// pxIterator->pxNext->xItemValue <= xValueOfInsertion判断pxIterator前一个列表项的数值是否小于插入列表项的数值
			// pxIterator = pxIterator->pxNext;不满足上述条件继续向前遍历
		}
	}
	//进行插入操作,双向链表插入
	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;

	/* 保存列表项挂接的列表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	( pxList->uxNumberOfItems )++;//列表中记录列表项的数值+1
}
20、函数uxListRemove

对于删除列表项的主要标志就是列表项的pvContainer是否指向一个列表。向列表中删除或者添加一个列表项时i,对于列表项的一个重要操作就是对于列表项的pvContainer进行操作。

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* 通过列表项查找挂接列表 */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;

	// 列表删除操作
	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

	/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();

	/* 删除的列表项为pxIndex  需要将pxIndex 指向前一个列表项*/
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	pxItemToRemove->pvContainer = NULL;//pvContainer记录列表项挂接的列表,此数据项主要就是用来表示列表项是否挂接在列表上
	( pxList->uxNumberOfItems )--;//列表中记录列表项数-1

	return pxList->uxNumberOfItems;//返回列表中列表项的个数
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章