FreeRTOS操作系统(详细速通篇)——— 第八章
本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。适用于快速了解FreeRTOS并进行开发、突击面试、对新手小白非常友好。期待您的后续关注和订阅!
目录
列表与列表项管理
1 列表与列表项简介
1.1 定义
在FreeRTOS中,列表是一个双向链表,包含多个列表项。每个列表具有一个列表根(List Root)和一个指向列表尾的指针。列表通过列表项来管理数据,每个列表项都存储了一个数值(通常是时间戳或优先级)和指向相应数据结构的指针。
简而言之,列表就是数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务;列表项就是放在列表中的项目。
列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加进来的个数决定的,随时可以改变 。列表就中包含着许多的列表项,而列表项就像是在列表内环形排列,如下图所示:
1.2 结构体介绍
(1)列表结构体:
typedef struct xLIST {
listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 用于调试和错误检测的完整性检查值 */
struct xLIST_ITEM *pxIndex; /* 指向列表中的当前节点 */
MiniListItem_t xListEnd; /* 列表的尾部节点 */
UBaseType_t uxNumberOfItems; /* 列表中项目的数量 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /* 用于调试和错误检测的完整性检查值 */
} List_t;
- 1. 在该结构体中,包含了两个宏,这两个宏是确定的已知常量。FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中是否遭到破坏,该功能一般用于调试,默认是不开启的。
- 2. 成员 `uxNumberOfItems 用于记录列表中列表项的个数(不包含 `xListEnd`)。
- 3. 成员 `pxIndex` 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
- 4. 成员变量 `xListEnd` 是一个迷你列表项,排在列表的最末尾。
列表的结构示意图
(2)列表项结构体:
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;
- 1. 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
- 2. 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
- 3. 成员变量 `pvOwner` 指向包含该列表项的对象(通常是任务控制块)。
- 4. 成员变量 `pxContainer` 指向列表项所在的列表。
列表的结构示意图
列表结构体有两种一种是正常的列表项,另一种是正常的列表项,另外一种是迷你列表项。迷你列表项一般放在列表的末尾处如下所示:
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;
- 1. 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
- 2. 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
- 3. 迷你列表项仅用于标记列表的末尾和挂载其他插入的列表项,因此不需要 `pvOwner` 和 `pxContainer` 成员变量,以节省内存开销。
迷你列表项结构示意图
2 相关API函数介绍
在列表的运用中,一般采用的函数如下所示,我们需要掌握其用法,其如何定义的我们仅需要掌握
函数 | 描述 |
vListInitialise() | 初始化列表 |
vListInitialiseItem() | 初始化列表项 |
vListInsertEnd() | 列表末尾插入列表项 |
vListInsert() | 列表插入列表项 |
uxListRemove() | 列表移除列表项 |
2.1 初始化列表
vListInitialise(List_t * const pxList)
其中pxList为等待初始化的列表
void vListInitialise(List_t * const pxList){
// 将 pxIndex 指向 xListEnd
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
// 将 xListEnd 的 xItemValue 初始化为最大值
pxList->xListEnd.xItemValue = portMAX_DELAY;
// 将 xListEnd 的 pxNext 和 pxPrevious 都指向 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.2 初始化列表项
vListInitialiseItem( ListItem_t * const pxItem )
其中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 );
}
2.3 列表末尾插入列表项
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
参数pxList为要插入的列表,pxNewListItem为要插进去的列表项
这里超过并不是指列表中的最末端,将待插入列表的列表项插入到列表 pxIndex 指针指向的列表项前面,是一种无序的插入方法。
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ){
// 获取列表 pxIndex 指向的列表项
ListItem_t * const pxIndex = pxList->pxIndex;
// 更新待插入列表项的指针成员变量
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
// 更新列表中原本列表项的指针成员变量
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
// 更新待插入列表项的所在列表成员变量
pxNewListItem->pxContainer = pxList;
// 更新列表中列表项的数量
( pxList->uxNumberOfItems )++;
}
2.4 列表插入列表项
vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
参数pxList为要插入的列表,pxNewListItem为要插进去的列表项
列表插入列表项依赖每个列表项的值(xItemValue ),在插入的时候会对已经存在列表中的列表项进行比较该值,即:插入的列表要处于比它xItemValue值小的右边,并处于比它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 );
// 如果待插入列表项的值为最大值
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 )++;
}
2.5 列表移除列表项
uxListRemove( ListItem_t * const pxItemToRemove )
pxItemToRemove为等待移除的列表项,并且其具备一个返回值,返回的值为移除掉这个列表项之后,剩余列表项的数量。
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ){
List_t * const pxList = pxItemToRemove->pxContainer;
// 更新指针,移除列表项
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
// 如果 pxIndex 正指向待移除的列表项
if( pxList->pxIndex == pxItemToRemove ) {
// pxIndex 指向上一个列表项
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
// 将待移除的列表项的所在列表指针清空
pxItemToRemove->pxContainer = NULL;
// 更新列表中列表项的数量
( pxList->uxNumberOfItems )--;
// 返回移除后的列表中列表项的数量
return pxList->uxNumberOfItems;
}
3 例子介绍
(1)整体流程
通过以上几个函数,进行简单写一个例子我们来串联一下思路
#include "FreeRTOS.h"
#include "list.h"
#include <stdio.h>
int main() {
// 初始化列表
List_t myList;
vListInitialise(&myList);
// myList 初始化后包含 xListEnd,xListEnd 的 xItemValue 为最大值,pxNext 和 pxPrevious 均指向 xListEnd 自身,uxNumberOfItems 为 0
// 初始化列表项1
ListItem_t listItem1;
vListInitialiseItem(&listItem1);
listItem1.xItemValue = 40; // 设置列表项1的值
// listItem1 初始化后,pxContainer 为 NULL,xItemValue 为 40
// 初始化列表项2
ListItem_t listItem2;
vListInitialiseItem(&listItem2);
listItem2.xItemValue = 60; // 设置列表项2的值
// listItem2 初始化后,pxContainer 为 NULL,xItemValue 为 60
// 初始化列表项3
ListItem_t listItem3;
vListInitialiseItem(&listItem3);
listItem3.xItemValue = 50; // 设置列表项3的值
// listItem3 初始化后,pxContainer 为 NULL,xItemValue 为 50
// 将列表项1插入到列表末尾
vListInsertEnd(&myList, &listItem1);
// myList 现在包含 listItem1 和 xListEnd,listItem1 的 pxNext 指向 xListEnd,pxPrevious 指向 xListEnd,pxContainer 指向 myList,uxNumberOfItems 为 1
// 将列表项2插入到列表末尾
vListInsertEnd(&myList, &listItem2);
// myList 现在包含 listItem1, listItem2 和 xListEnd,listItem2 的 pxNext 指向 xListEnd,pxPrevious 指向 listItem1,pxContainer 指向 myList,uxNumberOfItems 为 2
// 将列表项3按值插入到列表中
vListInsert(&myList, &listItem3);
// myList 现在包含 listItem1, listItem3, listItem2 和 xListEnd,listItem3 的 pxNext 指向 listItem2,pxPrevious 指向 listItem1,pxContainer 指向 myList,uxNumberOfItems 为 3
// 打印列表项值
ListItem_t *currentItem;
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
// 打印列表中所有项的值
// 从列表中移除列表项2
uxListRemove(&listItem2);
// myList 现在包含 listItem1, listItem3 和 xListEnd,listItem2 的 pxContainer 为 NULL,uxNumberOfItems 为 2
// 打印列表项值
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
// 再次打印列表中所有项的值
return 0;
}
(2)分开解析
1.初始化列表:
List_t myList;
vListInitialise(&myList);
初始化一个列表 myList
。该列表包含 xListEnd
,其 xItemValue
为最大值,pxNext
和 pxPrevious
均指向 xListEnd
自身,uxNumberOfItems
为 0。
2.初始化列表项:
ListItem_t listItem1, listItem2, listItem3;
vListInitialiseItem(&listItem1);
listItem1.xItemValue = 40;
vListInitialiseItem(&listItem2);
listItem2.xItemValue = 60;
vListInitialiseItem(&listItem3);
listItem3.xItemValue = 50;
初始化三个列表项,并设置它们的值分别为 40、60 和 50。每个列表项初始化后,pxContainer
为 NULL
。
3.插入列表项到列表末尾:
vListInsertEnd(&myList, &listItem1);
vListInsertEnd(&myList, &listItem2);
将 listItem1
和 listItem2
插入到列表 myList
的末尾。此时列表包含 listItem1
, listItem2
和 xListEnd
,列表项数量为 2。
4.按值插入列表项:
vListInsert(&myList, &listItem3);
将 listItem3
按值插入到列表 myList
中。此时列表包含 listItem1
, listItem3
, listItem2
和 xListEnd
,列表项数量为 3。
5.打印列表项值:
ListItem_t *currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
遍历并打印列表中所有项的值。
6.移除列表项:
uxListRemove(&listItem2);
从列表 myList
中移除 listItem2
。此时列表包含 listItem1
, listItem3
和 xListEnd
,列表项数量为 2。
7.再次打印列表项值:
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
遍历并再次打印列表中所有项的值。
本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。期待诸君的订阅和关注!
原文地址:https://blog.csdn.net/weixin_49007164/article/details/140490732
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!