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;//返回列表中列表項的個數
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章