- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
FreeRTOS Kernel V10.3.1 。
FreeRTOS 的 list.c / list.h 文件中有 3 个数据结构、2 个初始化函数、2 个插入函数、1 个移除函数和一些宏函数,链表是 FreeRTOS 中的重要数据结构,下述 “1、数据结构” 和 “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;
// 记录此 链表项 归谁拥有,通常是 TCB (任务控制块)
void * pvOwner;
// 拥有该 链表项 的 链表
struct xLIST * configLIST_VOLATILE pxContainer;
// 检验一个 链表项 数据是否完整
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
typedef struct xLIST_ITEM ListItem_t;
MINI 链表项,链表项的缩减版,专门用于表示链表尾节点,在 32 位 MCU 上不启用链表项数据完整性校验的情况下相比于普通的链表项节省了 8 个字节(两个指针) 。
struct xMINI_LIST_ITEM
{
// 检验一个 MINI链表项 数据是否完整
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;
由多个链表项构成的链表,常用于区分不同任务状态或任务优先级,比如就绪状态的任务放在就绪链表中,阻塞状态的任务放在阻塞链表中,方便任务管理 。
typedef struct xLIST
{
// 检验一个 链表 数据是否完整
listFIRST_LIST_INTEGRITY_CHECK_VALUE
// 记录 链表 中 链表项 数目
volatile UBaseType_t uxNumberOfItems;
// 遍历 链表 的指针
ListItem_t * configLIST_VOLATILE pxIndex;
// 使用 MINI链表项 表示 链表尾部
MiniListItem_t xListEnd;
// 检验一个 链表 数据是否完整
listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
注意:由于不涉及数据校验完整性,因此下述函数中关于校验的所有部分将被删除 。
初始化链表项函数 。
将链表项的 pxContainer 成员设置为 NULL ,因为初始化的时候该链表项未被任何链表包含 。
void vListInitialiseItem( ListItem_t * const pxItem )
{
// 确保链表项未被记录在链表中
pxItem->pxContainer = NULL;
}
初始化链表函数,具体步骤如下 。
void vListInitialise( List_t * const pxList )
{
// 链表当前指针指向 xListEnd
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
// 设置链表尾链表项排序值为最大, 保证 xListEnd 会被放在链表的尾部
pxList->xListEnd.xItemValue = portMAX_DELAY;
// 尾链表项 xListEnd 的前/后链表项指针均指向自己
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
// 初始化时链表中有 0 个链表项
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}
将一个链表项插入到链表当前指针 pxIndex 指向的链表项前面,具体步骤如下 。
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
// 获取链表中当前指针 pxIndex 位置
ListItem_t * const pxIndex = pxList->pxIndex;
// 1. 改变自身 pxNext 和 pxPrevious
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
// 2. 改变前一个链表项的 pxNext
pxIndex->pxPrevious->pxNext = pxNewListItem;
// 3. 改变后一个链表项的 pxPrevious
pxIndex->pxPrevious = pxNewListItem;
// 标记新插入的链表项所在的链表
pxNewListItem->pxContainer = pxList;
// 链表数量增加一
( pxList->uxNumberOfItems )++;
}
为方便绘图演示,将链表的结构在图上做了简化,具体如下图所示 。
注意:这里 pxList->pxIndex 自初始化以来从未修改过,保持指向链表 xListEnd 链表项,下图所有演示中,橙色虚线表示该步骤做了修改,黑色实线表示与上一步骤相比无修改 。
插入一个链表项 。
插入第二个链表项 。
插入第三个链表项 。
插入第四个链表项 。
将一个链表项按照链表中所有链表项的 xItemValue 值大小升序插入链表中,具体步骤如下 。
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
// 记录要插入的链表项的排序值
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
// 如果新插入的链表项排序值为最大值,直接插到尾节点 xListEnd 的前面
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/*
1. 遍历链表,将当前链表项 pxIterator 与要插入的新的链表项 pxNewListItem
的 xItemValue 值比较,直到 pxIterator 的 xItemValue 大于 pxNewListItem
的 xItemValue 值,此时 pxNewListItem 应该插入到 pxIterator 的后面
*/
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext ){}
}
// 2. 改变要插入链表项自身 pxNext 和 pxPrevious
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxPrevious = pxIterator;
// 3. 改变后一个链表项的 pxPrevious
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
// 4. 改变前一个链表项的 pxNext
pxIterator->pxNext = pxNewListItem;
// 标记新插入的链表项所在的链表
pxNewListItem->pxContainer = pxList;
// 链表数量增加一
( pxList->uxNumberOfItems )++;
}
从链表中移除指定的链表项,具体步骤如下 。
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = pxItemToRemove->pxContainer;
// 1. 改变后一个链表项 pxPrevious
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
// 2. 改变前一个链表项 pxNext
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
// 确保索引指向有效的项目
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
// 从链表中移除链表项后,该链表项不属于任何链表
pxItemToRemove->pxContainer = NULL;
// 链表中链表项的数量减一
( pxList->uxNumberOfItems )--;
// 返回链表中链表项的数量
return pxList->uxNumberOfItems;
}
// 设置 pxListItem 的 pxOwner 成员
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )
// 设置 pxListItem 的 xValue 成员值
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )
// 获取 pxListItem 的 pxOwner 成员
#define listGET_LIST_ITEM_OWNER( pxListItem )
// 获取 pxListItem 的 xValue 成员值
#define listGET_LIST_ITEM_VALUE( pxListItem )
// 获取链表中头链表项的 xItemValue 成员值
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )
// 获取链表中头链表项地址
#define listGET_HEAD_ENTRY( pxList )
// 获取某个链表项的下一个链表项地址
#define listGET_NEXT( pxListItem )
// 获取链表中 xListEnd 的地址
#define listGET_END_MARKER( pxList )
// 获取链表当前长度
#define listCURRENT_LIST_LENGTH( pxList )
// 将链表中 pxIndex 指向下一个链表项,用于获取下一个链表项(任务)
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
// 获取链表中头链表项的 pvOwner 成员
#define listGET_OWNER_OF_HEAD_ENTRY( pxList )
// 获取链表项的 pxContainer 成员
#define listLIST_ITEM_CONTAINER( pxListItem )
// 判断链表是否为空
#define listLIST_IS_EMPTY( pxList )
// 判断链表项是否和链表匹配(链表项是否在该链表中)
#define listIS_CONTAINED_WITHIN( pxList, pxListItem )
// 判断链表是否被初始化
#define listLIST_IS_INITIALISED( pxList )
下面直接将 FreeRTOS 内核中 list.c / list.h 文件移植到自己的工程中使用(当然,如果你已经懂得双向链表数据结构的原理,你也可以构建属于你自己的 list.c / list.h 文件) 。
移植可以总结为以下 4 个步骤 。
/* portMacro.h */
#include <stdint.h>
#define port_CHAR char
#define port_FLOAT float
#define port_DOUBLE double
#define port_LONG long
#define port_SHORT short
#define port_STACK_TYPE unsigned int
#define port_BASE_TYPE long
typedef port_STACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
typedef port_STACK_TYPE* StackType_p;
typedef long* BaseType_p;
typedef unsigned long* UBaseType_p;
#if(configUSE_16_BIT_TICKS == 1)
typedef uint16_t TickType_t;
#define portMAX_DELAY (TickType_t) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY (TickType_t) 0xffffffffUL
#endif
#define pdFALSE ((BaseType_t) 0)
#define pdTRUE ((BaseType_t) 1)
#define pdPASS (pdTRUE)
#define pdFAIL (pdFALSE)
configUSE_16_BIT_TICKS 是一个宏,用于 TickType_t 类型位数,具体定义如下 。
/* FreeRTOSConfig.h */
// 设置 TickType_t 类型位 16 位
#define configUSE_16_BIT_TICKS 0
完成后编译工程应该不会出现错误,这样实现 RTOS 简单内核关键的双链表数据结构就完成了 。
最后此篇关于FreeRTOS简单内核实现2双向链表的文章就讲到这里了,如果你想了解更多关于FreeRTOS简单内核实现2双向链表的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在努力实现以下目标, 假设我有字符串: ( z ) ( A ( z ) ( A ( z ) ( A ( z ) ( A ( z ) ( A ) ) ) ) ) 我想编写一个正则
给定: 1 2 3 4 5 6
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
大家好,我卡颂。 Svelte问世很久了,一直想写一篇好懂的原理分析文章,拖了这么久终于写了。 本文会围绕一张流程图和两个Demo讲解,正确的食用方式是用电脑打开本文,跟着流程图、Demo一
身份证为15位或者18位,15位的全为数字,18位的前17位为数字,最后一位为数字或者大写字母”X“。 与之匹配的正则表达式: ?
我们先来最简单的,网页的登录窗口; 不过开始之前,大家先下载jquery的插件 本人习惯用了vs2008来做网页了,先添加一个空白页 这是最简单的的做法。。。先在body里面插入 <
1、MySQL自带的压力测试工具 Mysqlslap mysqlslap是mysql自带的基准测试工具,该工具查询数据,语法简单,灵活容易使用.该工具可以模拟多个客户端同时并发的向服务器发出
前言 今天大姚给大家分享一款.NET开源(MIT License)、免费、简单、实用的数据库文档(字典)生成工具,该工具支持CHM、Word、Excel、PDF、Html、XML、Markdown等
Go语言语法类似于C语言,因此熟悉C语言及其派生语言( C++、 C#、Objective-C 等)的人都会迅速熟悉这门语言。 C语言的有些语法会让代码可读性降低甚至发生歧义。Go语言在C语言的
我正在使用快速将 mkv 转换为 mp4 ffmpeg 命令 ffmpeg -i test.mkv -vcodec copy -acodec copy new.mp4 但不适用于任何 mkv 文件,当
我想计算我的工作簿中的工作表数量,然后从总数中减去特定的工作表。我错过了什么?这给了我一个对象错误: wsCount = ThisWorkbook.Sheets.Count - ThisWorkboo
我有一个 perl 文件,用于查看文件夹中是否存在 ini。如果是,它会从中读取,如果不是,它会根据我为它制作的模板创建一个。 我在 ini 部分使用 Config::Simple。 我的问题是,如果
尝试让一个 ViewController 通过标准 Cocoa 通知与另一个 ViewController 进行通信。 编写了一个简单的测试用例。在我最初的 VC 中,我将以下内容添加到 viewDi
我正在绘制高程剖面图,显示沿路径的高程增益/损失,类似于下面的: Sample Elevation Profile with hand-placed labels http://img38.image
嗨,所以我需要做的是最终让 regStart 和 regPage 根据点击事件交替可见性,我不太担心编写 JavaScript 函数,但我根本无法让我的 regPage 首先隐藏。这是我的代码。请简单
我有一个非常简单的程序来测量一个函数花费了多少时间。 #include #include #include struct Foo { void addSample(uint64_t s)
我需要为 JavaScript 制作简单的 C# BitConverter。我做了一个简单的BitConverter class BitConverter{ constructor(){} GetBy
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我是 Simple.Data 的新手。但我很难找到如何进行“分组依据”。 我想要的是非常基本的。 表格看起来像: +________+ | cards | +________+ | id |
我现在正在开发一个 JS UDF,它看起来遵循编码。 通常情况下,由于循环计数为 2,Alert Msg 会出现两次。我想要的是即使循环计数为 3,Alert Msg 也只会出现一次。任何想法都
我是一名优秀的程序员,十分优秀!