(学习日记)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链…...
单片机中的几种周期(振动/时钟,状态,机械,指令周期)表示的含义(51为例)
几种周期含义及个人理解描述 参考:短文,参考,百度 个人理解简述:对于几个周期性来说,可以认为是小单位的时间组合成了长时间。就像把一个数据赋值,这个是简单的一个机械周期能完成的动作,但需要…...
Spring Boot+Vue前后端分离项目如何部署到服务器
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
【学习总结】Ubuntu中vscode用ROS插件调试C++程序
1、教程 参考博客: 【ROS】 在VScode中 ROS Debug 配置方法非常详细版 关于launch文件的配置: launch.json {"version": "0.2.0","configurations": [{"name": "ROS: Launch","request"…...
html--蝴蝶
<!DOCTYPE html> <html lang"en" > <head> <meta charset"UTF-8"> <title>蝴蝶飞舞</title> <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.cs…...
线程的 sleep()方法和 yield()方法有什么区别?为什么 Thread 类的 sleep()和 yield ()方法是静态的?
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 线程的 sleep()方法和 yield()方法有什么区别 sleep()方法: sleep()方法使当前线程进入休眠状态,即暂停执行一段时间。它是静态方法,属于Thread类,调用…...
Java进阶 Maven基础
资料格式 配置文件 com.itheima Java代码 Statement stat con.createStatement(); 示例 com.itheima 命令 mvn test - Maven简介 传统项目管理状态分析 Maven 是什么 Maven的本质是一个项目管理工具,将项目开发过程抽象成一个项目对象模型(POM&…...
Spring Boot(六十八):SpringBoot 整合Apache tika 实现文档内容解析
1 Apache Tika 介绍 Apache Tika 是一个开源的内容检测和分析框架,由Apache软件基金会开发和维护的顶级项目。它可以从各种格式的文件中提取元数据和文本内容。Tika非常适合处理全文搜索、内容分析、翻译、内容提取等需要大量处理和分析文档内容的任务。Apache Tika提供了多种…...
jQuery+CSS3自动轮播焦点图特效源码
jQueryCSS3自动轮播焦点图特效源码,源码由HTMLCSSJS组成,双击html文件可以本地运行效果,也可以上传到服务器里面 下载地址 jQueryCSS3自动轮播焦点图特效源码...
面试经典150题(114-118)
leetcode 150道题 计划花两个月时候刷完之未完成后转,今天完成了5道(114-118)150 gap 了一周,以后就不记录时间了。。 114.(70. 爬楼梯) 题目描述: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不…...
HTML表单标签详解:如何用HTML标签打造互动网页?
在互联网的世界中,表单是用户与网站进行互动的重要桥梁。无论是注册新账号、提交反馈、还是在线购物,表单都扮演着至关重要的角色。在网页中,我们需要跟用户进行交互,收集用户资料,此时就需要用到表单标签。 HTML提供…...
科学数据分析网站html5/东莞市网络seo推广价格
1. Linux系统几种常见软件包: Debian(扩展名.deb) ubuntu主要支持的软件包,Ubuntu软件仓库中提供的软件包均采用这种封装 Red Hat(扩展名.rpm) Fedora支持的一种软件包 TarBall (扩展名.tar.gz / .tar.bz2),类似与win的.zip,可以只…...
wordpress屏蔽搜索引擎/批量外链工具
IntelliJ IDEA 2020.3提供了许多实用的功能,例如调试时的交互式提示,Git暂存支持,对Java 15记录和密封类的扩展支持等等。它简化了端点,框架和事件探查器的日常工作。通过基于机器学习技术的更好的代码完成,更直观和有…...
怎么做简单的微信浏览的网站/成都最新数据消息
http://www.52c51.com/article/63.html市场上盛行的电子高压灭蚊手拍(简称“电蚊拍”),以其实用、灭蚊效果好、无化学污染、安全卫生等优点,普遍受到人们的欢迎。其实这种电灭蚊拍线路简单,具有一定电子制作能力的青少…...
荆州哪里有做网站的/百度投广告怎么收费
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类; JDK api里是这么说的: ThreadPoolExecutor,它可另行安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额…...
益阳市 网站建设/企业营销策略分析论文
那是我处理GD东西的第一个方法.我正在尝试使用jcrop jquery插件实现调整大小和裁剪的功能.我仍然不知道如何保存裁剪后的图像.在jcrop网站上没有太多关于它的内容.这是我的代码:if ($_SERVER[REQUEST_METHOD] POST) {$targ_w $targ_h 150;$jpeg_quality 90;$src…...
专业网站建设设计公司/合肥网络推广优化公司
使用google的gson进行object和json的转换,如下: public static String object2json(Object obj) {Gson gson new Gson();return gson.toJson(obj);} 这样转出来的字符串特殊字符,比如url中的会变成unicode编码。 需要禁用html转义。 如下: public stati…...