uCOSIII实时操作系统 四 任务管理

目录

uCOSIII启动过程:

stm32的启动过程:

uCOSIII的启动过程:

任务状态:

任务控制块:

任务堆栈:

任务就绪表:

优先级位映射表//OSPrioTbl[]

位映射表:

查找优先级:

什么是前导零指令呢?

就绪任务列表OSRdyList[]

任务的调度与切换

调度基础:

任务调度器:

中断级调度器:

时间片轮转调度:


uCOSIII启动过程:

stm32的启动过程:

在系统上电的时候第一个执行的是启动文件(.s文件)里边由汇编语言编写的复位函数Reset_Handler.复位函数的最后会调用C库函数_main,_main()函数的主要工作是初始化系统的堆和栈,最后调用C中的main()函数,进入C的编译环境下。

uCOSIII的启动过程:

uCOSIII启动是有一定顺序的所以在使用的时候按照顺序打开

  1. 调用OSInit()初始化UCOSIII系统。
  2. 创建任务。一般来说只在main()函数中创建一个开始(start_task)任务,其他任务在该任务中进行创建。创建任务之前,使用OS_CRITICAL_ENTER()函数进入临界区域,再利用OSTaskCreate()函数创建任务,任务创建完之后利用OS_CRITICAL_EXIT()退出临界状态。(RT-Thread和FreeRTOS则默认使用这种形式)。
  3. 当任务创建好后,就是处于任务就绪。在就绪态的任务可以参加操作系统的调度,任务调度器只启动一次,之后就不会在执行了,uCO/OS中启动任务调度器的函数是OSStart(),并且启动任务调度器的时候就不回返回了,从此任务都是由uCOS管理。调用OSStart()函数开启UCOSIII系统。

流程图可表示为:

打开main函数,代码如下:

int main(void)
{OS_ERR err;
CPU_SR_ALLOC();
/***********STM32硬件初始化***********/
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
uart_init(115200); //串口初始化
LED_Init(); //LED初始化
/***************END******************///第一步://初始化UCOSIII
OSInit(&err); //第二步://进入临界区
OS_CRITICAL_ENTER();//第三步://创建开始任务
OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块
(CPU_CHAR * )"start task", //任务名字
(OS_TASK_PTR )start_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )START_TASK_PRIO, //任务优先级
(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值//第四步:退出临界区
OS_CRITICAL_EXIT(); //退出临界区//第五步:开启UCOSIII
OSStart(&err); //开启UCOSIII
while(1);
}

任务状态:

UCOSIII支持的是单核CPU,不支持多核CPU,这样某一时刻只有一个任务会获得CPU的使用权进入运行态,其他任务就会进入其他状态,

UCOSIII有多个任务态 参考该篇文章的任务状态 http://t.csdnimg.cn/5OTXd

任务控制块:

引入:在裸机系统中,程序的主体是 CPU 按照顺序执行的。而在多任务系统中,任务的执行是由系统调度的。

系统为了顺利的调度任务使用OSTaskCreate()函数来创建任务的时候,为每一个任务定义了一个控制块TCB(Task ConTrol Block),这个任务控制块就相当于任务的身份证,里边含有任务的所有信息,比如任务的堆栈,任务名称,任务的形参等。有了这个控制块之后,以后系统对任务的全部操作都可以通过这个TCB来实现。

任务堆栈:

暂停一个任务,以后又能恢复运行,必须考虑将这个运行的信息保存,而回复运行的时候需要将这些信息恢复到运行环境。所以任务切换必须做环境的保护和恢复操作。

因此每个任务都应该有自己的堆栈,下面创建一个堆栈:

1、定义一个CPU_STK变量,在UCOSIII中CPU_STK数据类型用来定义任务堆栈。

//任务堆栈大小
#define LED0_STK_SIZE 128
//任务控制块
OS_TCB Led0TaskTCB;
//任务堆栈
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];

2、在使用OSTaskCreat()函数创建任务的时候将堆栈地址转递给OSTaskCreate函数的参数p_stk_base,将堆数据深度传递给参数stk_limit,堆栈深度通常为堆栈大小的十分之一,主要用来检测堆栈是否为空,将堆栈大小转递给stk_size。

(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小

任务就绪表:

引入:多任务操作系统的主要工作是为系统中处于就绪状态的任务分配CPU资源。

其中涉及到的两个关键:

  1. 判断那些任务处于就绪状态(找优先级)。
  2. 确定那个任务应该马上得到执行(找任务)。

在UCOSIII中,所有已经就绪等待的任务都会被放在一个所谓的就绪表当中,任务就绪表包括两部分:

  1. 一个优先级位映射表OSPrioTbl[],用来标注那个优先级下有任务就绪。
  2. 一个是就绪任务列表OSRdyList[],用来记录每一个优先级下所有的就绪任务,其中包括指向各个就绪任务的指针。

优先级位映射表//OSPrioTbl[]

OSPrioTbl[]在os_prio.c中有定义:
CPU_DATA OSPrioTbl[OS_PRIO_TBL_SIZE]; //UCOSIII_CORE中os_prio.c中41行

根据CPU_DATA的值,该表的成员元素的宽度可以为8,16,32位,跳转可知:

在STM32中CPU_DATA为unsigned int,有4个字节,32位。因此表OSPrioTbl每个参数有32位。其中每个位对应一个优先级,当某个任务就绪之后就会将优先级映射表中相应的位置1。

UCOSIII 中任务数目由宏 OS_CFG_PRIO_MAX 配置的(见 os_cfg.h)。

UCOS-III和逻辑一样,数值越小,优先级越高,因此优先级0是优先级最高的。 优先级OS_CFG_PRIO_MAX-1 的优先级最低(也就是63)。uCOSIII将最小优先级唯一的分配给空闲任务,其他任务不允许被设置为这个优先级。当任务准备好运行了,根据任务的优先级,位映像表中相应位就会被设置为 1。(如果处理器支持位清零指令 CLZ,这个指令会加快位映像表的设置过程。)

位映射表:

根据映射表可以知道OSPrioTb[]的大小是32位(unsigned int)在UCOSIII中咱们得中断有64个优先级,所以两个OSPrioTb[]既可以撑起咱们64个优先级。

想要验证OSPrioTb[]的个数是否为2通过OS_PRIO_TBL_SIZE转跳进入在os.h中

查找优先级:

UCOSIII中优先级是怎么查找的?

  1. 遍历OSPrioTb[]
  2. 判断OSPrioTb[N]是否等于0
  3. 若不为0找出不为零的那位(从左往右)

在UCOSIII中定义了函数OS_PrioGetHighest()用于找到就绪了的最高优先级的任务(位置:os_prio.c中85行 )

//源码分析
OS_PRIO  OS_PrioGetHighest (void)
{CPU_DATA  *p_tbl;OS_PRIO    prio;prio  = (OS_PRIO)0;//从OSPrioTbl[0]开始扫描映射表,一直遇到非零项。p_tbl = &OSPrioTbl[0];while (*p_tbl == (CPU_DATA)0) {   //当数组OSPrioTbl[]中的某个元素为零的时候,就继续扫描下一个数组的元素, prio 加  DEF_INT_CPU_NBR_BITS(32)                   prio += DEF_INT_CPU_NBR_BITS;   //p_tbl加一,继续寻找OSProTb[]中的下一个元素                    p_tbl++;}//一旦找到一个非0项,再加上该项的前导0的数量(从左到右第一为1的位置),就找到了最高优先级任务了。prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);             return (prio);
}

注意:一个操作系统如果只是具备了高优先级任务能够“立即”获得处理器并得到执行的特点,那么它仍然不算是实时操作系统。因为这个查找最高优先级任务的过程决定了调度时间是否具有实时性。

例如一个包含 n 个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和 n 相关,而下一个就绪任务抉择时间的长短将会极大的影响系统的实时性。

μC/OS 内核中采用两种方法寻找最高优先级的任务,第一种是通用的方法,因为 μC/OS 防止 CPU平台不支持前导零指令,就采用 C 语言模仿前导零指令的效果实现了快速查找到最高优先级任务的方法。而第二种方法则是特殊方法,利用硬件计算前导零指令 CLZ,这样子一次就能知道哪一个优先级任务能够运行,这种调度算法比普通方法更快捷,但受限于平台(在 STM32 中我们
就使用这种方法)。

什么是前导零指令呢?

如果分别建立了优先级3,5,8,11这个四个任务,任务创建成功之后,调用CPU_CntLeadZeros()我们可以计算出 OSPrioTbl[0] 第一个置 1 的位前面有 3 个 0,那么这个 3 就是我们要查找的最高优先级,至于后面还有多少个位置 1 我们都不用管,只需要找到第一个 1 即可。

就绪任务列表OSRdyList[]

通过上一步我们已经知道了哪一个优先级的任务已经就绪,但是UCOSIII支持时间片轮转调度,同一个优先级下可以有多个任务。而就绪任务列表OSRdyList[]就是用来记录优先级每个优先级所有的就绪任务。

准备运行好的任务放到就绪列表当中。就绪列表是一个数组OSRdyList[],它一共有 OS_CFG_PRIO_MAX 条记录,记录的数据类型为 OS_RDY_LIST( 见 OS.H)。如下图所示

转跳进该数据类型之后就绪列表中的每条记录都包含了三个变量NbrEntries,TailPtr,HeadPtr

NbrEntries:表示的是该优先级就绪任务数。当优先级没有就绪任务的时候,该变量的值被设置为0;

.TailPtr 和.HeadPtr 用亍该优先级就绪任务的建立双向列表。.HeadPtr 指向列表的头部,TailPtr 指向列表的尾部。

下图当有两个任务的时候逻辑图如下,此时NbrEntries=2

任务的调度与切换

调度基础:

调度器,决定了任务的运行顺序,UCOSIII是一个可以抢占优先级的,基于优先级内核。根据起重要性每个任务分配一个优先级。UCOSIII支持多个任务拥有相同的优先级(这一点UCOSII中没有)。

操作系统通过两个函数完成任务的调度功能:

  1. OSSched():在任务级被调用
  2. OSIntExit():在中断级被调用

确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的

任务调度器:

源码的位置:os_core.c:

源码分析:

//OSSched为任务级调度器,在中断服务函数中不能使用
void  OSSched (void)
{CPU_SR_ALLOC();如果在中断服务子程序中调用OSSched(),此时中断嵌套层数>0。函数return直接返回if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              return;                                             }//用户至少调用了一次给任务调度上锁函数OSSchedLock(),使OSSchedLockNestingCtr>0。函数return直接返回if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0){        return;                                             }在任务执行的时候不希望中断打断任务执行,所以先关闭中断CPU_INT_DIS();//从后边的注释可以知道是获取准备好的最高优先级,(从任务就绪表中获取)OSPrioHighRdy   = OS_PrioGetHighest();                  /* Find the highest priority ready                        *///获取下次任务切换要运行的任务,OSTCBHighRdyPtr指向将要切换任务的OS_TCB(任务块)OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;//判断要运行的任务是否是正在运行的任务,是就不用切换if (OSTCBHighRdyPtr == OSTCBCurPtr) {                   /* Current task is still highest priority task?           */CPU_INT_EN();                                       /* Yes ... no need to context switch                      */return;}#if OS_CFG_TASK_PROFILE_EN > 0uOSTCBHighRdyPtr->CtxSwCtr++;                            /* Inc. # of context switches to this task                */
#endifOSTaskCtxSwCtr++;                                       /* Increment context switch counter                       */#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS_TaskSw();
#endif//调用OS_TASK_SW()来完成实际上的任务切换OS_TASK_SW();                                           /* Perform a task level context switch                    *///开启中断CPU_INT_EN();
}

有时候我们并不希望发生任务调度,因为始终有一些代码的执行过程是不能被打断的。此时我们就可以使用函数OSSchedLock()对调度器加锁,当我们想要恢复任务调度的时候就可以使用函数OSSchedUnlock()给已经上锁的任务调度器解锁。

注意:OSSched()中真正执行任务切换的宏OS_TASK_SW()(在os_cpu.h中定义),宏OS_TASK_SW()就是函数OSCtxSW(),OSCtxSW()是os_cpu_a.asm中汇编编写的一段代码,OSCtxSW()要做的就是将当前任务的CPU寄存器的值保存在堆栈当中,也就是现场保护,保存完当前任务的的现场后将新的任务OS_TCB中保存任务堆栈指针加载到CPU的堆栈指针寄存器中,最后还要从新任务的堆栈重恢复CPU寄存器的值。

中断级调度器:

OSIntExit();位置:os_core.c中第300行

//中断级任务调度器
void  OSIntExit (void)
{CPU_SR_ALLOC();//进入临界段//判断UCOSIII是否执行if (OSRunning != OS_STATE_OS_RUNNING) {                 /* Has the OS started?                                    */return;                                             /* No                                                     */}//关闭中断防止被中断打断的代码CPU_INT_DIS();OSIntNestingCtr中断嵌套计数器,判断是否为0if (OSIntNestingCtr == (OS_NESTING_CTR)0) {             /* Prevent OSIntNestingCtr from wrapping                  */CPU_INT_EN();return;}//中断层数减一OSIntNestingCtr--;//因为OSIntExit()是在退出中断服务函数时调用的,因此中断嵌套计数器要减1///若 OSIntNestingCtr大于0时,说明还有其他的中断发生,那么跳回到中断服务程序中不需要任务切换if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* ISRs still nested?                                     */CPU_INT_EN();                                       /* Yes                                                    */return;}//检查调度器是否加锁,如果加锁的话就直接跳出,不需要做任何任务切换if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Scheduler still locked?                                */CPU_INT_EN();                                       /* Yes                                                    */return;}//接下来的五行程序和任务级调度器是一样的,从OSRdyList[]中取出最高优先级的任务控制块OS_TCBOSPrioHighRdy   = OS_PrioGetHighest();                  /* Find highest priority                                  */OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;     /* Get highest priority task ready-to-run                 */if (OSTCBHighRdyPtr == OSTCBCurPtr) {                   /* Current task still the highest priority?               */CPU_INT_EN();                                       /* Yes                                                    */return;}#if OS_CFG_TASK_PROFILE_EN > 0uOSTCBHighRdyPtr->CtxSwCtr++;                            /* Inc. # of context switches for this new task           */
#endifOSTaskCtxSwCtr++;                                       /* Keep track of the total number of ctx switches         */#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS_TaskSw();
#endif//调用中断级任务切换函数OSIntCtxSw();  /* Perform interrupt level ctx switch                     *///开启中断CPU_INT_EN();
}

注意:

在中断级调度器中真正完成任务切换的就是中断级任务切换函数OSIntCtxSWO(),与任务级切换函数 OSCtxSWO()不同的是,由于进入中断的时候现场已经保存过了,所以 OSIntCtxSWO()不需要像OSCtxSWO()一样先保存当前任务现场,只需要做 OSCtxSWO()的后半部分工作,也就是从将要执行的任务堆栈中恢复 CPU 寄存器的值。

时间片轮转调度:

UCOSIII 是支持多个任务拥有同一个优先级的,当多个任务有相同的优先级的时候,这些任务采用时间片轮转调度方法进行任务调度,uC/OS-III 允许每个任务运行规定的时间片,当任务没有用完分配给他的时间片它可以自愿地放弃 CPU。uC/OS-III 允许任务在运行时开启戒者关闭循环轮转调度。在 os_cfg.h 文件中有个宏 OS_CFG_SCHED_ROUND_ROBIN_EN,我们要想使用时间片轮转调度就需要将OS_CFG_SCHED_ROUND_ROBIN_EN 定义为 1这样 UCOSIII中有关时间片轮转调度的代码才会被编译,否则不能使用时间片轮转调度。

下边是正点原子对时间片轮转调度的解释:

UCOSIII通调用OS_SchedRoundRobin()函数来完成就会切换到该优先级对应的下一任务中。

该函数的位置:

源码解释:

//源码解析
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
void  OS_SchedRoundRobin (OS_RDY_LIST  *p_rdy_list)
{OS_TCB   *p_tcb;//任务控制块变量CPU_SR_ALLOC();//进入临界段//检查是否开启了时间片轮转调度if (OSSchedRoundRobinEn != DEF_TRUE) {                  /* Make sure round-robin has been enabled                 */return;}CPU_CRITICAL_ENTER();//关闭中断//获得最高优先级的就绪任务(指针地址)p_tcb = p_rdy_list->HeadPtr;                            /* Decrement time quanta counter                          *///无任务if (p_tcb == (OS_TCB *)0) {CPU_CRITICAL_EXIT();return;}//判断是否为空闲任务if (p_tcb == &OSIdleTaskTCB) {CPU_CRITICAL_EXIT();return;}//判断当前任务的时间片是否还有if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {p_tcb->TimeQuantaCtr--;}//是否还剩余时间片,有的话就跳出去返回if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {                /* Task not done with its time quanta                     */CPU_CRITICAL_EXIT();return;}//NbrEntries是代表的某个优先级下的任务,这里判断是否小于2,小于2就不用进行任务切换了直接返回if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {           /* See if it's time to time slice current task            */CPU_CRITICAL_EXIT();                                /* ... only if multiple tasks at same priority            */return;}//判断调度器是否上锁上锁的话直接返回if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Can't round-robin if the scheduler is locked           */CPU_CRITICAL_EXIT();return;}//当执行到这一步的时候说明当前任务的时间片已经用完了,将当前任务的任务块OS_TCB从双向链表的链表头移到链表尾()OS_RdyListMoveHeadToTail(p_rdy_list);                   /* Move current OS_TCB to the end of the list             */
//从这开始都是在为下一次任务做铺垫//重新获取新的双向链表表头也就是下一个要执行的任务p_tcb = p_rdy_list->HeadPtr;                            /* Point to new OS_TCB at head of the list                *///是否使用默认时间片长度第一个默认,第二个用户指定的值if (p_tcb->TimeQuanta == (OS_TICK)0) {                  /* See if we need to use the default time slice           */p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;} else {p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;           /* Load time slice counter with new time                  */}CPU_CRITICAL_EXIT();//开启中断
}
#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/153809.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 环境配置(详细教程)

文章目录 前言一、jdk 下载二、windows1、jdk 安装2、环境变量的配置2.1 Java_Home 配置2.2 Path 配置2.3 CLASSPATH 配置 3、检测是否配置成功 前言 java 环境配置&#xff0c;网上教程很多&#xff0c;那我为什么还要写&#xff1f; 首先为了完善我的知识体系今后一些软件的…

【大数据】Spark入门指南:从基础概念到实践应用全解析

原创不易&#xff0c;注重版权。转载请注明原作者和原文链接 文章目录 Spark是什么Spark组件Spark的优势Word Count Spark基本概念ApplicationDriverMaster & WorkerExecutorRDDJobTaskStageShuffleStage的划分窄依赖 & 宽依赖 DAG Spark执行流程Spark运行模式RDD详解R…

Transformer为什么如此有效 | 通用建模能力,并行

目录 1 更强更通用的建模能力 2 并行计算 3 大规模训练数据 4 多训练技巧的集成 Transformer是一种基于自注意力机制的网络&#xff0c;在最近一两年年可谓是大放异彩&#xff0c;我23年入坑CV的时候&#xff0c;我看到的CV工作似乎还没有一个不用到Transformer里的一些组…

存档&改造【04】二维码操作入口设置细节自动刷新设置后的交互式网格内容的隐藏

因为数据库中没有数据无法查看设置效果&#xff0c;于是自己创建了个测试数据表&#xff0c;用来给demo测试 -- 二维码操作入口设置 create table JM_QR_CODE(QR_CODE_ID NUMBER generated as identity primary key,SYSTEM_ID NUMBER(20) not null,IS_ENAB…

PostgreSQL安装错误:Problem running post-install step

问题描述 安装包&#xff1a;pgpostgresql-14.9-1-windows-x64 postgresql-16.0-1-windows-x64 采取措施 一、 首先安装的是16版本的程序&#xff0c;报错后卸载尝试安装14版本软件&#xff0c;依旧报错。 二、 网上搜索&#xff0c;发现该博客&#xff1a; PostgreSQL安…

嵌入式Linux裸机开发(五)中断管理

系列文章目录 文章目录 系列文章目录前言STM32 中断系统IMX6U中断控制8个中断GIC中断控制器GIC介绍中断IDGIC逻辑分块GIC协处理器 中断使能中断优先级 重点代码分析官方SDK函数start.S文件自行编写中断驱动文件 前言 最近在学习中发现&#xff0c;学Linux嵌入式不仅是对Linux的…

【leetcode】 vscode leetcode [ERROR] invalid password? 问题解决

目录 问题解决 问题 使用vscode连接leetcode出现下列问题&#xff1a; vscode leetcode [ERROR] invalid password?出现invalid password?的问题&#xff0c;首先需要检查账号密码是否出错&#xff0c;leetcode的账号可以是手机或邮箱&#xff0c;然后密码去check一下&…

如何做好sop流程图?sop流程图用什么软件做?

5.如何做好sop流程图&#xff1f;sop流程图用什么软件做&#xff1f; 建立标准作业程序sop已经成为企业进步和发展的必经之路&#xff0c;不过&#xff0c;很多刚刚开始着手搭建sop的企业并不知道要如何操作&#xff0c;对于如何做sop流程图、用什么软件做sop流程图等问题充满…

【gitlab】从其他仓库创建项目

需求描述 解决方法 以renren-fast脚手架为例 第一步 第二步 第三步 第四步 参考文章

Redis的五种常用数据类型

1.字符串 String的数据结构是简单的Key-Value模型&#xff0c;Value可以是字符串&#xff0c;也可以是数字。 String是Redis最基本的类型&#xff0c;是二进制安全的&#xff0c;意味着Redis的string可以包含任何数据&#xff0c;比如jpg图片。 一个redis中字符串value最大是…

“注释: 爱恨交织的双重标准?解析注释在代码开发中的作用。”

文章目录 每日一句正能量前言观点和故事程序员不写注释的原因是什么如何才能写出漂亮的注释后记 每日一句正能量 水与水之间有距离&#xff0c;但地心下直相牵&#xff0c;人与人之间有距离&#xff0c;但心里时刻挂念&#xff0c;发条短信道声晚安&#xff0c;梦里我们相见。 …

06-进程间通信

学习目标 熟练使用pipe进行父子进程间通信熟练使用pipe进行兄弟进程间通信熟练使用fifo进行无血缘关系的进程间通信使用mmap进行有血缘关系的进程间通信使用mmap进行无血缘关系的进程间通信 2 进程间通信相关概念 2.1 什么是进程间通信 Linux环境下&#xff0c;进程地址空间…

vscode package.json文件开头的{总是提升警告

警告如下 Problems loading reference https://json.schemastore.org/stylelintrc.json: Unable to load schema from https://json.schemastore.org/stylelintrc.json: read ECONNRESET. 解决如下 在设置&#xff08;settings.json&#xff09;里 新增一条属性 "ht…

Mock工具之Moco使用

一、什么是Mock mock英文单词有愚弄、嘲笑、模拟的意思&#xff0c;这里主要是模拟的意思 二、什么是Moco 开源的、基于java开发的一个mock框架支持http、https、socket等协议 三、Mock的特点 只需要简单的配置request、response等即可满足要求 支持在request 中设置headers、…

【附代码】使用Shapely计算多边形外扩与收缩

文章目录 相关文献效果图代码 作者&#xff1a;小猪快跑 基础数学&计算数学&#xff0c;从事优化领域5年&#xff0c;主要研究方向&#xff1a;MIP求解器、整数规划、随机规划、智能优化算法 本文档介绍如何使用 Shapely Python 包 计算多边形外扩与收缩。 如有错误&…

sface人脸相似度检测

sface人脸相似度检测&#xff0c;基于OPENCV&#xff0c;人脸检测采用yunet&#xff0c;人脸识别采用sface&#xff0c;支持PYTHON/C开发&#xff0c;图片来自网络&#xff0c;侵权请联系本人立即删除 yunet人脸检测sface人脸识别&#xff0c;检测两张图片的人脸相似度

Redis-01基本数据结构

1、String 1.1、介绍 String 是最基本的 key-value 结构&#xff0c;key 是唯一标识&#xff0c;value 是具体的值&#xff0c;value其实不仅是字符串&#xff0c; 也可以是数字&#xff08;整数或浮点数&#xff09;&#xff0c;value 最多可以容纳的数据长度是 512M 1.2、…

基于springboot实现在线动漫信息交流分享平台项目【项目源码+论文说明】计算机毕业设计

基于springboot实现在线动漫信息交流分享平台演示 摘要 随着社会互联网技术的快速发展&#xff0c;每个行业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于在线动漫信息平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#x…

基于拉丁超立方法的风光场景生成与削减

代码链接&#xff1a;基于拉丁超立方法的风光场景生成与削减 摘要&#xff1a;与蒙特卡洛法不同&#xff0c;拉丁超立方采样改进了采样策略能够做到较小采样规模中获得较高的采样精度&#xff0c;属于分层抽样技术&#xff0c;设定风光出力遵从正态分布normrnd&#xff0c;从而…