当前位置:网站首页>FreeRTOS列表和列表项源码分析
FreeRTOS列表和列表项源码分析
2022-08-09 10:56:00 【zhaodong_jack】
FreeRTOS中一个核心的数据结构就是列表和列表项。剖析FreeRTOS源码的一个必要条件就是掌握列表和列表项。列表和列表项属于数据结构的知识。在数据结构课程中所学的链表和FreeRTOS中的列表在实质上是一个东西。如果学过数据结构中的链表再看FreeRTOS中列表和列表项的知识那就不费吹灰之力了。废话不多说我们来看代码吧。
我这里是在Visual studio开发环境里测试代码。去掉了一些非必要性的代码(比如去掉了列表完整性检查),完全不影响剖析源码。
列表项数据类型
//定义列表项
struct xLIST_ITEM
{
volatile uint32_t xItemValue;//列表项辅助值 列表排序使用
struct xLIST_ITEM * volatile pxNext;//指向下一个列表项的指针
struct xLIST_ITEM * volatile pxPrevious;//指向前一个列表项的指针
void * pvOwner;//指向拥有该内核对象的列表项(通常是TCB)
struct xLIST * volatile pxContainer;//指向该列表项所属的列表
};
typedef struct xLIST_ITEM ListItem_t;
迷你列表项数据类型
//定义迷你列表项
struct xMINI_LIST_ITEM
{
volatile uint32_t xItemValue;//列表项辅助值 列表排序使用
struct xLIST_ITEM * volatile pxNext;//指向下一个列表项的指针
struct xLIST_ITEM * volatile pxPrevious;//指向前一个列表项的指针
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
迷你列表项比列表项少了pvOwner和pxContainer成员变量。迷你列表项就是列表项的阉割版。为什么不直接用列表项而要用迷你列表项呢。因为迷你列表项比列表项省内存。列表的根节点(头节点)就是迷你列表项。这个根节点没有实际意义,只是为了方便列表操作而存在。
列表数据类型
//定义列表
typedef struct xLIST
{
volatile uint32_t uxNumberOfItems;//列表中列表项的个数
ListItem_t * volatile pxIndex;//遍历列表项的指针
MiniListItem_t xListEnd;//列表中的头节点(根节点)只是为了方便列表的操作
}List_t;
列表项初始化函数
//列表项初始化函数
void vListInitialiseItem(ListItem_t * const pxItem)
{
pxItem->pxContainer = NULL;
}
列表项成员pxContainer表示该列表项属于哪条列表,在FreeRTOS内核中就是任务的状态列表项或事件列表项挂载到哪条列表中。
列表初始化函数
//列表初始化函数
void vListInitialise(List_t * const pxList)
{
pxList->pxIndex = (ListItem_t *)&(pxList->xListEnd);
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = (ListItem_t *) &(pxList->xListEnd);
pxList->xListEnd.pxPrevious = (ListItem_t *) &(pxList->xListEnd);
pxList->uxNumberOfItems = 0;
}
列表初始化略显复杂,初始化完成以后如下图
此时就是一条空列表。
列表末尾插入函数
//列表项末尾插入到列表
//FreeRTOS规定列表的pxIndex指针指向的是头,列表是环形的
//所以末尾插入就是插入到pxIndex前边
void vListInsertEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{
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)++;//列表中列表项的个数++
}
注意FreeRTOS约定列表的第一个节点是列表成员pxIndex指向的节点,那么列表末尾插入肯定是插入到pxIndex的前一个节点的位置。
列表插入函数
//列表项插入函数
//根据列表项的值决定要插入的位置
void vListInsert(List_t * const pxList, ListItem_t * const pxNewListItem)
{
ListItem_t * pxIterator;//定义一个指向列表项的指针
const uint32_t xValueOfInsertion = pxNewListItem->xItemValue;//获取待插入列表项的值
if (xValueOfInsertion == portMAX_DELAY)
{
pxIterator = pxList->xListEnd.pxPrevious;//获取根节点前一个节点的地址
}
else
{
/* 看出没?是不是和在一个有序数组里找一个位置很类似呀,我没学数组结构之前,别人我说数组和链表很相似, 那时候的我听到这句话是懵逼的,觉得链表和数组没啥关系。直到学了数据结构才明白了这句话的意义。 for(i = 0;value < buffer[i];i++) 这里体现出了泛型的思想,数据的存储方式不一样,操作方式也一样(操作方式广义上讲是一样的) */
for (pxIterator = (ListItem_t *)&(pxList->xListEnd); pxIterator->pxNext->xItemValue <= xValueOfInsertion;pxIterator = pxIterator->pxNext)
{
}
}
//新的节点插到pxIterator所指节点的后边
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
pxNewListItem->pxContainer = pxList;
(pxList->uxNumberOfItems)++;
}
列表删除函数
//删除一个列表项
uint32_t uxListRemove(ListItem_t * const pxItemToRemove)
{
List_t * const pxList = pxItemToRemove->pxContainer;//获取要删除列表项所在的列表
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
if (pxList->pxIndex == pxItemToRemove)//如果遍历指针恰好指向被删除的列表项
{
pxList->pxIndex = pxItemToRemove->pxPrevious;//那么遍历指针指向被删除节点的前一个节点
}
pxItemToRemove->pxContainer = NULL;//删除的列表项不属于任何列表
(pxList->uxNumberOfItems)--;//列表中的列表项个数减减
return pxList->uxNumberOfItems;//返回列表中列表项的个数
}
注意FreeRTOS链表删除函数在删除时并没有判断列表是否为空列表。所以在调用删除函数时,必须保证该列表项是在列表中的。
遍历列表函数
这个函数是我自己实现的,官方源码中并没有。是在学习列表时用来输出列表,观察列表。这个函数相对来说就很简单了。
//遍历列表
void traverse_list(List_t * const pxList)
{
const ListItem_t * ptemp = pxList->xListEnd.pxNext;//获取根节点的下一个节点的地址,
while (ptemp != (ListItem_t *)&(pxList->xListEnd))//如果遍历到的节点不是根节点
{
printf("%d\n",ptemp->xItemValue);//输出该节点的值(此处是输出,也可以做一些其它操作,如将其值赋值为0)
ptemp = ptemp->pxNext;//指向节点的指针向后移动
}
}
在列表插入函数中提到了泛型思想,那这个遍历我们也用同样的思维去实现它。感受泛型的威力。
//遍历列表
void traverse_list(List_t * const pxList)
{
for (const ListItem_t * ptemp = pxList->xListEnd.pxNext; ptemp != (ListItem_t *)&(pxList->xListEnd); ptemp = ptemp->pxNext)
{
printf("%d\n", ptemp->xItemValue);
}
}
数组输出的方式
for (i = 0; i < N; i++)
{
printf("%d ", buffer[i]);
}
是不是和输出一个数组用的是同样的套路。
下边是一些关于列表和列表项的宏
//设置列表项的拥有者
#define listSET_LIST_ITEM_OWNER(pxListItem,pxOwner) (((pxListItem)->pvOwner) = (void *)pxOwner)
//获取列表项的拥有者
#define listGET_LIST_ITEM_OWNER(pxListItem) ((pxListItem)->pvOwner)
//设置列表项的值
#define listSET_LIST_ITEM_VALUE(pxListItem,xValue) (((pxListItem)->xItemValue) = xValue)
//获取列表项的值
#define listGET_LIST_ITEM_VALUE(pxListItem) ((pxListItem)->xItemValue)
//获取列表中第一个列表项的值
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
//获取下一个列表项的地址(末尾列表项后边的列表项的值)
#define listGET_NEXT(pxListItem) ( ( pxListItem )->pxNext )
//获取列表中末尾列表项的地址
#define listGET_END_MARKER(pxList) ((ListItem_t const *)(&((pxList)->xListEnd)))
//判断列表是否为空
#define listLIST_IS_EMPTY( pxList ) (((pxList)->uxNumberOfItems == 0)? ( pdTRUE ) : ( pdFALSE ))
//获取列表的长度
#define listCURRENT_LIST_LENGTH( pxList ) ((pxList)->uxNumberOfItems)
//获取列表pxIndex所指向的列表项的pvOwner值(也就是TCB)
#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; \ }
//获取列表中第一个列表项的pvOwner值(也就是TCB)
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
//判断列表项是否在列表中
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
//获取列表项属于哪条列表
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
//判断列表是否被初始化
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
边栏推荐
- Getting Started with MNIST Machine Learning
- 1006 Sign In and Sign Out (25分)
- 详细的np.matmul / np.dot / np.multiply / tf.matmul / tf.multiply / *
- Unix Environment Programming Chapter 15 15.7 Message Queuing
- C语言统计不同单词数
- 为什么组合优先于继承
- 华为VRRP+MSTP联动接口检测实验案例
- Solve 1. tensorflow runs using CPU but not GPU 2. GPU version number in tensorflow environment 3. Correspondence between tensorflow and cuda and cudnn versions 4. Check cuda and cudnn versions
- CSDN的markdown编辑器语法完整大全
- 实测办公场景下,国产远程控制软件的表现力如何?(技术解析)
猜你喜欢
随机推荐
数据存储:对dataframe类,使用to_csv()将中文数据写入csv文件
多商户商城系统功能拆解26讲-平台端分销设置
faster-rcnn中的RPN原理
真香!肝完Alibaba这份面试通关宝典,我成功拿下今年第15个Offer
Quartz的理解
Invisible OOM in kubernetes
1009 Product of Polynomials C语言多项式乘积(25分)
bit、byte、KB、M、G、T相互关系
b站up主:空狐公子 --矩阵求导(分母布局)课程笔记
无重复字符的最长子串
1003 Emergency (25分)
pip常见命令和更改源文件
AQS同步组件-FutureTask解析和用例
【 original 】 VMware Workstation implementation Openwrt soft routing, the ESXI, content is very detailed!
Missing URI template variable ‘employeeNumber‘ for method parameter of type String
支付宝小程序的接入
centos7.5 设置Mysql开机自启动
linux mysql操作的相关命令
uni-app 自带的picker封装一个日期-时间选择器
最长回文子串