快速回顾
μ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 flags, INT8U *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_type, INT32U 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)
和其它事件的删除函数一样,作用是清空控制块,并就绪正在等待的所有任务,最后将控制块归还给空白控制块链表。