【FreeRTOS】 列表和列表项
相关实验资料已上传,免费提供
7.1 FreeRTOS 列表和列表项简介
列表(List):FreeRTOS 中最基本的一种数据结构,物理存储单元上是非连续、非顺序的;FreeRTOS 中的列表是一个双向链表。
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
- 宏 用于存放确定已知常量;
- 通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏。
uxNumberOfItems
- 用于记录列表中列表项的个数(不包含 xListEnd)中插入列表项时,该值加 1;
- 当从列表中移除列表项时,该值减 1
pxIndex
- 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项
xListEnd
一个迷你列表项
- 列表中迷你列表项的值一般被设置为最大值
- 用于将列表中的所有列表项按升序排序时,排在最末尾;
- 同时 xListEnd 也用于挂载其他插入到列表中的列表项
列表项(List 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; /* 重定义成 ListItem_t */
listFIRST_LIST_INTEGRITY_CHECK_VALUE / listSECOND_LIST_INTEGRITY_CHECK_VALUE
- 宏 用于存放确定已知常量;
- 通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏。
xItemValue
- 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
pxNext/pxPrevious
-
分别用于指向列表中列表项的下一个列表项和上一个列表项。
-
pxOwner 用于指向包含列表项的对象(通常是任务控制块),因此,列表项和包含列表项的对象之间存在双向链接。
pxContainer
- 用于指向列表项所在列表。
迷你列表项(Mini 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;/* 重定义成 MiniListItem_t */
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
- 用于检测列表项的数据完整性
xItemValue
- 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
pxNext / pxPrevious
- 分别用于指向列表中列表项的下一个列表项和上一个列表项。
迷你列表项相比于列表项,只用于标记列表的末尾和挂载其他插入列表中的列表项,
因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。
7.2 FreeRTOS 列表和列表项相关 API 函数
vListInitialise(pxList ):初始化列表
- 此函数用于初始化列表,在定义列表之后,需要先对其进行初始化;
- 只有初始化后的列表,才能够正常地被使用。
- 列表初始化的过程,其实就是初始化列表中的成员变量。
void vListInitialise(List_t * const pxList) //pxList 待初始化列表
{
/* 初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
/* xListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后 */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* 初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身 */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
/*初始化时,列表中的列表项数量为 0(不包含 xListEnd) */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* 初始化用于检测列表数据完整性的校验值 */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
vListInitialiseItem( pxItem):初始化列表项
-
此函数用于初始化列表项,如同列表一样,在定义列表项之后,也需要先对其进行初始化,
-
只有初始化有的列表项,才能够被正常地使用
-
将列表项所在列表设置为空,以保证列表项不再任何一个列表项
中即可
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 );
}
vListInsertEnd(List_t * const pxList,ListItem_t * const pxNewListItem):尾端插入
-
此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向列表项的前面,是一种无序的插入方法;
-
此函数就是将待插入的列表项插入到列表 pxIndex 指向列表项的前面;
-
要注意的是,pxIndex 不一定指向 xListEnd,而是有可能指向列表中任意一个列表项
作者在此处存疑,期待有大佬解惑:我认为这个环形列表只要大于等于三项,那就不能说每一个都是pxIndex指向列表中任意一个列表项。
void vListInsertEnd(List_t * const pxList,ListItem_t * const pxNewListItem)
{
/* 获取列表 pxIndex 指向的列表项 */
ListItem_t * const pxIndex = pxList->pxIndex;
/* 检查参数是否正确 */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 更新待插入列表项的指针成员变量 */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
}
vListInsert( List_t * const pxList,ListItem_t * const pxNewListItem);
此函数用于将待插入列表的列表项按照列表项值升序排序的顺序,有序地插入到列表中。
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 )
{
/* 插入的位置为列表 xListEnd 前面 */
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/* 遍历列表中的列表项,找到插入的位置 */
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext )
{
}
}
/* 将待插入的列表项插入指定位置 */
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* 更新待插入列表项所在列表 */
pxNewListItem->pxContainer = pxList;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )++;
}
- 将待插入列表项插入列表之前,会前遍历列表,找到待插入列表项需要插入的位置;
- 待插入列表项需要插入的位置是依照列表中列表项的值按照升序排序确定的
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove);
- pxItemToRemove:待移除的列表项;
- 返回值(UBaseType_t为整数):待移除列表项移除后,所在列表剩余列表项的数量;
- 于将列表项从列表项所在列表中移除
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();
/* 如果 pxIndex 正指向待移除的列表项 */
if( pxList->pxIndex == pxItemToRemove )
{
/* pxIndex 指向上一个列表项 */
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 将待移除列表项的所在列表指针清空 */
pxItemToRemove->pxContainer = NULL;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )--;
/* 返回列表项移除后列表中列表项的数量 */
return pxList->uxNumberOfItems;
}
- 函数 uxListRemove()移除后的列表项,依然于列表有着单向联系;
- 即移除后列表项中用于指向上一个和下一个列表项的指针,依然指向列表中的列表项。
7.3 FreeRTOS 操作列表和列表项的宏定义
这些宏操作列表及列表项的实现都比较简单
读者可自行查阅 list.h 文件
7.4 FreeRTOS 列表项的插入与删除实验
学习笔记md文件和实验工程均已上传,免费提供