列表是FreeRTOS中的一個數據結構,概念上和鏈表有點相似,列表被用來跟蹤FreeRTOS中的任務。shell
列表數據結構定義以下:數據結構
typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE //檢查列表完整性 volatile UBaseType_t uxNumberOfItems; //就列表中列表項的數量 ListItem_t * configLIST_VOLATILE pxIndex; //記錄當前列表項索引號,用於遍歷列表 MiniListItem_t xListEnd; //表示列表結束 listSECOND_LIST_INTEGRITY_CHECK_VALUE //檢查列表完整性 } List_t;
列表項ListItem_t的定義以下:函數
typedef struct xLIST_ITEM ListItem_t; struct xLIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE //檢查列表完整性 configLIST_VOLATILE TickType_t xItemValue; //列表項值 struct xLIST_ITEM * configLIST_VOLATILE pxNext; //指向下一個列表項 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;//指向前一個列表項 void * pvOwner; //記錄此鏈表項歸誰有 struct xLIST * configLIST_VOLATILE pxContainer; //記錄此列表項歸哪一個列表 listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE //檢查列表完整性 };
Mini列表項MiniListItem_t的定義以下:ui
typedef struct xMINI_LIST_ITEM MiniListItem_t; struct xMINI_LIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE //檢查迷你列表項的完整性 configLIST_VOLATILE TickType_t xItemValue;//記錄列表項值 struct xLIST_ITEM * configLIST_VOLATILE pxNext; //指向下一個列表項 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //指向前一個列表項 };
列表初始化:列表初始化是對列表結構體List_t中的各個成員變量進行初始化,列表的初始化經過vListInitialise()來完成,函數實現以下:code
void vListInitialise( List_t * const pxList ) { pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); //(1) pxList->xListEnd.xItemValue = portMAX_DELAY; //(2) pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); //(3) pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );//(4) pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//(5) listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );//(6) listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );//(7) }
(1)xListEnd用來表示列表的末尾,pxIndex表示列表項的索引號,此時只有一個列表項xListEnd。blog
(2)xListEnd的列表項值初始化爲portMAX_DELAY,這是個宏,不一樣MCU值不一樣,可能爲0xfff或0xffffffffULL。索引
(3)xListEnd的pxNext變量指向自身。內存
(4)xListEnd的pxPrevious變量指向自身。原型
(5)此時沒有其餘列表項,uxNumberOfItems爲0,這裏沒有算xListEnd。it
(6)(7)初始化列表項中用於完整性檢查字段。
初始化以後的結果以下:
列表項初始化:列表項在使用的時候也須要初始化。列表項初始化函數爲vListInitialiseItem()。
void vListInitialiseItem( ListItem_t * const pxItem ) { pxItem->pxContainer = NULL; listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); }
列表項初始化僅將列表項成員pxContainer初始化爲NULL。
函數原型:
#include "FreeRTOS.h" #include "list.h" void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem );
函數說明:列表項插入函數。列表項的插入位置由列表項中成員變量xItemValue決定,根據xItemValue的值反照升序的方式排列。
參數說明:pxList:列表項要插入的列表;pxNewListItem:要插入的列表項。
函數實現:
#include "FreeRTOS.h" #include "list.h" void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) { ListItem_t * pxIterator; const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; //(1) listTEST_LIST_INTEGRITY( pxList ); //(2) listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); if( xValueOfInsertion == portMAX_DELAY ) //(3) { pxIterator = pxList->xListEnd.pxPrevious; //(4) } else { for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) //(5) { /* There is nothing to do here, * just iterating to the wanted insertion position. */ } } pxNewListItem->pxNext = pxIterator->pxNext; //(6) pxNewListItem->pxNext->pxPrevious = pxNewListItem; pxNewListItem->pxPrevious = pxIterator; pxIterator->pxNext = pxNewListItem; pxNewListItem->pxContainer = pxList; //(7) ( pxList->uxNumberOfItems )++; //(8) }
(1)獲取要插入的列表項值,根據這個值確認列表項的插入位置。
(2)檢查列表和列表項 的完整性。
(3)插入的列表項值爲portMAX_DELAY,插入的位置就在列表最末尾。
(4)插入的列表項值爲portMAX_DELAY,列表項插入在xListEnd前面。
(5)插入的列表項值不等於portMAX_DELAY,在列表中一個一個找插入的位置,找到合適的位置就退出,當前for循環體沒有代碼,這個查找過程就按照升序的方式查找列表項插入點。
(6)從本行開始的四行代碼就是將列表項插入到列表中,插入過程和雙向鏈表相似。
(7)列表項的成員變量pxContainer記錄此列表項屬於哪一個列表。
(8)列表的成員變量uxNumberOfItems加一,表示又添加了一個列表項。
列表項插入過程演示:
在一個空的列表中插入三個列表項,這三個列表項的值分別爲40、60、50。
一、插入值爲40的列表項
在一個空的列表插入一個列表值爲40的列表項ListIterm1,插入完成後以下圖所示,可看出列表是一個環形列表:
二、插入值爲60的列表項
在上面列表中插入值爲60的列表項,插入完成後以下圖所示:
三、插入值爲50的列表項
在上面列表中插入值爲50的列表項,插入完成後以下圖所示:
函數原型:
#include "FreeRTOS.h" #include "list.h" void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem );
函數說明:列表末尾插入列表項。
參數說明:pxList:列表項要插入的列表;pxNewListItem:要插入的列表項。
函數實現:
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) { ListItem_t * const pxIndex = pxList->pxIndex; listTEST_LIST_INTEGRITY( pxList ); //(1) listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); pxNewListItem->pxNext = pxIndex; //(2) pxNewListItem->pxPrevious = pxIndex->pxPrevious; mtCOVERAGE_TEST_DELAY(); pxIndex->pxPrevious->pxNext = pxNewListItem; pxIndex->pxPrevious = pxNewListItem; pxNewListItem->pxContainer = pxList; //(3) ( pxList->uxNumberOfItems )++; //(4) }
(1)這行和下一行代碼完成對列表和列表項的完整性檢查。
(2)從本行開始的代碼就是將要插入的列表項插入到列表末尾。這裏所謂的末尾是根據列表的成員變量pxIndex來肯定的。pxIndex成員變量用於遍歷列表,pxIndex所指向的列表項就是要遍歷的開始列表項,也就是pxIndex所指向的列表項就表明列表頭。新的列表項就插入到pxIndex所指向的列表項的前面。
(3)標記新的列表項pxNewListItem屬於列表pxList。
(4)記錄列表中的列表項數目的變量加一,更新列表項數目。
列表項插入過程演示:
一、默認列表
插入列表項前先準備一個默認列表,以下圖所示:
二、插入值爲50的列表項
在上面的列表中插入一個值爲50的列表項,插入完成以後以下圖所示:
函數原型:
#include "FreeRTOS.h" #include "list.h" UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );
函數說明:列表項刪除函數。
參數說明:pxItemToRemove:要刪除的列表項。注意:列表項的刪除只是將指定的列表項從列表中刪除掉,並不會將這個列表項的內存給釋放掉,若是這個列表項是動態內存分配的話。
返回值:返回刪除列表項之後的列表剩餘列表項數目。
函數實現:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) { List_t * const pxList = pxItemToRemove->pxContainer; //(1) pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; //(2) pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; mtCOVERAGE_TEST_DELAY(); if( pxList->pxIndex == pxItemToRemove ) { pxList->pxIndex = pxItemToRemove->pxPrevious; //(3) } else { mtCOVERAGE_TEST_MARKER(); } pxItemToRemove->pxContainer = NULL; //(4) ( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems; //(5) }
(1)讀取列表項中的成員pxContainer,獲得列表項處於哪一個列表中。
(2)與下一行完成列表項的刪除。將要刪除的列表項的先後兩個列表項鍊接在一塊兒。
(3)若是列表的pxIndex正好指向要刪除的列表項,則pxIndex從新指向被刪除的列表項的前一個列表項。
(4)被刪除列表項的成員變量pxContainer清零。
(5)返回新列表的當前列表項數目。
列表的遍歷是經過listGET_OWNER_OF_NEXT_ENTRY宏實現的。每調用一次這個函數列表的pxIndex變量就會指向下一個列表項,而且返回這個列表項的pxOwner變量值。
宏的實現:
#include "FreeRTOS.h" #include "list.h" #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \//(1) { \ List_t * const pxConstList = ( pxList ); \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \//(2) if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd))\//(3) { \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \//(4) } \ ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \//(5) }
(1)pxTCB用來保存pxIndex所指向的列表項的pvOwner變量值,也就是這個列表項的全部者,一般是一個任務的任務控制塊。pxList表示要遍歷的列表。
(2)列表的pxIndex變量指向下一個列表項。
(3)若是pxIndex指向了列表的xListEnd成員變量,表示到了列表末尾。
(4)若是到了列表末尾的話就跳過xListEnd,pxIndex再一次從新指向處於列表頭的列表項,這樣就完成了一次對列表的遍歷。
(5)將pxIndex所指向的新列表項的pvOwner賦值給pxTCB。
建立一個任務,用於操做列表和列表項。調用列表和列表項相關的API函數。
configSTACK_DEPTH_TYPE Task06_STACK_SIZE = 5; UBaseType_t Task06_Priority = 1; TaskHandle_t Task06_xHandle; List_t TestList; ListItem_t ListItem1; ListItem_t ListItem2; ListItem_t ListItem3; void vTask06_Code(void *para) { //初始化列表和列表項 vListInitialise(&TestList); vListInitialiseItem(&ListItem1); vListInitialiseItem(&ListItem2); vListInitialiseItem(&ListItem3); ListItem1.xItemValue = 40; ListItem2.xItemValue = 60; ListItem3.xItemValue = 50; //打印列表和其餘列表項地址 PRINT(" ==== list and listItem address ===="); PRINT(" type address"); PRINT(" ------------------------------"); PRINT(" TestList %#x", &TestList); PRINT(" TestList->pxIndex %#x", TestList.pxIndex); PRINT(" TestList->xListEnd %#x", &TestList.xListEnd); PRINT(" ListItem1 %#x", &ListItem1); PRINT(" ListItem2 %#x", &ListItem2); PRINT(" ListItem3 %#x", &ListItem3); PRINT(" =============== end ===============\n"); //向列表中添加列表項ListItem1,觀察列表項中成員變量的值。 vListInsert(&TestList, &ListItem1); PRINT(" ====== add listItem1 in TestList ======="); PRINT(" type address"); PRINT(" ----------------------------------------"); PRINT(" TestList->xListEnd->pxNext %#x", TestList.xListEnd.pxNext); PRINT(" ListItem1->pxNext %#x", ListItem1.pxNext); PRINT(" -------- 先後向鏈接切割線 ---------"); PRINT(" TestList->xListEnd->pxPrevious %#x", TestList.xListEnd.pxPrevious); PRINT(" ListItem1->pxPrevious %#x", ListItem1.pxPrevious); PRINT(" ================= end ==================\n"); //向列表中插入列表項ListItem2,觀察列表項中成員變量的變化 vListInsert(&TestList, &ListItem2); PRINT(" ====== add listItem2 in TestList ======="); PRINT(" type address"); PRINT(" ----------------------------------------"); PRINT(" TestList->xListEnd->pxNext %#x", TestList.xListEnd.pxNext); PRINT(" ListItem1->pxNext %#x", ListItem1.pxNext); PRINT(" ListItem2->pxNext %#x", ListItem2.pxNext); PRINT(" -------- 先後向鏈接切割線 ---------"); PRINT(" TestList->xListEnd->pxPrevious %#x", TestList.xListEnd.pxPrevious); PRINT(" ListItem1->pxPrevious %#x", ListItem1.pxPrevious); PRINT(" ListItem2->pxPrevious %#x", ListItem2.pxPrevious); PRINT(" ================= end ==================\n"); //向列表中插入列表項ListItem3,觀察列表項中成員變量的變化 vListInsert(&TestList, &ListItem3); PRINT(" ====== add listItem3 in TestList ======="); PRINT(" type address"); PRINT(" ----------------------------------------"); PRINT(" TestList->xListEnd->pxNext %#x", TestList.xListEnd.pxNext); PRINT(" ListItem1->pxNext %#x", ListItem1.pxNext); PRINT(" ListItem3->pxNext %#x", ListItem3.pxNext); PRINT(" ListItem2->pxNext %#x", ListItem2.pxNext); PRINT(" -------- 先後向鏈接切割線 ---------"); PRINT(" TestList->xListEnd->pxPrevious %#x", TestList.xListEnd.pxPrevious); PRINT(" ListItem1->pxPrevious %#x", ListItem1.pxPrevious); PRINT(" ListItem3->pxPrevious %#x", ListItem3.pxPrevious); PRINT(" ListItem2->pxPrevious %#x", ListItem2.pxPrevious); PRINT(" ================= end ==================\n"); //刪除ListItem2,觀察列表項中成員變量的變化 uxListRemove(&ListItem2); PRINT(" ========== delete listItem2 ============"); PRINT(" type address"); PRINT(" ----------------------------------------"); PRINT(" TestList->xListEnd->pxNext %#x", TestList.xListEnd.pxNext); PRINT(" ListItem1->pxNext %#x", ListItem1.pxNext); PRINT(" ListItem3->pxNext %#x", ListItem3.pxNext); PRINT(" -------- 先後向鏈接切割線 ---------"); PRINT(" TestList->xListEnd->pxPrevious %#x", TestList.xListEnd.pxPrevious); PRINT(" ListItem1->pxPrevious %#x", ListItem1.pxPrevious); PRINT(" ListItem3->pxPrevious %#x", ListItem3.pxPrevious); PRINT(" ================= end ==================\n"); //pxIndex向後移一項 TestList.pxIndex = TestList.pxIndex->pxNext; //在列表末尾插入列表項ListItem2 vListInsertEnd(&TestList, &ListItem2); PRINT(" ===== add listItem2 in TestList end ====="); PRINT(" type address"); PRINT(" ----------------------------------------"); PRINT(" TestList->pxIndex %#x", TestList.pxIndex); PRINT(" TestList->xListEnd->pxNext %#x", TestList.xListEnd.pxNext); PRINT(" ListItem2->pxNext %#x", ListItem2.pxNext); PRINT(" ListItem1->pxNext %#x", ListItem1.pxNext); PRINT(" ListItem3->pxNext %#x", ListItem3.pxNext); PRINT(" -------- 先後向鏈接切割線 ---------"); PRINT(" TestList->xListEnd->pxPrevious %#x", TestList.xListEnd.pxPrevious); PRINT(" ListItem2->pxPrevious %#x", ListItem2.pxPrevious); PRINT(" ListItem1->pxPrevious %#x", ListItem1.pxPrevious); PRINT(" ListItem3->pxPrevious %#x", ListItem3.pxPrevious); PRINT(" ================= end ==================\n"); for (;;) { static unsigned int cnt = 0; PRINT(" task06 cnt %u...", cnt); cnt++; vTaskDelay(1000); } } void creat_task_test_list(void) { if (xTaskCreate(vTask06_Code, "list test task06", Task06_STACK_SIZE, NULL, Task06_Priority, &Task06_xHandle) != pdPASS) PRINT("creat task failed!\n"); }
編譯,運行,打印結果以下:
$ ./build/freertos-simulator ==== list and listItem address ==== type address ------------------------------ TestList 0x60f000 TestList->pxIndex 0x60f010 TestList->xListEnd 0x60f010 ListItem1 0x60f0c0 ListItem2 0x60f120 ListItem3 0x60f080 =============== end =============== ====== add listItem1 in TestList ======= type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 ListItem1->pxNext 0x60f010 -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f0c0 ListItem1->pxPrevious 0x60f010 ================= end ================== ====== add listItem2 in TestList ======= type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 ListItem1->pxNext 0x60f120 ListItem2->pxNext 0x60f010 -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f120 ListItem1->pxPrevious 0x60f010 ListItem2->pxPrevious 0x60f0c0 ================= end ================== ====== add listItem3 in TestList ======= type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 ListItem1->pxNext 0x60f080 ListItem3->pxNext 0x60f120 ListItem2->pxNext 0x60f010 -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f120 ListItem1->pxPrevious 0x60f010 ListItem3->pxPrevious 0x60f0c0 ListItem2->pxPrevious 0x60f080 ================= end ================== ========== delete listItem2 ============ type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 ListItem1->pxNext 0x60f080 ListItem3->pxNext 0x60f010 -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f080 ListItem1->pxPrevious 0x60f010 ListItem3->pxPrevious 0x60f0c0 ================= end ================== ===== add listItem2 in TestList end ===== type address ---------------------------------------- TestList->pxIndex 0x60f0c0 TestList->xListEnd->pxNext 0x60f120 ListItem2->pxNext 0x60f0c0 ListItem1->pxNext 0x60f080 ListItem3->pxNext 0x60f010 -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f080 ListItem2->pxPrevious 0x60f010 ListItem1->pxPrevious 0x60f120 ListItem3->pxPrevious 0x60f0c0 ================= end ================== task06 cnt 0... task06 cnt 1...
過程分析:
(1)列表和列表項的地址以下:
TestList 0x60f000 ListItem1 0x60f0c0 TestList->pxIndex 0x60f010 TestList->xListEnd 0x60f010 ListItem2 0x60f120 ListItem3 0x60f080
(2)列表TestList添加列表項ListItem1後的地址以下:
type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 //ListItem1 ListItem1->pxNext 0x60f010 //TestList->xListEnd -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f0c0 //ListItem1 ListItem1->pxPrevious 0x60f010 //TestList->xListEnd
用示意圖表示以下圖所示:
(2)列表TestList添加列表項ListItem2後的地址以下:
type address ---------------------------------------- TestList->xListEnd->pxNext 0x60f0c0 //ListItem1 ListItem1->pxNext 0x60f120 //ListItem2 ListItem2->pxNext 0x60f010 //TestList->xListEnd -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f120 //ListItem2 ListItem1->pxPrevious 0x60f010 //TestList->xListEnd ListItem2->pxPrevious 0x60f0c0 //ListItem1
用示意圖表示以下圖所示:
(3)列表TestList插入列表項ListItem3後的地址以下:
TestList->xListEnd->pxNext 0x60f0c0 //ListItem1 ListItem1->pxNext 0x60f080 //ListItem3 ListItem3->pxNext 0x60f120 //ListItem2 ListItem2->pxNext 0x60f010 //TestList->xListEnd -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f120 //ListItem2 ListItem1->pxPrevious 0x60f010 //TestList->xListEnd ListItem3->pxPrevious 0x60f0c0 //ListItem1 ListItem2->pxPrevious 0x60f080 //ListItem3
用示意圖表示以下圖所示:
(4)列表TestList刪除列表項ListItem2以後的地址以下:
TestList->xListEnd->pxNext 0x60f0c0 //ListItem1 ListItem1->pxNext 0x60f080 //ListItem3 ListItem3->pxNext 0x60f010 //TestList->xListEnd -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f080 //ListItem3 ListItem1->pxPrevious 0x60f010 //TestList->xListEnd ListItem3->pxPrevious 0x60f0c0 //ListItem1
用示意圖表示以下圖所示:
(4)列表TestList在末端插入列表項ListItem2以後的地址以下:
這裏所說的末端就是TestList->pxIndex指向的列表項(表明列表頭),新的列表項就插入到TestList->pxIndex所指向的列表項的前面。
TestList->pxIndex 0x60f0c0 //ListItem1 TestList->xListEnd->pxNext 0x60f120 //ListItem2 ListItem2->pxNext 0x60f0c0 //ListItem1 ListItem1->pxNext 0x60f080 //ListItem3 ListItem3->pxNext 0x60f010 //TestList->xListEnd -------- 先後向鏈接切割線 --------- TestList->xListEnd->pxPrevious 0x60f080 //ListItem3 ListItem2->pxPrevious 0x60f010 //TestList->xListEnd ListItem1->pxPrevious 0x60f120 //ListItem2 ListItem3->pxPrevious 0x60f0c0 //ListItem1
用示意圖表示以下圖所示: