当前位置: 首页 > news >正文

μC/OS-Ⅱ源码学习(6)---事件标志组

快速回顾

μC/OS-Ⅱ中的多任务

μC/OS-Ⅱ源码学习(1)---多任务系统的实现

μC/OS-Ⅱ源码学习(2)---多任务系统的实现(下)

μC/OS-Ⅱ源码学习(3)---事件模型

μC/OS-Ⅱ源码学习(4)---信号量

μC/OS-Ⅱ源码学习(5)---消息队列

        本文进一步解析事件模型中,事件标志组类型的函数源码。

简单介绍

        事件标志组(Event flag group)是一种特殊的事件,其事件控制块为:

//ucos_ii.h
typedef struct os_flag_grp {INT8U         OSFlagType;         /* 事件标志组类型 */void         *OSFlagWaitList;     /* 等待链表,里面的每一个节点都记录了相关任务等待事件标志位的信息 */OS_FLAGS      OSFlagFlags;        /* 当前存在的标志 */
#if OS_FLAG_NAME_EN > 0uINT8U        *OSFlagName;
#endif
} OS_FLAG_GRP;

        与其他事件不同,事件标志组不共享OS_EVENT结构,而是有自己特殊的结构OS_FLAG_GRP。其中OS_FLAGS的长度是可以设定的,表示一个事件标志组内最多有多少种标志:

//ucos_ii.h
#if OS_FLAGS_NBITS == 8u
typedef  INT8U    OS_FLAGS;
#endif#if OS_FLAGS_NBITS == 16u
typedef  INT16U   OS_FLAGS;
#endif#if OS_FLAGS_NBITS == 32u
typedef  INT32U   OS_FLAGS;
#endif

        OSFlagWaitList链表用来记录等待该事件标志组的所有任务及其需求,通常通常用一个节点对象OS_FLAG_NODE来保存这些信息:

//ucos_ii.h
typedef struct os_flag_node {void         *OSFlagNodeNext;          /* 等待列表的下一个节点 */void         *OSFlagNodePrev;          /* 等待列表的前一个节点 */void         *OSFlagNodeTCB;           /* 指向等待该事件标志组的任务,和下一个成员相匹配 */void         *OSFlagNodeFlagGrp;       /* 指向等待的事件标志组          */OS_FLAGS      OSFlagNodeFlags;         /* 等待的标志位 */INT8U         OSFlagNodeWaitType;      /* 等待类型,即任意匹配或全部匹配等 */
} OS_FLAG_NODE;

       回忆其它事件(如信号量、消息队列,以下简称普通事件)的等待表,都是使用类似就绪表的方式来快速检索。这是因为普通事件类型在pend时,无需记录其它的等待信息,例如一个任务通过pend函数等待一个普通事件时,该普通事件只需要记录该任务的优先级,当普通事件发生后,直接查表来通知对应的任务即可。

        而事件标志组在pend过程中需要记录的信息就多了一些,比如不同任务执行所需要的标志位可能是不一样的,这就导致简单使用等待表的方式会丢失这些信息。μC/OSⅡ将这些信息记录在一个个节点中,并用遍历链表的方式查找满足条件的任务。

        可以说,不同的等待结构和方式,是事件标志组区分于其它事件的根本原因,也因此,μC/OSⅡ使用了额外的控制块类型OS_FLAG_GRP来实现事件标志组。

事件标志组的创建

        对应函数为OSFlagCreate(OS_FLAGS flagsINT8U *perr),需要传入一个标志作为初始值,返回一个初始化后的事件标志组控制块。

//os_flag.c
OS_FLAG_GRP  *OSFlagCreate (OS_FLAGS  flags,INT8U    *perr)
{OS_FLAG_GRP *pgrp;
#if OS_CRITICAL_METHOD == 3u        /* 初始化临界区变量 */OS_CPU_SR    cpu_sr = 0u;
#endif#ifdef OS_SAFETY_CRITICALif (perr == (INT8U *)0) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endif#ifdef OS_SAFETY_CRITICAL_IEC61508if (OSSafetyCriticalStartFlag == OS_TRUE) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endifif (OSIntNesting > 0u) {         /* 不能在中断中调用 */*perr = OS_ERR_CREATE_ISR;   return ((OS_FLAG_GRP *)0);}OS_ENTER_CRITICAL();pgrp = OSFlagFreeList;        /* 获取一个新的事件标志组控制块 */if (pgrp != (OS_FLAG_GRP *)0) {       /* 查看是否是有效控制块 *//* 如果是一个有效控制块,则将原链表头指向下一个,完成首个控制块的脱离 */OSFlagFreeList       = (OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;pgrp->OSFlagType     = OS_EVENT_TYPE_FLAG;  /* 设置为事件标志组类型 */pgrp->OSFlagFlags    = flags;               /* 设置初始值 */pgrp->OSFlagWaitList = (void *)0;           /* 等待表初始化为0 */
#if OS_FLAG_NAME_EN > 0upgrp->OSFlagName     = (INT8U *)(void *)"?";
#endifOS_EXIT_CRITICAL();*perr                = OS_ERR_NONE;} else {     //没有有效的任务控制块了OS_EXIT_CRITICAL();*perr                = OS_ERR_FLAG_GRP_DEPLETED;}return (pgrp);                                  /* 返回创建的事件标志组控制块 */
}

事件标志组的操作

OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_typeINT32U timeout, INT8U *perr)

        功能描述:等待事件flags的到来,如果已经存在这些事件标志,则直接继续执行,否则阻塞等待。该函数有很多可选功能:

wait_type可以是以下值及其组合:

//os_flag.c
#define  OS_FLAG_WAIT_CLR_ALL           0u  /* 等待所有传入的事件标志的清除(0有效) */
#define  OS_FLAG_WAIT_CLR_AND           0u#define  OS_FLAG_WAIT_CLR_ANY           1u  /* 等待任意传入的事件标志的清除(0有效) */
#define  OS_FLAG_WAIT_CLR_OR            1u#define  OS_FLAG_WAIT_SET_ALL           2u  /* 等待所有传入的事件标志的出现(1有效) */
#define  OS_FLAG_WAIT_SET_AND           2u#define  OS_FLAG_WAIT_SET_ANY           3u  /* 等待任意传入的事件标志的出现(1有效) */
#define  OS_FLAG_WAIT_SET_OR            3u#define  OS_FLAG_CONSUME             0x80u  /* 当pend得到满足后,这些标志会消耗掉 */

timeout等待超时时长,当大于0时,表示等待timeout时长后系统会将该任务就绪。传入等于0时,表示一直等待直到事件标志的出现。

        接着看源码:

//os_flag.c
OS_FLAGS  OSFlagPend (OS_FLAG_GRP  *pgrp,OS_FLAGS      flags,INT8U         wait_type,INT32U        timeout,INT8U        *perr)
{OS_FLAG_NODE  node;OS_FLAGS      flags_rdy;INT8U         result;INT8U         pend_stat;BOOLEAN       consume;
#if OS_CRITICAL_METHOD == 3u       /* 初始化临界区变量 */OS_CPU_SR     cpu_sr = 0u;
#endif#ifdef OS_SAFETY_CRITICALif (perr == (INT8U *)0) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endif#if OS_ARG_CHK_EN > 0uif (pgrp == (OS_FLAG_GRP *)0) {       /* 事件标志组不能为空 */*perr = OS_ERR_FLAG_INVALID_PGRP;return ((OS_FLAGS)0);}
#endifif (OSIntNesting > 0u) {        /* 不能在中断中调用 */*perr = OS_ERR_PEND_ISR;return ((OS_FLAGS)0);}if (OSLockNesting > 0u) {        /* 调度器上锁时无法调用 */*perr = OS_ERR_PEND_LOCKED;return ((OS_FLAGS)0);}if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) {       /* 必须是事件标志组类型 */*perr = OS_ERR_EVENT_TYPE;return ((OS_FLAGS)0);}result = (INT8U)(wait_type & OS_FLAG_CONSUME);if (result != (INT8U)0) {                 /* 是否需要消耗掉这些flags */wait_type &= (INT8U)~(INT8U)OS_FLAG_CONSUME;   //清除消耗(consume)标志consume    = OS_TRUE;     //消耗标志为真} else {consume    = OS_FALSE;}
/*$PAGE*/OS_ENTER_CRITICAL();switch (wait_type) {case OS_FLAG_WAIT_SET_ALL:       /* 需要所有指定标志都出现 */flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);    /* 通过按位与获取我们想要的位 */if (flags_rdy == flags) {         /* 这些位必须值为1(与所需标志相同) */if (consume == OS_TRUE) {pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;   /* 如果需要消耗掉这些位,通过按位与取反对应位即可 */}OSTCBCur->OSTCBFlagsRdy = flags_rdy;     /* 将准备好的标志位传递给任务TCB */OS_EXIT_CRITICAL();*perr                   = OS_ERR_NONE;return (flags_rdy);} else {         /* 若标志位不满足条件,则创建节点并进行任务和标志组的双向绑定 */OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);OS_EXIT_CRITICAL();}break;case OS_FLAG_WAIT_SET_ANY:     //任意指定的标志位出现flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);    /* 通过按位与互殴去我们想要的位 */if (flags_rdy != (OS_FLAGS)0) {      /* 只要有任意位满足,整个flags就不为0 */if (consume == OS_TRUE) {      /* 是否需要消耗掉这些标志 */pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;    /* 按位与取反对应位进行清除 */}OSTCBCur->OSTCBFlagsRdy = flags_rdy;     /* 将已有的标志位传递给任务TCB */OS_EXIT_CRITICAL();*perr                   = OS_ERR_NONE;return (flags_rdy);} else {       /* 若标志位不满足条件,则创建节点并进行任务和标志组的双向绑定 */OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);OS_EXIT_CRITICAL();}break;#if OS_FLAG_WAIT_CLR_EN > 0u        /* 0有效,和上面代码逻辑是一样的 */case OS_FLAG_WAIT_CLR_ALL:flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags;    /* Extract only the bits we want     */if (flags_rdy == flags) {                     /* Must match ALL the bits that we want     */if (consume == OS_TRUE) {                 /* See if we need to consume the flags      */pgrp->OSFlagFlags |= flags_rdy;       /* Set ONLY the flags that we wanted        */}OSTCBCur->OSTCBFlagsRdy = flags_rdy;      /* Save flags that were ready               */OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */*perr                   = OS_ERR_NONE;return (flags_rdy);} else {                                      /* Block task until events occur or timeout */OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);OS_EXIT_CRITICAL();}break;case OS_FLAG_WAIT_CLR_ANY:flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags;   /* Extract only the bits we want      */if (flags_rdy != (OS_FLAGS)0) {               /* See if any flag cleared                  */if (consume == OS_TRUE) {                 /* See if we need to consume the flags      */pgrp->OSFlagFlags |= flags_rdy;       /* Set ONLY the flags that we got           */}OSTCBCur->OSTCBFlagsRdy = flags_rdy;      /* Save flags that were ready               */OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */*perr                   = OS_ERR_NONE;return (flags_rdy);} else {                                      /* Block task until events occur or timeout */OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);OS_EXIT_CRITICAL();}break;
#endifdefault:     //其他情况是非法的,返回空标志(0)OS_EXIT_CRITICAL();flags_rdy = (OS_FLAGS)0;*perr      = OS_ERR_FLAG_WAIT_TYPE;return (flags_rdy);}
/*$PAGE*/OS_Sched();        /* 尝试切换上下文 */OS_ENTER_CRITICAL();     //再次回到这里时,表明任务由于以下原因重新就绪了if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) {      /* 非标志位满足条件导致就绪 */pend_stat                = OSTCBCur->OSTCBStatPend;OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;    //pend恢复OK状态OS_FlagUnlink(&node);      //将上面绑定的节点进行解绑OSTCBCur->OSTCBStat      = OS_STAT_RDY;      /* 使任务状态变为就绪态 */OS_EXIT_CRITICAL();flags_rdy                = (OS_FLAGS)0;     //这种情况下,标志位没有意义,直接清空switch (pend_stat) {case OS_STAT_PEND_ABORT:*perr = OS_ERR_PEND_ABORT;     /* 因使用Abort函数导致的pend结束 */break;case OS_STAT_PEND_TO:default:*perr = OS_ERR_TIMEOUT;      /* 超时导致的pend结束 */break;}return (flags_rdy);}flags_rdy = OSTCBCur->OSTCBFlagsRdy;     //将外部传入的标志位赋值flags_rdy,做其它处理if (consume == OS_TRUE) {         /* 消耗标志位 */switch (wait_type) {case OS_FLAG_WAIT_SET_ALL:case OS_FLAG_WAIT_SET_ANY: pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;     /* 将事件标志组对应位清除 */break;#if OS_FLAG_WAIT_CLR_EN > 0ucase OS_FLAG_WAIT_CLR_ALL:case OS_FLAG_WAIT_CLR_ANY:      pgrp->OSFlagFlags |=  flags_rdy;   /* 0有效,将事件标志组对应位置位 */break;
#endifdefault:    //其他情况为非法,返回空标志OS_EXIT_CRITICAL();*perr = OS_ERR_FLAG_WAIT_TYPE;return ((OS_FLAGS)0);}}OS_EXIT_CRITICAL();*perr = OS_ERR_NONE;     /* 没有错误发生,表明一定有标志位出现 */return (flags_rdy);     //返回获得的标志位
}

        具体看看事件标志组与任务的双向绑定函数OS_FlagBlock()

//os_flag.c
static  void  OS_FlagBlock (OS_FLAG_GRP  *pgrp,OS_FLAG_NODE *pnode,OS_FLAGS      flags,INT8U         wait_type,INT32U        timeout)
{OS_FLAG_NODE  *pnode_next;INT8U          y;OSTCBCur->OSTCBStat      |= OS_STAT_FLAG;      //任务状态:等待标志位OSTCBCur->OSTCBStatPend   = OS_STAT_PEND_OK;OSTCBCur->OSTCBDly        = timeout;       /* 填装等待超时 */
#if OS_TASK_DEL_EN > 0uOSTCBCur->OSTCBFlagNode   = pnode;       /* 任务等待标志位指针指向该事件标志组结点结构 */
#endif/* 标志组节点(pnode)是一个全新的空白节点,开始填装,并将其加到OSFlagWaitList链表首,原来的链表接在新节点后 */pnode->OSFlagNodeFlags    = flags;        /* 保存我们需要等待的标志位 */pnode->OSFlagNodeWaitType = wait_type;       /* 保存等待类型 */pnode->OSFlagNodeTCB      = (void *)OSTCBCur;     /* 保存当前任务的TCB */pnode->OSFlagNodeNext     = pgrp->OSFlagWaitList; /* 将新节点下一个指向原链表首 */pnode->OSFlagNodePrev     = (void *)0;pnode->OSFlagNodeFlagGrp  = (void *)pgrp;         /* 保存事件标志组 */pnode_next                = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;if (pnode_next != (void *)0) {        //首次插入链表时,链表是空的pnode_next->OSFlagNodePrev = pnode;       /* 非首次插入时,将原链表头上一个指向新创建的节点 */}pgrp->OSFlagWaitList = (void *)pnode;       /* 将链表头指向新节点 */y            =  OSTCBCur->OSTCBY;      /* 从优先级就绪表中将等待标志位的任务清除 */OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;if (OSRdyTbl[y] == 0x00u) {OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}
}

        可以用下图表示该函数做了什么:

①事件标志组(OS_FLAG_GRP)第一次被等待时,创建首个节点(OS_FLAG_NODE),并双向引用。

②该事件标志组(OS_FLAG_GRP)第二次被等待时,将新节点填好后挂载在原链表头,原来的链表元素接续在新节点后,同时双向引用不能少。

        再看解除绑定函数OS_FlagUnlink(),就是将上面的操作反过来,解除这些链接。

//os_flag.c
void  OS_FlagUnlink (OS_FLAG_NODE *pnode)
{
#if OS_TASK_DEL_EN > 0uOS_TCB       *ptcb;
#endifOS_FLAG_GRP  *pgrp;OS_FLAG_NODE *pnode_prev;OS_FLAG_NODE *pnode_next;pnode_prev = (OS_FLAG_NODE *)pnode->OSFlagNodePrev;pnode_next = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;if (pnode_prev == (OS_FLAG_NODE *)0) {         /* 前一个节点为0,表明pnode为链表首 */pgrp                 = (OS_FLAG_GRP *)pnode->OSFlagNodeFlagGrp;pgrp->OSFlagWaitList = (void *)pnode_next;        /* 将该节点脱离链表 */if (pnode_next != (OS_FLAG_NODE *)0) {pnode_next->OSFlagNodePrev = (OS_FLAG_NODE *)0;     /* 下一个节点的上一个指向0 */}} else {     //前一个节点非0pnode_prev->OSFlagNodeNext = pnode_next;     /* 上一节点的下一个,指向下一个节点 */if (pnode_next != (OS_FLAG_NODE *)0) {       /* 下一节点非空 */pnode_next->OSFlagNodePrev = pnode_prev;     /* 下一节点的上一个,指向上一个节点,完成pnode的脱离 */}}
#if OS_TASK_DEL_EN > 0uptcb                = (OS_TCB *)pnode->OSFlagNodeTCB;ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0;
#endif
}

OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *perr)

        功能描述:为事件标志组设置(或清除)一些标志位。支持一些选项opt:

//ucos_ii.h
#define  OS_FLAG_CLR     0u   //清除这些标志位
#define  OS_FLAG_SET     1u   //置位这些标志位

        接下来看源码:

//os_flag.c
OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *pgrp,OS_FLAGS      flags,INT8U         opt,INT8U        *perr)
{OS_FLAG_NODE *pnode;BOOLEAN       sched;OS_FLAGS      flags_cur;OS_FLAGS      flags_rdy;BOOLEAN       rdy;
#if OS_CRITICAL_METHOD == 3u      /* 初始化临界区变量 */OS_CPU_SR     cpu_sr = 0u;
#endif#ifdef OS_SAFETY_CRITICALif (perr == (INT8U *)0) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endif#if OS_ARG_CHK_EN > 0uif (pgrp == (OS_FLAG_GRP *)0) {      /* 事件标志组不能为空 */*perr = OS_ERR_FLAG_INVALID_PGRP;return ((OS_FLAGS)0);}
#endifif (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) {     /* 事件类型必须为“事件标志组” */*perr = OS_ERR_EVENT_TYPE;return ((OS_FLAGS)0);}OS_ENTER_CRITICAL();switch (opt) {case OS_FLAG_CLR:pgrp->OSFlagFlags &= (OS_FLAGS)~flags;     /* 清除对应的标志位 */break;case OS_FLAG_SET:pgrp->OSFlagFlags |=  flags;     /* 置位对应标志位 */break;default:OS_EXIT_CRITICAL();         /* 非法选项,标记错误,返回空标志 */*perr = OS_ERR_FLAG_INVALID_OPT;return ((OS_FLAGS)0);}sched = OS_FALSE;        /* 初始化切换上下文标志,默认不切换 */pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;      //获取事件标志组的等待链表while (pnode != (OS_FLAG_NODE *)0) {       /* 遍历所有的等待节点 */switch (pnode->OSFlagNodeWaitType) {      //判断等待类型case OS_FLAG_WAIT_SET_ALL:       /* 所有标志位满足 */flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags);   //将任务等待的标志位和当前已有的标志位进行按位与操作if (flags_rdy == pnode->OSFlagNodeFlags) {rdy = OS_FlagTaskRdy(pnode, flags_rdy);   /* 如果已有标志位能满足任务等待的需求,则尝试就绪该任务 */if (rdy == OS_TRUE) {   //是否成功就绪(在优先级就绪表置位)sched = OS_TRUE;       /* 将切换标志置位 */}}break;case OS_FLAG_WAIT_SET_ANY:      /* 任意标志位满足 */flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags);if (flags_rdy != (OS_FLAGS)0) {     //只有不为0,说明存在标志位满足条件rdy = OS_FlagTaskRdy(pnode, flags_rdy);   /* 尝试就绪等待链表中的任务 */if (rdy == OS_TRUE) {sched = OS_TRUE;}}break;#if OS_FLAG_WAIT_CLR_EN > 0u     /* 0有效,其它和上面代码逻辑相同 */case OS_FLAG_WAIT_CLR_ALL:flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;if (flags_rdy == pnode->OSFlagNodeFlags) {rdy = OS_FlagTaskRdy(pnode, flags_rdy);if (rdy == OS_TRUE) {sched = OS_TRUE;}}break;case OS_FLAG_WAIT_CLR_ANY:flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;if (flags_rdy != (OS_FLAGS)0) {rdy = OS_FlagTaskRdy(pnode, flags_rdy);if (rdy == OS_TRUE) {sched = OS_TRUE;}}break;
#endifdefault:     //非法选项,标记错误,并返回空标志OS_EXIT_CRITICAL();*perr = OS_ERR_FLAG_WAIT_TYPE;return ((OS_FLAGS)0);}pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;    /* 节点指向链表中的下一个 */}OS_EXIT_CRITICAL();if (sched == OS_TRUE) {    //若切换标志位为真,则尝试上下文切换OS_Sched();}OS_ENTER_CRITICAL();flags_cur = pgrp->OSFlagFlags;OS_EXIT_CRITICAL();*perr     = OS_ERR_NONE;return (flags_cur);    //返回事件标志组当前的标志
}

        其中函数OS_FlagTaskRdy(pnode, flags_rdy)用于就绪节点指向的任务,并将该节点从事件标志组控制块的等待链表中脱离:

//os_flag.c
static  BOOLEAN  OS_FlagTaskRdy (OS_FLAG_NODE *pnode,OS_FLAGS      flags_rdy)
{OS_TCB   *ptcb;BOOLEAN   sched;ptcb                 = (OS_TCB *)pnode->OSFlagNodeTCB;    /* 取出等待事件的任务TCB */ptcb->OSTCBDly       = 0u;     //等待超时计数器设置为0(不计数)ptcb->OSTCBFlagsRdy  = flags_rdy;    //将准备好的标志位填装到TCB中ptcb->OSTCBStat     &= (INT8U)~(INT8U)OS_STAT_FLAG;    //清除等待标志位状态ptcb->OSTCBStatPend  = OS_STAT_PEND_OK;     //pend状态设置为OKif (ptcb->OSTCBStat == OS_STAT_RDY) {       /* 任务是否就绪 */OSRdyGrp               |= ptcb->OSTCBBitY;       /* 将优先级就绪表中对应优先级置位 */OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;sched                   = OS_TRUE;} else {sched                   = OS_FALSE;}OS_FlagUnlink(pnode);     //释放该节点,从标志组的等待链表中脱离return (sched);    //返回是否需要切换任务
} 

事件标志组的删除

        函数原型:OS_FLAG_GRP OSFlagDel(OS_FLAG_GRP *pgrp, INT8U opt, INT8U *perr)

        和其它事件的删除函数一样,作用是清空控制块,并就绪正在等待的所有任务,最后将控制块归还给空白控制块链表。

相关文章:

μC/OS-Ⅱ源码学习(6)---事件标志组

快速回顾 μC/OS-Ⅱ中的多任务 μC/OS-Ⅱ源码学习(1)---多任务系统的实现 μC/OS-Ⅱ源码学习(2)---多任务系统的实现(下) μC/OS-Ⅱ源码学习(3)---事件模型 μC/OS-Ⅱ源码学习(4)---信号量 μC/OS-Ⅱ源码学习(5)---消息队列 本文进一步解析事件模型中,事件标志…...

ASP.NET|日常开发中读写TXT文本详解

ASP.NET|日常开发中读写TXT文本详解 前言一、读取 TXT 文本1.1 使用StreamReader类 二、写入 TXT 文本2.1 使用StreamWriter类 三、文件编码问题3.1 常见编码格式 四、错误处理和性能考虑4.1 错误处理4.2 性能考虑 结束语优质源码分享 ASP.NET|日常开发中…...

《C 语言向量运算:点亮人工智能几何计算之路》

在人工智能蓬勃发展的时代,数学运算作为其坚实的基石发挥着不可替代的作用。而向量的点积与叉积运算,更是在人工智能的几何计算领域有着独特且关键的地位。今天,就让我们一同深入探讨如何在 C 语言中实现向量的点积、叉积运算,并领…...

HarmonyOS 获取进程相关的信息process 常用的几个方法

获取进程相关的信息,提供进程管理的相关功能。 process 1. EventListener 2. isIsolatedProcess 3. is64Bit 4. getStartRealtime 5. getPastCpuTime 导入模块 import { process } from kit.ArkTS; 属性 名称类型可读可写说明uidnumber是否进程的用户标识。…...

Linux 权限管理实践:精确控制用户对 systemctl 和 journalctl 命令的使用

前言 在 Linux 系统管理中,精确控制用户对特定命令的访问权限是一项关键的安全实践。使用 systemctl 和 journalctl 命令时,不当的权限设置可能会导致不必要的风险。本篇博客将详细讨论如何通过 sudoers 文件和 Polkit 策略为不同用户配置 systemctl 和…...

图像处理之滤波

中值滤波、均值滤波、高斯滤波和双边滤波是常见的图像处理技术,主要用于去噪和图像平滑。低通滤波和高通滤波用于处理图像中的频率成分。它们的主要区别在于它们所允许通过的频率范围。滤波、卷积、去噪、模糊、提取特征是一个意思。 卷积就是两个矩阵的乘法&#…...

html基础-认识html

1.什么是html html是浏览器可以识别的的标记语言&#xff0c;我们在浏览器浏览的网页就是一个个的html文档 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>认识html</title> </head> <body><h1…...

金智塔科技联合浙大人工智能研究所发布全新“智信”可信行业数据空间,共促数字金融创新发展!

由中国计算机学会(CCF)主办&#xff0c;CCF数字金融分会、同济大学、上海立信会计金融学院联合承办&#xff0c;金智塔科技作为金牌合作单位的数字金融领域年度巅峰盛会——首届CCF中国数字金融大会于2024年12月7日在上海成功举办。中国工程院院士蒋昌俊任大会主席&#xff0c;…...

基于单片机的语音识别自动避障小车(论文+源码)

1.系统设计 此次基于单片机的语音识别自动避障小车&#xff0c;以STC89C52单片机作为系统的主控制器&#xff0c;利用超声波模块来实现小车与障碍物距离的测量并通过LCD液晶显示&#xff0c;当距离低于阈值时会通过WT588语音模块进行报警提示&#xff0c;并且小车会后退来躲避…...

使用layui的table提示Could not parse as expression(踩坑记录)

踩坑记录 报错图如下 原因&#xff1a; 原来代码是下图这样 上下俩中括号都是连在一起的&#xff0c;可能导致解析问题 改成如下图这样 重新启动项目&#xff0c;运行正常&#xff01;...

EF Code 多对多表关系建设和Linq 知识点

自引用组织结构树&#xff0c;比如部门、组织 除了根节点&#xff0c;其他节点都有一个父节点&#xff0c;也包含多个子节点&#xff0c;那么在定义表结构时&#xff0c;既要申明父表的关系&#xff0c;也要申明子表的关系 EF Code 多对多 builder.ToTable("T_Student&…...

Maven 的下载

目录 1、Maven 官方地址2、下载3、解压4、配置本地仓库 1、Maven 官方地址 https://maven.apache.org/ 2、下载 3、解压 将下载的压缩包解压到任意位置 4、配置本地仓库 在 Maven 的安装目录下新建文件夹&#xff0c;用来当作 Maven 的本地仓库 进入 conf 目录下&#xff…...

VPN模式

拓扑结构 实验图&#xff1a; 路由器router 配置 DHCP配置 需要右键激活 路由器项配置网关 dns项配置ip DNS服务配置 正向区域 选择不允许动态更新 反向区域 创建主机 正向 验证是否创建成功 反向查找区域 输入网段 使用默认名称---不允许动态更新 KALI机的验证 web服务…...

LeetCode 热题 100-两数之和(简单)

1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值 target 的那两个整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。…...

【C语言】拆解C语言的编译过程

前言 学习C语言的过程中&#xff0c;涉及到各种各样的关键词&#xff0c;在我们点击编译的时候&#xff0c;都会做什么呢&#xff1f;让我们来拆解一下 C语言的编译过程 C语言的编译过程包括预处理、编译、汇编和链接四个主要步骤。每个步骤都有其特定的任务和输出文件类型&am…...

RabbitMQ中的Work Queues模式

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;是实现异步通信和解耦系统的关键组件之一。RabbitMQ 是一个广泛使用的开源消息代理软件&#xff0c;支持多种消息传递模式。其中&#xff0c;Work Queues&#xff08;工作队列&#xff09;模式是一…...

OpenCV圆形标定板检测算法findGrid原理详解

OpenCV的findGrid函数检测圆形标定板的流程如下: class CirclesGridClusterFinder {CirclesGridClusterFinder(const CirclesGridClusterFinder&); public:CirclesGridClusterFinder...

快速理解类的加载过程

当程序主动使用某个类时&#xff0c;如果该类还未加载到内存中&#xff0c;则系统会通过如下三个步骤来对该类进行初始化&#xff1a; 1.加载&#xff1a;将class文件字节码内容加载到内存中&#xff0c;并将这些静态数据转换成方法区的运行时数据结构&#xff0c;然后生成一个…...

monorepo代码管理框架

1. 新建 vue3-component 文件夹 2. 运行pnpm init 3. pnpm i vue typescript 4. 新建.npmrc shamefully-hoisttrue link-workspace-packagestrue 5. ts文件配置 pnpm tsc --init 默认.bin路径下的tsc 6. 新建pnpm-workspace.yaml packages:- packages/** # all packages- p…...

LabVIEW实现蓝牙通信

目录 1、蓝牙通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...

R环境配置 以及Debug方法 (VSCode, conda, 远程R)

生物信息学中的R环境配置 以及Debug方法 开始设置1、建议使用VSCode conda 远程R2、 VSCode配置安装插件安装好插件后&#xff0c;远程设置链接成功后&#xff0c;设置项目 3、 linux conda 和 远程R配置4、VScode 远程访问R环境下面配置远程R 5、开始Debug新建个R文件&#…...

ComfyUI 与 Stable Diffusion WebUI 的优缺点比较

ComfyUI与Stable Diffusion WebUI都是AI绘画领域比较知名两款产品&#xff0c;两者存在诸多差异&#xff0c;本篇就带你熟悉二者的优劣&#xff0c;方便自己做出决策。 界面与操作 ComfyUI&#xff1a;界面简洁直观&#xff0c;通过节点和连线的方式构建工作流&#xff0c;用…...

Ubuntu 系统下安装 Nginx

一、Nginx是什么 是一个高性能的 HTTP 和反向代理 web 服务器&#xff0c;同时也提供了 IMAP/POP3/SMTP 服务。 是一款轻量级的 Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#xff0c;在BSD-like 协议下发行。其特点是占有内存少&…...

【Qt】drawText字体大小问题探究

背景 软件的一个功能是&#xff1a; 打开图片在图片上绘制序号&#xff0c;序号的样式是圆圈内包含数字将带有序号的图片打印出来 实现思路也很简单&#xff0c;在屏幕上显示时重写paintEvent函数&#xff0c;利用QPainter完成图片和序号的绘制。打印时只需要将QPainter对应…...

视频汇聚平台:Liveweb视频流媒体平台视频监控系统解决方案

数字化技术在安防领域的广泛应用已经成为公安等重要执法部门的重要趋势&#xff0c;主要得益于无线网络通信技术和计算机技术的快速进步。传统的视频监控系统存在诸多局限&#xff0c;例如只能进行现场监视&#xff0c;报警信息传输简单&#xff0c;无法远距离传输视频信号&…...

Android开发中有关MediaPlayer 播放.mp3文件使用之一

我们在项目中&#xff0c;经常会添加一个简单的语音提示&#xff1a;我们通常会选择MediaPlayer播放SD文件中的.MP3文件或者存到assets下的.mp3文件。正常使用流程如下&#xff1a; 一、播放assets下的.mp3文件 根据assets获取需要播放的文件名 getApplicationContext().getAs…...

Leetcode经典题11--加油站

题目描述 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 gas 和…...

23种设计模式之状态模式

目录 1. 简介2. 代码2.1 State &#xff08;定义抽象状态接口&#xff09;2.2 StartState &#xff08;实现具体状态类&#xff09;2.3 EndState &#xff08;实现具体状态类&#xff09;2.4 Context &#xff08;定义上下文类&#xff09;2.5 Test &#xff08;测试类&#xf…...

大模型的构建与部署(3)——数据标注

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl1. 数据标注的重要性 1.1 增强数据可解释性 数据标注通过为原始数据添加标签或注释,显著增强了数据的可解释性。在机器学习和深度学习领域,模型的训练依赖于大量带标签的数据。这些标签不仅帮助…...

windows11 专业版 docker desktop 安装指南

家庭中文版需升级专业版&#xff0c;家庭版没有hyper-v。 开始运行optionalfeatures.exe打开windows功能 安装wsl2 步骤 1 - 启用适用于 Linux 的 Windows 子系统步骤 2 - 检查运行 WSL 2 的要求步骤 3 - 启用虚拟机功能步骤 4 - 下载 Linux 内核更新包 步骤 1 - 启用适用于 L…...

mixed strategy

混合策略和期望收益的基本概念 在博弈论中&#xff0c;混合策略是指参与者以一定的概率选择不同的纯策略。期望收益则是在考虑这些概率的情况下&#xff0c;参与者所能获得的平均收益。 以“石头 - 剪刀 - 布”游戏为例 游戏规则回顾 石头胜剪刀&#xff0c;剪刀胜布&#xff0…...

登上Nature和CVPR!小波变换+UNet上大分!

最近UNet又出现了不少新成果&#xff0c;结合小波变换屡登Nature子刊和CVPR24&#xff01;比如三路径U-Net模型&#xff0c;利用Haar小波变换大幅提高系统整体性能&#xff1b;再比如利用小波变换的特性来改进U-Net架构的MLWNet网络&#xff0c;性能猛超SOTA&#xff01; 原因…...

2_使用 HTML5 Canvas API (1) --[HTML5 API 学习之旅]

1.在页面中加入 canvas 在网页中加入 <canvas> 元素可以通过简单的 HTML 和 JavaScript 实现。以下是两个具体的示例&#xff0c;展示了如何在页面中使用 <canvas> 绘制图形和处理用户交互。 示例 1: 简单的静态绘图 这个例子展示了一个基础的 <canvas> 应…...

梳理你的思路(从OOP到架构设计)_UML应用:业务内涵的分析抽象表达01

目录 1、 系统分析(System Analysis) 系統分析的涵意 业务(领域)知识 业务内涵 业务(领域)概念 2、举例(一) &#xff1a;东方传说 UML与建模工具 1、 系统分析(System Analysis) 系統分析的涵意 许多人在学习系统分析(System Analysis)时&#xff0c;常迷失于其字面上…...

redis集群安装部署 redis三主三从集群

redis集群安装部署 redis三主三从集群 1、下载redis2、安装redis集群 三主三从3、配置redis开机自启动3.1、建立启动脚本3.2、复制多份redis启动脚本给集群使用3.3、添加可执行权限3.4、配置开机自启动 1、下载redis 本次redis安装部署选择当前最新的稳定版本7.4.1 下载链接: …...

【PHP】部署和发布PHP网站到IIS服务器

欢迎来到《小5讲堂》 这是《PHP》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言安装PHP稳定版本线程安全版解压使用 PHP配置配置文件扩展文件路径…...

大模型qiming面试内容整理-系统设计与架构

在大模型和机器学习相关岗位的面试中,系统设计与架构的考察通常会涉及如何设计一个可扩展、可靠且高效的机器学习系统,特别是在面对大规模数据和复杂模型时。这一部分的考察不仅测试候选人对机器学习和深度学习的理解,还会评估其如何设计实际生产环境中的系统来满足需求。以…...

【Reading Notes】Favorite Articles from 2024

文章目录 1、January2、February3、March4、April5、May6、June7、July8、August9、September10、October11、November12、December 1、January 2、February 3、March Sora外部测试翻车了&#xff01;3个视频都有Bug&#xff08; 2024年03月01日&#xff09; 不仔细看还真看不…...

Qt-chart 画柱状图

记录下&#xff0c;记录下 效果图 直接上代码 // 创建柱状系列 QBarSeries *series new QBarSeries();// 创建数据集 QBarSet *setTar new QBarSet(("tar"));QBarSet *setReality new QBarSet(("reality"));//添加柱状数据*setTar << 1<<…...

【深入理解Java线程池】

深入理解Java线程池 Java线程池是Java并发编程中的一个重要概念&#xff0c;它提供了一种管理和复用线程的机制&#xff0c;可以显著减少创建和销毁线程的开销&#xff0c;提高系统的响应速度和吞吐量。以下是对Java线程池的详细解析&#xff1a; 一、线程池的基本概念 线程…...

honle电源控制器维修UV灯高压电源EVG EPS200

UV电源控制器维修&#xff1b;honle电源维修&#xff1b;UV电源维修MUC-Steuermodul 2 LΛmpen D-82166 主要维修型号&#xff1a; EPS 60/120、EPS 100、EPS200、EPS 220、EPS 340、LED Spot 100、UV2000F HONLE UV灯高压电源控制器故障包括&#xff1a; 1、电压不稳&#…...

java中List集合小练习

题目&#xff1a;将1~100之间所有正整数存放在一个List集合中&#xff0c;并将集合索引位置时10的对象从集合中移除。 代码&#xff1a; import java.util.ArrayList; import java.util.List;public class ListTest {public ListTest(){List<Integer> listnew ArrayLis…...

【STM32练习】基于STM32的PM2.5环境监测系统

一.项目背景 最近为了完成老师交付的任务&#xff0c;遂重制了一下小项目用STM32做一个小型的环境监测系统。 项目整体示意框图如下&#xff1a; 二.器件选择 单片机&#xff08;STM32F103&#xff09;数字温湿度模块&#xff08;DHT11&#xff09;液晶显示模块&#xff08;0.8…...

JS哪些操作会造成内存泄露?

在 JavaScript 中&#xff0c;内存泄露是指程序不再使用的内存没有被释放&#xff0c;从而导致内存的持续增长&#xff0c;最终可能导致性能下降或应用崩溃。以下是一些常见的可能导致内存泄露的操作和情况&#xff1a; 1. 全局变量 如果不小心创建了全局变量&#xff0c;可能…...

《知识拓展 · 统一建模语言UML》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…...

GNSS 全球卫星导航系统(Global Navigation Satellite System): 卫星授时

文章目录 引言I GNSSGNSS三大核心能力卫星系统、区域性的系统以及增强系统II 卫星授时GNSS是如何实现授时优势引言 GNSS指的是全球卫星导航系统,是能在全球范围内提供导航服务的卫星导航系统的通称。 美国GPS、俄罗斯GLONASS、中国北斗卫星导航系统和欧洲GALILEO,是联合国全…...

【数据结构】循环队列原理与代码

理论 存在的意义&#xff1a; 将顺序队列从逻辑上视为一个环。解决“假溢出”&#xff08;出队入队连续操作后两个指针均到数组末端maxsize-1处&#xff0c;虽然队里没有元素但无法让元素进队&#xff09;。两种定义 1.题目是队列非空时队头指针和队尾指针分别指向队头元素和队…...

Kettle Doris Plugin编译过程

起因 公司的数据中台产品用的doris作为数据仓库&#xff0c;用kettle作为etl引擎&#xff0c;但是表输出组件太慢了。偶然有一天发现doris提供了kettle组件&#xff0c;但是需要自己编译&#xff0c;于是就开始自己编译。 坑 因为kettle的依赖包在m2&#xff08;中央仓库&am…...

Day10 苍穹外卖项目 订单搜索、各个状态的订单统计、查询订单详细、接单、拒单、取消订单、派送订单、完成订单

目录 1.订单搜索 1.1 需求分析和设计 1.2 接口设计 1.2 代码实现 1.2.1 admin/OrderController 1.2.2 OrderService 1.2.3 OrderServiceImpl 2.各个状态的订单数量统计 2.1 需求分析和设计 2.2 接口设计 2.3 代码实现 2.3.1 admin/OrderController 2.3.2 OrderService 2.3.3 Or…...

你知道OSI参考模型是什么吗?

你知道OSI参考模型是什么吗&#xff1f; 一. OSI模型图解二. OSI七层模型三. OSI模型的特点四. OSI模型与TCP/IP模型的对比五. 总结 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱…...