前言
- 本博文基於FreeRTOSV9.0.0和COTEX-M3內核的文件(我在網上看的不同的版本在功能和效果上有一定差異);
- 本博文針對列表和列表項相關操作的圖文分析,大多是根據自己的理解去分析的;
- 如有錯誤之處還請多多指教;
List 和List Item結構體定義
//節點(列表項)結構體
struct xLIST_ITEM
{
TickType_t xItemValue; //節點編號,用於幫助節點做升序排列;
struct xLIST_ITEM * pxNext; //指向鏈表下一個節點;
struct xLIST_ITEM * pxPrevious; //指向鏈表前一個節點;
void * pvOwner; //指向擁有該節點的內核對象,通常爲TCB 任務控制塊 (task control block);
void * pvContainer; //指向該節點所在的列表;
};
typedef struct xLIST_ITEM ListItem_t; //節點數據結構類型重定義;
/*
mini鏈表節點結構體定義作爲雙鏈表的結尾
因爲雙向鏈表是首尾相連,頭即是尾,尾既是頭;
*/
struct xMINI_LIST_ITEM
{
TickType_t xItemvalue; //節點編號,用於幫助節點做升序排列
struct xLIST_ITEM * pxNext; //指向鏈表下一個節點;
struct xLIST_ITEM * pxPrevious; //指向鏈表前一個節點;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
//鏈表(列表)根節點結構體
struct xLIST
{
UBaseType_t uxNumberOfItems; //鏈表節點數(列表項);
ListItem_t * pxIndex; //鏈表節點索引指針;
MiniListItem_t xListEnd; //包含最大可能項值的列表項,這意味着它總是在列表的末尾,因此用作標記。
};
typedef struct xLIST List_t; //鏈表結構體類型重定義;
我個人對List結構體的理解
struct xLIST
{
UBaseType_t uxNumberOfItems;
ListItem_t * pxIndex;
MiniListItem_t xListEnd;
};
typedef struct xLIST List_t; //鏈表結構體類型重定義;
每個被定義了的列表都有一個List結構體變量,這是整個列表最重要的部分,可以稱爲生產者;具有以下特性:
- 作爲一個不計入列表項總數(uxNumberOfItems)的列表項存在於列表的尾端(首端),承前啓後,前爲輔助值(列表項編號)最大的列表項,後爲列表首項,從而使得列表爲雙向環形 (如圖一);
- 列表結構體變量中的pxIndex成員,始終指向自身結構體的xListEnd地址;所以作爲(ListItem_t *)類型變量,多所指示的前一項和後一項始終保持同步 (如圖二);
結尾插入函數:vListInsert()過程分析
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
pxNewListItem->pxNext = pxIndex; //(1)
pxNewListItem->pxPrevious = pxIndex->pxPrevious; //(2)
pxIndex->pxPrevious->pxNext = pxNewListItem; //(3)
pxIndex->pxPrevious = pxNewListItem; //(4)
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
/*-----------------------------------------------------------*/
其他幾個步驟都很好理解,主要是代碼中標註的(1)~(4),尾端插入過程如下:
中間插入函數:vListInsert()
其實並沒有強調是“中間”,有可能還是結尾;
void vListInsert(List_t * const pxList,ListItem_t * const pxNewListItem)
{
ListItem_t * pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
if(xValueOfInsertion == portMAX_DELAY)
{
pxIterator = pxList->xListEnd.pxPrevious; //(1)
}
else
{
for(pxIterator = (ListItem_t *)&(pxList->xListEnd); //(A)
pxIterator->pxNext->xItemValue <= xValueOfInsertion; //(B)
pxIterator = pxIterator->pxNext) //(C)
{
//這裏並不實現什麼功能,所有的操作在上面括號中就完成了;
}
}
pxNewListItem->pxNext = pxIterator->pxNext; //(2)
pxNewListItem->pxNext->pxPrevious = pxNewListItem;//(3)
pxNewListItem->pxPrevious = pxIterator; //(4)
pxIterator->pxNext = pxNewListItem; //(5)
pxNewListItem->pvContainer = (void *)pxList;
(pxList->uxNumberOfItems)++;
}
分了兩種情況:
①:xValueOfInsertion == portMAX_DELAY 過程從(1)~(5)
②:這個情況相對好理解一些,插入的流程爲:根據要插入的列表項的xItemValue值的大小按照升序的原則,在列表中從第一個(node1)列表項開始,尋找列表項中列表項的xItemValue值剛好大於New列表項的xItemValue值的哪一個列表項,將New列表項插入到它前面;
刪除函數:uxListRemove()
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{
List_t * const pxList = (List_t *)pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; //(1)
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxPrevious; //(2)
//如果要刪除的列表項爲END,那麼pxIndex的指向向前移動一項;
if(pxList->pxIndex == pxItemToRemove)
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
//mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pvContainer = NULL; //(3)
(pxList->uxNumberOfItems)--;
return pxList->uxNumberOfItems;
}
擦除函數相對比較簡單,這裏就不畫圖了;