FreeRTOS中的列表和列表項類似於數據結構中的鏈表和節點;
相關的文件是list.c和list.h兩個文件;
List_t列表結構體
具體定義如下:
/*
* Definition of the type of queue used by the scheduler.
*/
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;
在上述結構體中:
- listFIRST_LIST_INTEGRITY_CHECK_VALUE和listSECOND_LIST_INTEGRITY_CHECK_VALUE兩個成員,是用來檢查列表完整性的,如果使用需要將宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES設置為1,之后會將這兩個成員分別用xListIntegrityValue1和xListIntegrityValue2替換,並且在初始化函數vListInitialise()中給這兩個成員變量設置一個特殊的值,一般默認不使用;
- uxNumberOfItems是一個unsigned long類型的變量,用於記錄列表中列表項的個數;
- pxIndex用來記錄當前列表項索引值,用於遍歷列表;
- xListEnd列表中最后一個列表項,用來表示列表結尾,該變量的類型是一個Mini列表項類型;
xLIST_ITEM列表項結構體
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
};
typedef struct xLIST_ITEM ListItem_t;
在上述結構體中:
- 第一個成員和最后一個成員,用來檢查列表項完整性的,與列表結構體中的作用類似;
- xItemValue為列表項值;
- pxNext指向下一個列表項和pxPrevious配合實現類似雙向鏈表的功能;
- pvOwner記錄該列表項的擁有者,通常是任務塊;
- pxContainer記錄此列表項歸哪個列表;
xMINI_LIST_ITEM迷你列表項結構體
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;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
迷你列表項和列表項結構體區別就是少了幾個成員變量,之所以如此定義是因為有些情況下不需要列表項中的部分變量;
列表的初始化
列表是通過vListInitialise()函數初始化的,源碼定義如下:
void vListInitialise( List_t * const pxList )
{
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
對於listSET_LIST_INTEGRITY_CHECK_1_VALUE和listSET_LIST_INTEGRITY_CHECK_2_VALUE兩項默認不使用,所以整個列表初始化之后的示意圖如下:
-
由於目前還沒有列表項所以uxNumberOfItems變量初始化為0;
-
pxIndex指向列表結構體xLIST中內嵌的xListEnd迷你列表項;
-
列表項xListEnd中成員變量xItemValue初始化為portMAX_DELAY,portMAX_DELAY是一個宏,定義在portmacro.h文件中,根據使用的MCU不同,對應的值也不同,定義如下:
#if( configUSE_16_BIT_TICKS == 1 ) #define portMAX_DELAY ( TickType_t ) 0xffff #else #define portMAX_DELAY ( TickType_t ) 0xffffffffUL #endif
-
pxNext和pxPrevious都指向自身,也就是xListEnd;
列表項的初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pxContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
主要是將成員變量pxContainer指向NULL;
列表插入操作
列表插入函數有兩個,分別是vListInsertEnd()和vListInsert()函數;
vListInsertEnd()函數是往列表的末尾添加列表項的,列表中的xListEnd成員變量表示末尾,那么該函數插入一個列表項是不是就是插入到xListEnd的前面或者后面?這個不一定;
所謂的末尾要根據列表的成員變量pxIndex來確定的;
pxIndex成員變量是用來遍歷列表的,它所指向的列表項就是要遍歷的開始列表項,也就是列表的頭部,由於是個環形列表,所以新的列表項就應該插入到pxIndex所指向的列表項的前面,這才是真正意義上的尾部;
vListInsert()函數是向列表中插入一個列表項,插入的位置由列表項成員變量xItemValue決定
如果xItemValue的值是portMAX_DELAY,那么就直接將列表項插入到xListEnd的前面;
如果xItemValue的值不等於portMAX_DELAY,那么這個要插入的位置根據xItemValue的值按照升序的方式在列表中一個個找,直到最終確定位置插入列表項;
列表項的刪除
列表項的刪除是由函數uxListRemove()實現的,整個過程的操作如下:
- 從傳入的要刪除的列表項中讀取成員變量pxContainer得到該列表項處於哪個列表中;
- 斷開該列表項的前后“連接”,即刪除;
- 如果列表的pxIndex正好指向要刪除的列表項,那么在刪除列表項以后重新給pxIndex找個新的指向,這個新的指向就是被刪除列表項的前一個列表項;
- 將被刪除的列表項的成員變量pxContainer指向NULL;
- 返回新列表當前的列表項數目;
列表的遍歷
FreeRTOS提供了一個函數來完成列表的遍歷,就是listGET_OWNER_OF_NEXT_ENTRY(),這個函數本質上是一個宏,沒調用一次這個函數列表的pxIndex變量就會指向下一個列表項,並且通過pxTCB傳出這個列表項的pvOwner變量值;
此函數用於從多個同優先級的就緒任務中查找下一個要運行的任務;