(学习日记)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链…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...