当前位置:网站首页>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 )
边栏推荐
- centos7.5 设置Mysql开机自启动
- TensorFlow:NameError: name ‘input_data’ is not defined
- kubernetes中不可见的OOM
- sublime记录
- b站up主:空狐公子 --矩阵求导(分母布局)课程笔记
- torch.stack()的官方解释,详解以及例子
- ThreadLocal及其内存泄露分析
- 聚类了解
- Preparation for gold three silver four: how to successfully get an Ali offer (experience + interview questions + how to prepare)
- cnn的输入输出
猜你喜欢

matlab图像分割,从基因芯片荧光图像中提取阴性点(弱)和阳性点(强)

为什么组合优先于继承

研发需求的验收标准应该怎么写? | 敏捷实践

linux mysql操作的相关命令

Tensorflow realize parameter adjustment of linear equations

微信小程序——天气查询

非科班毕业生,五面阿里:四轮技术面+HR一面已拿offer

支付宝小程序的接入

Getting Started with MNIST Machine Learning

Multi-merchant mall system function disassembly 26 lectures - platform-side distribution settings
随机推荐
Cluster understanding
∘(空心的点乘)的数学含义
985毕业,工作3年,分享从阿里辞职到了国企的一路辛酸和经验
verbose np.matmul/np.dot/np.multiply/tf.matmul/tf.multiply/*
MySQL查询性能优化七种武器之索引潜水
多商户商城系统功能拆解26讲-平台端分销设置
为什么组合优先于继承
caffe ---make all editing error
faster-rcnn中的RPN原理
matlab图像分割,从基因芯片荧光图像中提取阴性点(弱)和阳性点(强)
golang 三种指针类型具体类型的指针、unsafe.Pointer、uintptr作用
使用pip成功安装某个库,但pycharm中找不到,此问题的解决方案
电磁场与电磁波-场论基础
性能测试(06)-逻辑控制器
C语言统计不同单词数
threejs+shader 曲线点运动,飞线运动
Unix Environment Programming Chapter 15 15.7 Message Queuing
Julia常见符号意思
二叉树 前序是根在前(根左右)中序(左根右)
从位图到布隆过滤器