FreeRTOS 使用双向链表来描述链表结构,FreeRTOS 中定义了 3 个相关的结构:
ListItem_t:用来表示链表中的一个元素;
MiniListItem_t:用来表示链表中初始的那个元素;
List_t:用来表示一个链表;
1.ListItem_t
struct xLIST_ITEM
{
configLIST_VOLATILE TickType_t xItemValue; //数据
struct xLIST_ITEM * configLIST_VOLATILE pxNext; //next 指针
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //prv指针
void * pvOwner; // 指向拥有这个 Item 成员的结构体,通常是描述进程 TCB 的指针
struct xLIST * configLIST_VOLATILE pxContainer; //记录该链表项属于哪个链表
};
typedef struct xLIST_ITEM ListItem_t;
2.MiniListItem_t:用来表示链表中初始的那个元素;
struct xMINI_LIST_ITEM
{
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
3.表示链表
typedef struct xLIST
{
volatile UBaseType_t uxNumberOfItems; //记录链表里有多少成员
ListItem_t * configLIST_VOLATILE pxIndex; //指向item的结构体,用来遍历链表
MiniListItem_t xListEnd; //链表尾部
} List_t;
MiniListItem_t xListEnd的含义:
由于在链表的尾部(头部),其链表项一般不需要有具体的内容,只需要提供下一个或前一个链表项的地址即可,因此为了节省RAM,定义了一个Mini链表项MiniListItem_t 作为链表的尾。
链表记录了有多少个节点,遍历节点的指针,链表尾部成员可以找到下一个或上一个节点。
链表之间的关系用图示如下:
二,链表的相关操作:list.c
1. 链表项初始化:
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 );
}
//链表的初始化函数
void vListInitialise( List_t * const pxList )
{
//当前指针指向链表尾部
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
//设置排序值最大,确保xListEnd放在尾部
pxList->xListEnd.xItemValue = portMAX_DELAY;
//xListEnd的前一项和下一项指向尾部,表示链表为空
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
//链表数为0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
//写入校验
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
2. 链表的插入
链表的插入操作有两个函数,分别是vListInsertEnd()和vListInsert()。
其中vListInsertEnd()直接在链表pxIndex指向的前一位置插入一个链表项,
而vListInsert()是有序的插入方法,链表项将会按xItemValue的值进行升序插入到链表中去。
2.1
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 );
//新链表插入到pxIndex指向的链表前
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
mtCOVERAGE_TEST_DELAY();
//原来的链表指向新的链表项
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
//记录链表项属于pxList链表
pxNewListItem->pxContainer = pxList;
//链表项数目增加
( pxList->uxNumberOfItems )++;
}
2.2 按值插入
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 )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
//寻找位置
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
/* There is nothing to do here, just iterating to the wanted
* insertion position. */
}
}
//插入操作
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
//记录链表项属于pxList链表
pxNewListItem->pxContainer = pxList;
//链表项数目增加
( pxList->uxNumberOfItems )++;
}
3.删除操作
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = pxItemToRemove->pxContainer;
//调整前后指针
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
mtCOVERAGE_TEST_DELAY();
//当前链表遍历指针等于删除项,则指向前一个,保证遍历指针的有效性
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//移除的链表项不在被链表拥有
pxItemToRemove->pxContainer = NULL;
//链表项数目减1
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
4.遍历
使用宏定义
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{
List_t * const pxConstList = ( pxList );
//遍历链表的指针指向下一个
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
//遍历到表尾
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )
{
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
}
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;
}
这里定义的变量名是pxTCB(Task Control Block 任务控制块),这个遍历操作主要是给任务调度管理用的。
通过多次调用这一宏可以对链表进行遍历操作。