前言
- 本博文基于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->pxNext; //(2)
//如果要删除的列表项为END,那么pxIndex的指向向前移动一项;
if(pxList->pxIndex == pxItemToRemove)
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
//mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pvContainer = NULL; //(3)
(pxList->uxNumberOfItems)--;
return pxList->uxNumberOfItems;
}
擦除函数相对比较简单,这里就不画图了;