列表和列表项是FreeRTOS的一个数据结构, FreeRTOS内核调度大量使用了列表(list)和列表项(list item)数据结构。
FreeRTOS列表使用指针指向列表项。一个列表(list)下面可能有很多个列表项(list item),每个列表项都有一个指针指向列表。

一、什么是列表和列表项
列表概念上和链表有点相似,用来追踪FreeRTOS中的任务。与列表相关的东西都在list.c和list.h中。
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE UBaseType_t uxNumberOfItems;//用来记录列表中列表项的数量
ListItem_t * configLIST_VOLATILE pxIndex; //pxIndex,用来记录当前列表项索引号,用于遍历列表
MiniListItem_t xListEnd; //列表中最后一个列表项,用来表示列表结束
listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
- uxNumberOfItems表示该列表中挂接的列表项数目,0表示列表为空。
- 列表项类型指针用于遍历列表,列表初始化后,这个指针指向&xListEnd。通过宏listGET_OWNER_OF_NEXT_ENTRY()来获取列表中的下一个列表项。
- 列表项xListEnd用于标记列表结束。xListEnd.xItemValue被初始化为一个常数,其值与硬件架构相关,为0xFFFF(16位架构)或者0xFFFFFFFF(32位架构)。
列表项
列表项就是存放在列表中的项目。
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; //上一个列表项,同pxNext配合可实现双向链表功能
void * pvOwner; //记录此链表项归谁所有,指向一个任务TCB
void * configLIST_VOLATILE pvContainer; //记录此列表项归哪个列表,指向包含该列表项的列表
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
};
typedef struct xLIST_ITEM ListItem_t;
-
xItemValue是列表项值,通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。
如果任务因为等待从队列取数据而进入阻塞状态,则任务的事件列表项的列表项值保存任务优先级有关信息,状态列表项的列表项值保存阻塞时间有关的信息。
迷你列表项
既然有了全功能版的列表项,为什么还要声明迷你版的列表项呢?
这是因为列表结构体需要一个列表项成员,但又不需要列表项中的所有字段,所以才有了迷你版列表项。
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; //上一个列表项,同pxNext配合可实现双向链表功能
};
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;
/* 列表项xListEnd的pxNext和pxPrevious指针指向了它自己 */
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 );
}
列表项的初始比较简单,只要确保列表项不在任何列表中即可。
void vListInitialiseItem( ListItem_t * const pxItem )
{
pxItem->pvContainer = NULL;/不属于任何list
//初始化用于完成性检查的变量
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
其他内容,在实际使用时进行赋值。
三、列表项插入和删除
列表项所在的位置取决于列表项的列表项值(xItemValue)
3.1 列表项插入
vListInsert(List_t *const pxList, ListItem_t *const pxNewListItem)
参数:列表项要插入的列表和要插入的列表项
要插入的位置由列表项中成员变量xItemValue来决定。
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 );
/*将新的列表项插入到列表,根据xItemValue的值降序插入列表。*/
if( xValueOfInsertion == portMAX_DELAY ) // 插入末尾
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );\
pxIterator->pxNext->xItemValue <= xValueOfInsertion;\
pxIterator = pxIterator->pxNext )
{ }// empty
}
//插入中间位置时,插在Interator后面
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
//列表项在列表中了,列表项成员变量pvContainer记录此列表项属于哪个列表
pxNewListItem->pvContainer = ( void * ) pxList;
//表示又加了一个列表项
( pxList->uxNumberOfItems )++;
}
portMAX_DELAY:如果列表中存在与新列表项xItemValue值相同的列表项,则新插入的列表项位于它之后。如果列表项的xItemValue值等于portMAX_DELAY(列表结束标记,我们在讲列表数据结构时,说到每个列表数据结构体中都有一个列表项成员xListEnd,用于标记列表结束。xListEnd.xItemValue被初始化为一个常数,其值与硬件架构相关,为0xFFFF或者0xFFFFFFFF。这个常数在移植层定义,即宏portMAX_DELAY),则表示到达了列表结束位置。
3.2 将列表项插入到列表末端
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t* const pxIndex = pxList->pxIndex;
/*检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );
/*向列表中插入新的列表项*/
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious =pxIndex->pxPrevious;
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext =pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
pxNewListItem->pvContainer = ( void* ) pxList;
( pxList->uxNumberOfItems )++;
}