(学习日记)2024.03.12:UCOSIII第十四节:时基列表
写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。
标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。
点击此处进入学习日记的总目录
2024.03.12
- 二十八、UCOSIII:时基列表
- 1、实现时基列表
- 1. 定义时基列表变量
- 2. 修改任务控制块TCB
- 2、实现时基列表相关函数
- 1. OS_TickListInit()函数
- 2. OS_TickListInsert()函数
- 3. OS_TickListRemove()函数
- 4. OS_TickListUpdate()函数
- 3、修改OSTimeDly()函数
- 4、修改OSTimeTick()函数
二十八、UCOSIII:时基列表
从本章开始,我们在OS
中加入时基列表。
时基列表是跟时间相关的,处于延时的任务和等待事件有超时限制的任务都会从就绪列表中移除,然后插入时基列表。
时基列表在OSTimeTick
中更新,如果任务的延时时间结束或者超时到期,就会让任务就绪,从时基列表移除,插入就绪列表。
到目前为止,我们在OS
中只实现了两个列表,一个是就绪列表,一个是本章将要实现的时基列表,在本章之前,任务要么在就绪列表,要么在时基列表。
1、实现时基列表
1. 定义时基列表变量
时基列表在代码层面上由全局数组OSCfg_TickWheel[]
和全局变量OSTickCtr
构成,一个空的时基列表示意图见图
/* 时基列表大小,在os_cfg_app.h 定义 */
#define OS_CFG_TICK_WHEEL_SIZE 17u
/* 在os_cfg_app.c 定义 */
/* 时基列表 *///(1)(2)
OS_TICK_SPOKE OSCfg_TickWheel[OS_CFG_TICK_WHEEL_SIZE];
/* 时基列表大小 */
OS_OBJ_QTY const OSCfg_TickWheelSize = (OS_OBJ_QTY )OS_CFG_TICK_WHEEL_SIZE;
/* 在os.h中声明 */
/* 时基列表 */
extern OS_TICK_SPOKE OSCfg_TickWheel[];
/* 时基列表大小 */
extern OS_OBJ_QTY const OSCfg_TickWheelSize;/* Tick 计数器,在os.h中定义 */
OS_EXT OS_TICK OSTickCtr; //(3)
- (1)
OS_TICK_SPOKE
为时基列表数组OSCfg_TickWheel[]
的数据类型, 在os.h
文件定义
typedefstruct os_tick_spoke OS_TICK_SPOKE;
//在μC/OS-III中,内核对象的数据类型都会用大写字母重新定义。struct os_tick_spoke {OS_TCB *FirstPtr; //时基列表OSCfg_TickWheel[]的每个成员都包含一条单向链表, 被插入该条链表的TCB会按照延时时间做升序排列。FirstPtr用于指向这条单向链表的第一个节点。OS_OBJ_QTY NbrEntries; //时基列表OSCfg_TickWheel[]的每个成员都包含一条单向链表, NbrEntries表示该条单向链表当前有多少个节点。OS_OBJ_QTY NbrEntriesMax; //时基列表OSCfg_TickWheel[]的每个成员都包含一条单向链表, NbrEntriesMax记录该条单向链表最多的时候有多少个节点, 在增加节点的时候会刷新,在删除节点的时候不刷新。
};
- (2):
OS_CFG_TICK_WHEEL_SIZE
是一个宏, 在os_cfg_app.h
中定义,用于控制时基列表的大小。
OS_CFG_TICK_WHEEL_SIZE
的推荐值为任务数/4
,不推荐使用偶数,如果算出来是偶数,则加1
变成质数,实际上质数是一个很好的选择。 - (3):
OSTickCtr
为SysTick
周期计数器, 记录系统启动到现在或者从上一次复位到现在经过了多少个SysTick
周期。
2. 修改任务控制块TCB
时基列表OSCfg_TickWheel[]
的每个成员都包含一条单向链表,被插入该条链表的TCB
会按照延时时间做升序排列,为了TCB
能按照延时时间从小到大串接在一起, 需要在TCB
中加入几个成员
struct os_tcb {CPU_STK *StkPtr;CPU_STK_SIZE StkSize;/* 任务延时周期个数 */OS_TICK TaskDelayTicks;/* 任务优先级 */OS_PRIO Prio;/* 就绪列表双向链表的下一个指针 */OS_TCB *NextPtr;/* 就绪列表双向链表的前一个指针 */OS_TCB *PrevPtr;/* 时基列表相关字段 */OS_TCB *TickNextPtr; //(1)OS_TCB *TickPrevPtr; //(2)OS_TICK_SPOKE *TickSpokePtr; //(5)OS_TICK TickCtrMatch; //(4)OS_TICK TickRemain; //(3)
};
带序号的字段可以配合上图一起理解,这样会比较容易。
上图是在时基列表 OSCfg_TickWheel[]
索引11这条链表里面插入了两个TCB
,一个需要延时1
个时钟周期,另外一个需要延时13
个时钟周期。
- (1):
TickNextPtr
用于指向链表中的下一个TCB
节点。 - (2):
TickPrevPtr
用于指向链表中的上一个TCB
节点。 - (3):
TickRemain
用于设置任务还需要等待多少个时钟周期,每到来一个时钟周期,该值会递减。 - (4):
TickCtrMatch
的值等于时基计数器OSTickCtr
的值加上TickRemain
的值, 当TickCtrMatch
的值等于OSTickCtr
的值的时候,表示等待到期,TCB
会从链表中删除。 - (5):每个被插入链表的TCB都包含一个字段
TickSpokePtr
,用于回指到链表的根部。
2、实现时基列表相关函数
时基列表相关函数在os_tick.c
实现,在os.h
中声明。
1. OS_TickListInit()函数
OS_TickListInit()
函数用于初始化时基列表,即将全局变量OSCfg_TickWheel[]
的数据域全部初始化为0
/* 初始化时基列表的数据域 */
void OS_TickListInit (void)
{OS_TICK_SPOKE_IX i;OS_TICK_SPOKE *p_spoke;for (i = 0u; i < OSCfg_TickWheelSize; i++) {p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];p_spoke->FirstPtr = (OS_TCB *)0;p_spoke->NbrEntries = (OS_OBJ_QTY )0u;p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;}
}
2. OS_TickListInsert()函数
OS_TickListInsert()
函数用于往时基列表中插入一个任务TCB
/* 将一个任务插入时基列表,根据延时时间的大小升序排列 */
void OS_TickListInsert (OS_TCB *p_tcb,OS_TICK time)
{OS_TICK_SPOKE_IX spoke;OS_TICK_SPOKE *p_spoke;OS_TCB *p_tcb0;OS_TCB *p_tcb1;p_tcb->TickCtrMatch = OSTickCtr + time; //(1)p_tcb->TickRemain = time; //(2)spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize); //(3)p_spoke = &OSCfg_TickWheel[spoke]; //(4)/* 插入 OSCfg_TickWheel[spoke] 的第一个节点 */if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) //(5){p_tcb->TickNextPtr = (OS_TCB *)0;p_tcb->TickPrevPtr = (OS_TCB *)0;p_spoke->FirstPtr = p_tcb;p_spoke->NbrEntries = (OS_OBJ_QTY)1u;}/* 如果插入的不是第一个节点,则按照TickRemain大小升序排列 */else //(6){/* 获取第一个节点指针 */p_tcb1 = p_spoke->FirstPtr;while (p_tcb1 != (OS_TCB *)0){/* 计算比较节点的剩余时间 */p_tcb1->TickRemain = p_tcb1->TickCtrMatch - OSTickCtr;/* 插入比较节点的后面 */if (p_tcb->TickRemain > p_tcb1->TickRemain){if (p_tcb1->TickNextPtr != (OS_TCB *)0){/* 寻找下一个比较节点 */p_tcb1 = p_tcb1->TickNextPtr;}else{ /* 在最后一个节点插入 */p_tcb->TickNextPtr = (OS_TCB *)0;p_tcb->TickPrevPtr = p_tcb1;p_tcb1->TickNextPtr = p_tcb;p_tcb1 = (OS_TCB *)0; //(7)}}/* 插入比较节点的前面 */else{/* 在第一个节点插入 */if (p_tcb1->TickPrevPtr == (OS_TCB *)0) {p_tcb->TickPrevPtr = (OS_TCB *)0;p_tcb->TickNextPtr = p_tcb1;p_tcb1->TickPrevPtr = p_tcb;p_spoke->FirstPtr = p_tcb;}else{/* 插入两个节点之间 */p_tcb0 = p_tcb1->TickPrevPtr;p_tcb->TickPrevPtr = p_tcb0;p_tcb->TickNextPtr = p_tcb1;p_tcb0->TickNextPtr = p_tcb;p_tcb1->TickPrevPtr = p_tcb;}/* 跳出while循环 */p_tcb1 = (OS_TCB *)0; //(8)}}/* 节点成功插入 */p_spoke->NbrEntries++; //(9)}/* 刷新NbrEntriesMax的值 */if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) //(10){p_spoke->NbrEntriesMax = p_spoke->NbrEntries;}/* 任务TCB中的TickSpokePtr回指根节点 */p_tcb->TickSpokePtr = p_spoke; //(11)
}
- (1):
TickCtrMatch
的值等于当前时基计数器的值OSTickCtr
加上任务要延时的时间time
,time
由函数形参传进来。
OSTickCtr
是一个全局变量, 记录的是系统自启动以来或者自上次复位以来经过了多少个SysTick
周期。
OSTickCtr
的值每经过一个SysTick
周期其值就加一,当TickCtrMatch
的值与其相等时,就表示任务等待时间到期。 - (2):将任务需要延时的时间
time
保存到TCB
的TickRemain
, 它表示任务还需要延时多少个SysTick
周期,每到来一个SysTick
周期,TickRemain
会减一。 - (3):由任务的
TickCtrMatch
对时基列表的大小OSCfg_TickWheelSize
进行求余操作, 得出的值spoke
作为时基列表OSCfg_TickWheel[]
的索引。
只要是任务的TickCtrMatch
对OSCfg_TickWheelSize
求余后得到的值spoke
相等, 那么任务的TCB就会被插入OSCfg_TickWheel[spoke]
下的单向链表中,节点按照任务的TickCtrMatch
值做升序排列。
举例:在上图中,时基列表OSCfg_TickWheel[]
的大小OSCfg_TickWheelSize
等于12
, 当前时基计数器OSTickCtr
的值为10
,有三个任务分别需要延时TickTemain=1
、TickTemain=23
和TickTemain=25
个时钟周期, 三个任务的TickRemain
加上OSTickCtr
可分别得出它们的TickCtrMatch
等于11、23和35
, 这三个任务的TickCtrMatch
对OSCfg_TickWheelSize
求余操作后的值spoke
都等于11
,所以这三个任务的TCB
会被插入OSCfg_TickWheel[11]
下的同一条链表, 节点顺序根据TickCtrMatch
的值做升序排列。 - (4):根据刚刚算出的索引值
spoke
,获取到该索引值下的成员的地址, 也叫根指针,因为该索引下对应的成员OSCfg_TickWheel[spoke]
会维护一条双向的链表。 - (5):将
TCB
插入链表中分两种情况,第一是当前链表是空的, 插入的节点将成为第一个节点,这个处理非常简单;第二是当前链表已经有节点。 - (6):当前的链表中已经有节点,插入的时候则根据
TickCtrMatch
的值做升序排列, 插入的时候分三种情况,第一是在最后一个节点之间插入, 第二是在第一个节点插入,第三是在两个节点之间插入。 - (7)(8):节点成功插入
p_tcb1
指针,跳出while
循环 - (9):节点成功插入,记录当前链表节点个数的计数器
NbrEntries
加一。 - (10):刷新
NbrEntriesMax
的值,NbrEntriesMax
用于记录当前链表曾经最多有多少个节点, 只有在增加节点的时候才刷新,在删除节点的时候是不刷新的。 - (11):任务
TCB
被成功插入链表,TCB
中的TickSpokePtr
回指所在链表的根指针。
3. OS_TickListRemove()函数
OS_TickListRemove()
用于从时基列表删除一个指定的TCB节点
/* 从时基列表中移除一个任务 */
void OS_TickListRemove (OS_TCB *p_tcb)
{OS_TICK_SPOKE *p_spoke;OS_TCB *p_tcb1;OS_TCB *p_tcb2;/* 获取任务TCB所在链表的根指针 */p_spoke = p_tcb->TickSpokePtr; //(1)/* 确保任务在链表中 */if (p_spoke != (OS_TICK_SPOKE *)0){/* 将剩余时间清零 */p_tcb->TickRemain = (OS_TICK)0u;/* 要移除的刚好是第一个节点 */if (p_spoke->FirstPtr == p_tcb) //(2){/* 更新第一个节点,原来的第一个节点需要被移除 */p_tcb1 = (OS_TCB *)p_tcb->TickNextPtr;p_spoke->FirstPtr = p_tcb1;if (p_tcb1 != (OS_TCB *)0){p_tcb1->TickPrevPtr = (OS_TCB *)0;}}/* 要移除的不是第一个节点 */ //(3)else{/* 保存要移除的节点的前后节点的指针 */p_tcb1 = p_tcb->TickPrevPtr;p_tcb2 = p_tcb->TickNextPtr;/* 节点移除,将节点前后的两个节点连接在一起 */p_tcb1->TickNextPtr = p_tcb2;if (p_tcb2 != (OS_TCB *)0){p_tcb2->TickPrevPtr = p_tcb1;}}/* 复位任务TCB中时基列表相关的字段成员 */ //(4)p_tcb->TickNextPtr = (OS_TCB *)0;p_tcb->TickPrevPtr = (OS_TCB *)0;p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;p_tcb->TickCtrMatch = (OS_TICK )0u;/* 节点减1 */p_spoke->NbrEntries--; //(5)}
}
- (1):获取任务TCB所在链表的根指针。
- (2):要删除的节点是链表的第一个节点,这个操作很好处理,只需更新下第一个节点即可。
- (3):要删除的节点不是链表的第一个节点,则先保存要删除的节点的前后节点,然后把这前后两个节点相连即可。
- (4):复位任务TCB中时基列表相关的字段成员。
- (5):节点删除成功,链表中的节点计数器NbrEntries减一。
4. OS_TickListUpdate()函数
OS_TickListUpdate()
在每个SysTick
周期到来时在OSTimeTick()
被调用,用于更新时基计数器OSTickCtr
, 扫描时基列表中的任务延时是否到期
void OS_TickListUpdate (void)
{OS_TICK_SPOKE_IX spoke;OS_TICK_SPOKE *p_spoke;OS_TCB *p_tcb;OS_TCB *p_tcb_next;CPU_BOOLEAN done;CPU_SR_ALLOC();/* 进入临界段 */OS_CRITICAL_ENTER();/* 时基计数器++ */OSTickCtr++; //(1)spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize); //(2)p_spoke = &OSCfg_TickWheel[spoke];p_tcb = p_spoke->FirstPtr;done = DEF_FALSE;while (done == DEF_FALSE){if (p_tcb != (OS_TCB *)0) //(3){p_tcb_next = p_tcb->TickNextPtr;p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr; //(4)/* 节点延时时间到 */if (OSTickCtr == p_tcb->TickCtrMatch) //(5){/* 让任务就绪 */OS_TaskRdy(p_tcb);}else //(6){/* 如果第一个节点延时期未满,则退出while循环因为链表是根据升序排列的,第一个节点延时期未满,那后面的肯定未满 */done = DEF_TRUE;}/* 如果第一个节点延时期满,则继续遍历链表,看看还有没有延时期满的任务如果有,则让它就绪 */p_tcb = p_tcb_next; //(7)}else{done = DEF_TRUE; //(8)}}/* 退出临界段 */OS_CRITICAL_EXIT();
}
- (1):每到来一个
SysTick
时钟周期,时基计数器OSTickCtr
都要加一操作。 - (2):计算要扫描的时基列表的索引,每次只扫描一条链表。
时基列表里面有可能有多条链表,为啥只扫描其中一条链表就可以?
因为任务在插入时基列表的时候, 插入的索引值spoke_insert
是通过TickCtrMatch
对OSCfg_TickWheelSize
求余得出。
现在需要扫描的索引值spoke_update
是通过OSTickCtr
对OSCfg_TickWheelSize
求余得出,TickCtrMatch
的值等于OSTickCtr
加上TickRemain
,只有在经过TickRemain
个时钟周期后,spoke_update
的值才有可能等于spoke_insert
。
如果算出的spoke_update
小于spoke_insert
, 且OSCfg_TickWheel[spoke_update]下的链表的任务没有到期,那后面的肯定都没有到期,不用继续扫描。
举例,在上图时基列表中有三个TCB ,时基列表OSCfg_TickWheel[]的大小OSCfg_TickWheelSize等于12, 当前时基计数器OSTickCtr的值为7,有三个任务分别需要延时TickTemain=16、TickTemain=28和TickTemain=40个时钟周期, 三个任务的TickRemain加上OSTickCtr可分别得出它们的TickCtrMatch等于23、35和47
这三个任务的TickCtrMatch对OSCfg_TickWheelSize求余操作后的值spoke都等于11, 所以这三个任务的TCB会被插入OSCfg_TickWheel[11]下的同一条链表,节点顺序根据TickCtrMatch的值做升序排列。
当下一个SysTick时钟周期到来的时候,会调用OS_TickListUpdate()函数,这时OSTickCtr加一操作后等于8, 对OSCfg_TickWheelSize(等于12)求余算得要扫描更新的索引值spoke_update等8,则对OSCfg_TickWheel[8]下面的链表进行扫描, 从 图时基列表中有三个TCB 可以得知,8这个索引下没有节点,则直接退出,刚刚插入的三个TCB是在OSCfg_TickWheel[11]下的链表, 根本不用扫描,因为时间只是刚刚过了1个时钟周期而已,远远没有达到他们需要的延时时间。
- (3):判断链表是否为空,为空则跳转到第(8)步骤。
- (4):链表不为空,递减第一个节点的
TickRemain
。 - (5):判断第一个节点的延时时间是否到,如果到期,让任务就绪, 即将任务从时基列表删除,插入就绪列表,这两步由函数
OS_TaskRdy()
来完成, 该函数在os_core.c
中定义,具体实现见 代码清单:时基列表-8。
void OS_TaskRdy (OS_TCB *p_tcb)
{/* 从时基列表删除 */OS_TickListRemove(p_tcb);/* 插入就绪列表 */OS_RdyListInsert(p_tcb);
}
- (6):如果第一个节点延时期未满,则退出
while循环
, 因为链表是根据升序排列的,第一个节点延时期未满,那后面的肯定未满。 - (7):如果第一个节点延时到期,则继续判断下一个节点延时是否到期。
- (8):链表为空,退出扫描,因为其他还没到期。
3、修改OSTimeDly()函数
加入时基列表之后,OSTimeDly()
函数需要被修改,迭代的代码已经用条件编译屏蔽。
void OSTimeDly(OS_TICK dly)
{CPU_SR_ALLOC();/* 进入临界区 */OS_CRITICAL_ENTER();
#if 0/* 设置延时时间 */OSTCBCurPtr->TaskDelayTicks = dly;/* 从就绪列表中移除 *///OS_RdyListRemove(OSTCBCurPtr);OS_PrioRemove(OSTCBCurPtr->Prio);
#endif/* 插入时基列表 */OS_TickListInsert(OSTCBCurPtr, dly);/* 从就绪列表移除 */OS_RdyListRemove(OSTCBCurPtr);/* 退出临界区 */OS_CRITICAL_EXIT();/* 任务调度 */OSSched();
}
4、修改OSTimeTick()函数
加入时基列表之后,OSTimeTick()
函数需要被修改,被迭代的代码已经用条件编译屏蔽。
void OSTimeTick (void)
{
#if 0unsigned int i;CPU_SR_ALLOC();/* 进入临界区 */OS_CRITICAL_ENTER();for (i=0; i<OS_CFG_PRIO_MAX; i++){if (OSRdyList[i].HeadPtr->TaskDelayTicks > 0){OSRdyList[i].HeadPtr->TaskDelayTicks --;if (OSRdyList[i].HeadPtr->TaskDelayTicks == 0){/* 为0则表示延时时间到,让任务就绪 *///OS_RdyListInsert (OSRdyList[i].HeadPtr);OS_PrioInsert(i);}}}/* 退出临界区 */OS_CRITICAL_EXIT();#endif/* 更新时基列表 */OS_TickListUpdate();/* 任务调度 */OSSched();
}
相关文章:

(学习日记)2024.03.12:UCOSIII第十四节:时基列表
写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈。 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录&a…...

四.流程控制(顺序,分支,循环,嵌套)
c刚刚转过来的记得写在public static void main(String[] args)的花括号里 一.顺序结构 二.分支结构 if ,switch 1.if (条件判断) 2.if else 3.if else if else if ... else(它是一个一个否定来一个个执行判断的 4.s…...

了解常用开发模型 -- 瀑布模型、螺旋模型、增量与迭代、敏捷开发
目录 瀑布模型 开发流程 开发特征 优缺点 适用场景 螺旋模型 开发流程 开发特征 优缺点 适用场景 增量与迭代开发 什么是增量开发?什么是迭代开发? 敏捷开发 什么是敏捷开发四原则(敏捷宣言)? 什么是 s…...

使用 Vue CLI 创建一个 Vue2 项目
全局安装 Vue CLI 参考官网 Vue CLI,安装命令如下 npm install -g vue/cli 目前 Vue CLI 的最新版本为 v5.0.8 创建 Vue2 项目 在希望创建项目的目录下打开命令行,键入命令 vue create my-project 其中 my-project 更改为自己需要的项目名 随后&a…...

Linux工具 - 耀眼的git
~~~~ 前言耀眼的GitGit是什么(本质)Git出现的背景(本着开源的精神)在命令行中使用Git(Come on 来使用Git吧).git文件说明新建仓库git clone 克隆云端仓库到本地git addgit commit -mgit pushgit pullgit st…...

Spring Security的开发
文章目录 1,介绍2, 核心流程3, 核心原理3.1 过滤器链机制3.2 主体3.3 认证3.4 授权3.5 流程图4, 核心对象4.1 UserDetailsService 接口4.2 PasswordEncoder 接口4.3 hasAuthority方法4.4 hasAnyAuthority方法4.5 hasRole方法4.5 hasAnyRole方法5, 核心注解5.1 @PreAuthorize5.1…...

C语言 实用调试技巧
我们的博客已经更新到了数据结构,但是当我在深耕数据结构时我发现我在C语言是遗漏了一个重要的东西,那就是C语言的使用调试技巧。这篇博客对数据结构非常重要,请大家耐心观看。 1. 什么是bug? 第一次被发现的导致计算机错误的飞蛾…...
GPT的实现细节
关于GPT的代码细节,这里梳理了一下: 数据集构造 原始数据集schema: inputwho is your favorite basketball player? outputOf course Kobe Bryant!那么在构造训练集时,根据chunk size构造多个输入: input_1who is …...

docker安装Milvus
docker安装Milvus 拉去CPU版本的milvus镜像 $ sudo docker pull milvusdb/milvus:0.10.0-cpu-d061620-5f3c00 docker pull milvusdb/milvus:0.10.0-cpu-d061620-5f3c00 mkdir -p milvus/conf cd milvus/conf ls wget https://raw.githubusercontent.com/milvus-io/milvus/v0.1…...

HTML静态网页成品作业(HTML+CSS)——世博园介绍(2个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有2个页面。 二、作品演示 三、代…...
微信小程序订阅消息授权弹窗事件
微信小程序消息订阅授权弹窗事件 ,每次授权完成之后,只可以推送一条模板消息。 目录 1、HTML代码 2、JS代码 1、HTML代码 <button bindtap"openPopup" class"openPopup">订阅消息</button> 2、JS代码 // 是否设置过授…...

谷歌的后量子密码学威胁模型
1. 引言 若现在不使用量子安全算法来加密数据,能够存储当前通信的攻击者最快十年内就能对其解密。这种先存储后解密的攻击是当前采用后量子密码学 (post-quantum cryptography,PQC) 背后的主要动机,但其他未来的量子计算威胁也需要一个深思熟…...

机器人在果园内行巡检仿真
文章目录 创建工作空间仿真果园场景搭建小车模型搭建将机器人放在仿真世界中创建工作空间 mkdir -p ~/catkin_ws/src cd ~/catkin_ws仿真果园场景搭建 cd ~/catkin_ws/src git clone https://gitcode.com/clearpathrobotics/cpr_gazebo.git小车模型搭建 DiffBot是一种具有两个…...
蓝桥杯算法基础(14):十大排序算法(归并排序)c语言版
归并排序 基于分而治之的思想,拿两个已经有序的序列重新组合成一个新的有序序列. 这是一个简单的合并函数,需要两个序列都有序 //默认a和b数组都是有序的 //temp为一个数组的首地址 void mergeSort(int a[],int,alen,int b[],int blen,int* temp){int …...

力扣刷题(DAY09-DAY11)
Day09 0958. 二叉树的完全性检验 知识点:完全二叉树:在一棵完全二叉树中,除了最后一层外,所有层都被完全填满,并且最后一层中的所有节点都尽可能靠左。最后一层(第 h 层)中可以包含 1 到 个节点…...
IPC之管道
什么是管道? 管道的本质是操作系统在内核中创建出的一块缓冲区,也就是内存 管道的应用 $ ps aux | grep xxx ps aux 的标准输出写到管道,grep 从管道这块内存中读取数据来作为它的一个标准输入,而且 ps 和 grep 之间是兄弟关系&a…...

VUE-组件间通信(二)$emit
$emit 1、单向绑定 子组件向父组件传值 2、使用示例 父组件 <template><div id"app"><!-- 监听自定义触发事件 emitInvokeEvents--><SonDemo emitInvokeEvents"fatherFunction"></SonDemo></div> </template&…...
java 程序连接 redis 集群 的时候报错 MUTLI is currently not supported in cluster mode
找了半天找不到,为什么国内文章环境是真的差, redis 集群不支持事务,而你的方法上面估计使用了 spring 的事务导致错误具体解决: Transactional(propagation Propagation.NOT_SUPPORTED)public <T> void removeMultiCacheMapValue…...

AVP-SLAM:自动泊车系统中的语义SLAM_
AVP-SLAM:自动泊车系统中的语义SLAM 附赠最强自动驾驶学习资料:直达链接 ●论文摘要 在自动代客泊车系统中车辆在狭窄且拥挤且没有GPS信号的停车场中进行导航,具备准确的定位能力是至关重要的。传统的基于视觉的方法由于在停车场中由于缺少…...

PHP反序列化--pop链
目录 一、了解pop链 1、pop链: 2、pop链触发规则: (1)通过普通函数触发: (2)通过魔术方法触发: 3、pop链魔术方法例题: 一、了解pop链 1、pop链: pop链…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...

【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...

Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...