浅析μCOS/II v2.85内核OSFlagPend()和OSFlagPost()函数工作原理
文章来源:http://gliethttp.cublog.cn[转载请声明出处]
//对于flag--"事件组"的使用,可以用一个简单的例子做说明: // 比如,我现在用迅雷下载一部10集的连续剧,我打算10集全部下载完成之后, //才开始正式看,现在3~10集因为种子原因,先早下完了,现在第1集下到了82%, //第2集下到了97%,因为我的计划是10集全部下完才开始看,而第1集和第2集 //由于网络,种子等等各种原因,迟迟不能下载完成,进而导致我的计划被悬停,不能进行, //已下载的8集,也因为前2集没能下完,而白白等待---这就等同于flag事件组, //1~10集,每一集都是一个事件,因为我内定,10个事件全部完成之后,才进入下一事件--"观看" //所以及早完成自己事件的第3~10集,将主动把自己通过flag事件组函数OSFlagPost()登记到事件组上, //他们不关心,其他友邻事件完成否,只专注自己的事件是否完成,自己的事件一旦完成 //就登记到事件组上,最后3~10集,都把自己登记上去了,只剩下第1集和第2集, //一旦某天的某个时刻,第2集下完了,那么第2集也把自己登记到事件组上,这样整个事件距离完成 //还剩下一个事件,就是第1集是否下载完成,只要第1集下载完成,那么我内定的"观看"计划 //开始启动,过了3分钟,由于网速提高,竟以300k的速度开始下载第1集,1分钟之后, //第1集也下载完成了,第1集立即调用OSFlagPost事件组函数,将自己登记到事件组上, //ok,OSFlagPost()检测到所有事件已经完成,OSFlagPost()将是"我"自动进入下一事件---"观看" // 还有一点就是关于flag事件组和Sem,Mbox,Queue的区别之处,flag事件组不使用事件控制矩阵来 //管理被阻塞在事件上的task进程,flag事件组使用pgrp的双向链表来挂接起所有task, //在OSFlagPost()中将遍历这个链表,查找符合当前flag事件的task,将该task从双向链表中摘下 //然后放入就绪控制矩阵中,之所以这样,是因为flag事件组不像Sem,Mbox,Queue那样具有二值性, //即Sem,Mbox,Queue,要么有,要么没有,flag事件组,还要进一步判断,有的话,是什么程度的有. //---------------------------------------------------------------------- //1.OSFlagPend()函数 OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *perr) { OS_FLAG_NODE node; OS_FLAGS flags_rdy; INT8U result; INT8U pend_stat; BOOLEAN consume; #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr = 0; #endif
#if OS_ARG_CHK_EN > 0 if (perr == (INT8U *)0) { return ((OS_FLAGS)0); } if (pgrp == (OS_FLAG_GRP *)0) { *perr = OS_ERR_FLAG_INVALID_PGRP; return ((OS_FLAGS)0); } #endif if (OSIntNesting > 0) { //ISR中,不能使用OSFlagPend() *perr = OS_ERR_PEND_ISR; return ((OS_FLAGS)0); } if (OSLockNesting > 0) { //μCOS/II v2.85内核已经被强制锁住 *perr = OS_ERR_PEND_LOCKED; return ((OS_FLAGS)0); } if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { //确保该event控制块是flag类型 *perr = OS_ERR_EVENT_TYPE; return ((OS_FLAGS)0); } result = (INT8U)(wait_type & OS_FLAG_CONSUME); if (result != (INT8U)0) { //收到指定事件们之后,复位flag事件组,将相应的事件标志清0 wait_type &= ~(INT8U)OS_FLAG_CONSUME; consume = OS_TRUE; } else { consume = OS_FALSE; } OS_ENTER_CRITICAL(); switch (wait_type) { case OS_FLAG_WAIT_SET_ALL: //2007-09-09 gliethttp //flag事件组中所有事件都置位才唤醒 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags); if (flags_rdy == flags) { //flag事件组中指定的所有事件都已经登记了 if (consume == OS_TRUE) { //清除flag事件组中的相应事件标志位 pgrp->OSFlagFlags &= ~flags_rdy; } OSTCBCur->OSTCBFlagsRdy = flags_rdy;//返回成功的flag事件组值 OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_rdy); } else { //flag事件组中指定的所有事件中,可能有1个还没有完成登记工作,所以本task悬停在flag事件控制矩阵中 //2007-09-09 gliethttp //node为该task在栈空间上分配的数据,因为本task任务需要悬停,所以 //位于该task栈空间上的node,不会被破坏,它和全局变量性质等同 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout); OS_EXIT_CRITICAL(); } break; case OS_FLAG_WAIT_SET_ANY: //2007-09-09 gliethttp //flag事件组中指定的事件们,只要有一个事件发生置位就唤醒 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags); if (flags_rdy != (OS_FLAGS)0) { if (consume == OS_TRUE) { //清除flag事件组中的相应事件标志位 pgrp->OSFlagFlags &= ~flags_rdy; } OSTCBCur->OSTCBFlagsRdy = flags_rdy;//返回成功的flag事件组值 OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_rdy); } else { //flag事件组中指定的所有事件中,没有1个进行登记,所以悬停本task在flag事件控制矩阵中 //2007-09-09 gliethttp //node为该task在栈空间上分配的数据,因为本task任务需要悬停,所以 //位于该task栈空间上的node,不会被破坏,它和全局变量性质等同 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout); OS_EXIT_CRITICAL(); } break; #if OS_FLAG_WAIT_CLR_EN > 0 case OS_FLAG_WAIT_CLR_ALL: //2007-09-09 gliethttp //flag事件组中所有事件都清0才唤醒 flags_rdy = (OS_FLAGS)(~pgrp->OSFlagFlags & flags); if (flags_rdy == flags) { //flag事件组中指定的所有事件都已经把事件自己对应的位清0 if (consume == OS_TRUE) { //还原flag事件组中的相应事件标志位 pgrp->OSFlagFlags |= flags_rdy; } OSTCBCur->OSTCBFlagsRdy = flags_rdy;//返回成功的flag事件组值 OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_rdy); } else { //flag事件组中指定的所有事件中,可能有1个还没有完成清0工作,所以本task悬停在flag事件控制矩阵中 //2007-09-09 gliethttp //node为该task在栈空间上分配的数据,因为本task任务需要悬停,所以 //位于该task栈空间上的node,不会被破坏,它和全局变量性质等同 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout); OS_EXIT_CRITICAL(); } break; case OS_FLAG_WAIT_CLR_ANY: //2007-09-09 gliethttp //flag事件组中指定的事件们,只要有一个事件发生清0就唤醒 flags_rdy = (OS_FLAGS)(~pgrp->OSFlagFlags & flags); if (flags_rdy != (OS_FLAGS)0) { if (consume == OS_TRUE) { //还原flag事件组中的相应事件标志位 pgrp->OSFlagFlags |= flags_rdy; } OSTCBCur->OSTCBFlagsRdy = flags_rdy;//返回成功的flag事件组值 OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_rdy); } else { //flag事件组中指定的所有事件中,没有1个发生清0操作,所以悬停本task在flag事件控制矩阵中 //2007-09-09 gliethttp //node为该task在栈空间上分配的数据,因为本task任务需要悬停,所以 //位于该task栈空间上的node,不会被破坏,它和全局变量性质等同 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout); OS_EXIT_CRITICAL(); } break; #endif default: OS_EXIT_CRITICAL(); flags_rdy = (OS_FLAGS)0; *perr = OS_ERR_FLAG_WAIT_TYPE; return (flags_rdy); } //因为本task正在运行,所以本task现在的优先级最高,现在本task已经将自己从就绪控制矩阵--调度器(x,y)矩形阵列中 //把自己摘掉,所以调度函数OS_Sched()一定会切换到另一个task中执行新task的代码[gliethttp] OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》 //2007-09-09 gliethttp //可能因为OSFlagPend()中指定的timeout已经超时 //[由OSTimeTick()函数把本task重新置入了就绪控制矩阵,具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》], //又或者确实在应用程序的调用了OSFlagPost(),最终使得flag事件组条件满足, //以下代码将具体解析是有什么引起的:1.超时,2.收到正常信号 OS_ENTER_CRITICAL(); if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) { //是因为timeout超时,使得本task获得重新执行的机会 pend_stat = OSTCBCur->OSTCBStatPend; OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; //OS_FlagUnlink()把分配在本task栈空间上的局部变量node,从pgrp事件组双向链表上摘下来. OS_FlagUnlink(&node); OSTCBCur->OSTCBStat = OS_STAT_RDY;//本task正在运行,不悬停在任何事件控制矩阵上 OS_EXIT_CRITICAL(); flags_rdy = (OS_FLAGS)0; switch (pend_stat) { case OS_STAT_PEND_TO: default: *perr = OS_ERR_TIMEOUT;//因为超时,本task才被调度 break; case OS_STAT_PEND_ABORT: *perr = OS_ERR_PEND_ABORT;//人为取消 break; } return (flags_rdy); } //由于每个事件都调用OSFlagPost()登记了事件自己,所以条件满足,本task被正常唤醒 //已经将本task在pgrp事件组双向链表上摘下来,并且把本task放入了就绪控制矩阵中, //否则本task也不会执行至此.[gliethttp] flags_rdy = OSTCBCur->OSTCBFlagsRdy; if (consume == OS_TRUE) { switch (wait_type) { case OS_FLAG_WAIT_SET_ALL: case OS_FLAG_WAIT_SET_ANY: //清除flag事件组中的相应事件标志位 pgrp->OSFlagFlags &= ~flags_rdy; break; #if OS_FLAG_WAIT_CLR_EN > 0 case OS_FLAG_WAIT_CLR_ALL: case OS_FLAG_WAIT_CLR_ANY: //还原flag事件组中的相应事件标志位 pgrp->OSFlagFlags |= flags_rdy; break; #endif default: OS_EXIT_CRITICAL(); *perr = OS_ERR_FLAG_WAIT_TYPE; return ((OS_FLAGS)0); } } OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_rdy); } //---------------------------------------------------------------------- //2.OS_FlagBlock()函数 static void OS_FlagBlock(OS_FLAG_GRP *pgrp, OS_FLAG_NODE *pnode, OS_FLAGS flags, INT8U wait_type, INT16U timeout) { OS_FLAG_NODE *pnode_next; INT8U y; //pnode指向本task在自己栈空间上分配的一个局部变量 //一个node描述一个task OSTCBCur->OSTCBStat |= OS_STAT_FLAG;//是Flag事件让本task进入悬停等待的 OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;//假定不是超时,为正常收到信号 //超时,如果timeout=0,那么,本task将一直悬停,仅仅当收到事件触发信号后才重新进入调度队列 OSTCBCur->OSTCBDly = timeout; #if OS_TASK_DEL_EN > 0 OSTCBCur->OSTCBFlagNode = pnode; #endif //一个node描述一个task pnode->OSFlagNodeFlags = flags;//该task对应的flag事件组值 pnode->OSFlagNodeWaitType = wait_type;//该task对应的等待类型 pnode->OSFlagNodeTCB = (void *)OSTCBCur;//该task的TCB任务上下文指针 pnode->OSFlagNodeNext = pgrp->OSFlagWaitList;//把该node挂到pgrp->OSFlagWaitList头部 pnode->OSFlagNodePrev = (void *)0;//因为是头部,所以没有prev pnode->OSFlagNodeFlagGrp = (void *)pgrp;//该task对应的pgrp管理组 pnode_next = (OS_FLAG_NODE *)pgrp->OSFlagWaitList; if (pnode_next != (void *)0) { //在本task之前,已经有其他task悬停在flag事件组上了 pnode_next->OSFlagNodePrev = pnode; } pgrp->OSFlagWaitList = (void *)pnode;//设置本task为链表头部 //把本task从就绪控制矩阵中摘下[gliethttp] y = OSTCBCur->OSTCBY; OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX; if (OSRdyTbl[y] == 0x00) { //当前y行对应的8个或16个task都已经悬停,那么当前y行也清除. OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } } //---------------------------------------------------------------------- //3.OS_FlagUnlink()函数 void OS_FlagUnlink (OS_FLAG_NODE *pnode) { #if OS_TASK_DEL_EN > 0 OS_TCB *ptcb; #endif OS_FLAG_GRP *pgrp; OS_FLAG_NODE *pnode_prev; OS_FLAG_NODE *pnode_next; //把管理本task的node从pgrp双向链表中摘下来 pnode_prev = (OS_FLAG_NODE *)pnode->OSFlagNodePrev; pnode_next = (OS_FLAG_NODE *)pnode->OSFlagNodeNext; if (pnode_prev == (OS_FLAG_NODE *)0) { //说明本node为双向链表头 pgrp = (OS_FLAG_GRP *)pnode->OSFlagNodeFlagGrp; pgrp->OSFlagWaitList = (void *)pnode_next;//设置本task的下一个task作为链表头 if (pnode_next != (OS_FLAG_NODE *)0) { //如果下一个task存在,那么将下一个task的node的prev设置成0, //进而来表征下一个task是链表头 pnode_next->OSFlagNodePrev = (OS_FLAG_NODE *)0; } } else { //说明本node为双向链表中普通一员 pnode_prev->OSFlagNodeNext = pnode_next;//直接跳过本node的链接 if (pnode_next != (OS_FLAG_NODE *)0) { //下一个task存在,那么完成双向链表的prev项 pnode_next->OSFlagNodePrev = pnode_prev; } } #if OS_TASK_DEL_EN > 0 ptcb = (OS_TCB *)pnode->OSFlagNodeTCB; ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0; #endif } //---------------------------------------------------------------------- //4.OSFlagPost()函数 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 == 3 OS_CPU_SR cpu_sr = 0; #endif
#if OS_ARG_CHK_EN > 0 if (perr == (INT8U *)0) { return ((OS_FLAGS)0); } if (pgrp == (OS_FLAG_GRP *)0) { *perr = OS_ERR_FLAG_INVALID_PGRP; return ((OS_FLAGS)0); } #endif if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { *perr = OS_ERR_EVENT_TYPE; return ((OS_FLAGS)0); }
OS_ENTER_CRITICAL(); //对flag事件组,进行位操作 switch (opt) { case OS_FLAG_CLR: pgrp->OSFlagFlags &= ~flags;//清除flag标志组pgrp->OSFlagFlags中flags位为1的位 break; case OS_FLAG_SET: pgrp->OSFlagFlags |= flags;//置位flag标志组pgrp->OSFlagFlags中flags位为1的位 break; default://没有该操作,直接error返回 OS_EXIT_CRITICAL(); *perr = OS_ERR_FLAG_INVALID_OPT; return ((OS_FLAGS)0); } sched = OS_FALSE; pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList; //2007-09-10 gliethttp //遍历悬停在pgrp->OSFlagWaitList双向链表上的所有task,唤醒满足flag事件组条件者 while (pnode != (OS_FLAG_NODE *)0) {//双向链表中还有task没有运算 switch (pnode->OSFlagNodeWaitType) { //该node管理的task的等待类型 case OS_FLAG_WAIT_SET_ALL: //flag事件组中所有事件都置位才唤醒 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags); if (flags_rdy == pnode->OSFlagNodeFlags) { //flag事件组中指定的所有事件都已经登记了,那么将本task的node从flag事件组pgrp双向链表中 //摘下来,如果本task仅仅在等待flag事件组的发生,那么将本task添加到就绪控制矩阵中, //等待os的调度 rdy = OS_FlagTaskRdy(pnode, flags_rdy); if (rdy == OS_TRUE) { sched = OS_TRUE;//本task被添加到了就绪控制矩阵中,为了rtos要求,需要调度 } } break; case OS_FLAG_WAIT_SET_ANY: //flag事件组中指定的事件们,只要有一个事件发生置位就唤醒 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags); if (flags_rdy != (OS_FLAGS)0) { //flag事件组中指定的所有事件都已经登记了,那么将本task的node从flag事件组pgrp双向链表中 //摘下来,如果本task仅仅在等待flag事件组的发生,那么将本task添加到就绪控制矩阵中, //等待os的调度 rdy = OS_FlagTaskRdy(pnode, flags_rdy); if (rdy == OS_TRUE) { sched = OS_TRUE;//本task被添加到了就绪控制矩阵中,为了rtos要求,需要调度 } } break; #if OS_FLAG_WAIT_CLR_EN > 0 case OS_FLAG_WAIT_CLR_ALL: //flag事件组中所有事件都清0才唤醒 flags_rdy = (OS_FLAGS)(~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags); if (flags_rdy == pnode->OSFlagNodeFlags) { //flag事件组中指定的所有事件都已经登记了,那么将本task的node从flag事件组pgrp双向链表中 //摘下来,如果本task仅仅在等待flag事件组的发生,那么将本task添加到就绪控制矩阵中, //等待os的调度 rdy = OS_FlagTaskRdy(pnode, flags_rdy); if (rdy == OS_TRUE) { sched = OS_TRUE;//本task被添加到了就绪控制矩阵中,为了rtos要求,需要调度 } } break; case OS_FLAG_WAIT_CLR_ANY: //flag事件组中指定的事件们,只要有一个事件发生清0就唤醒 flags_rdy = (OS_FLAGS)(~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags); if (flags_rdy != (OS_FLAGS)0) { //flag事件组中指定的所有事件都已经登记了,那么将本task的node从flag事件组pgrp双向链表中 //摘下来,如果本task仅仅在等待flag事件组的发生,那么将本task添加到就绪控制矩阵中, //等待os的调度 rdy = OS_FlagTaskRdy(pnode, flags_rdy); if (rdy == OS_TRUE) { sched = OS_TRUE;//本task被添加到了就绪控制矩阵中,为了rtos要求,需要调度 } } break; #endif default: OS_EXIT_CRITICAL(); *perr = OS_ERR_FLAG_WAIT_TYPE; return ((OS_FLAGS)0); } pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;//下一个悬停在该flag事件组上的task } OS_EXIT_CRITICAL(); if (sched == OS_TRUE) { //可能刚刚放到就绪控制矩阵上的被唤醒的task-A的优先级比调用OSFlagPost()函数的进程B优先级高 //所以需要调用shedule函数, //如果真的高,那么调用OSFlagPost()函数的进程B就要被抢占,os将会切换到新的task去执行[gliethttp] //如果没有调用OSFlagPost()函数的进程B优先级高,那么os不会切换,仍然继续执行进程B,OSFlagPost()正常返回 OS_Sched(); } OS_ENTER_CRITICAL(); //返回当前的OSFlagFlags数值,如果因为OS_Sched()调度去执行了A进程,那么这里的OSFlagFlags //数值可能已经被A进程的consume属性复了位.[gliethttp] flags_cur = pgrp->OSFlagFlags; OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (flags_cur); } //---------------------------------------------------------------------- //5.OS_FlagTaskRdy()函数 static BOOLEAN OS_FlagTaskRdy (OS_FLAG_NODE *pnode, OS_FLAGS flags_rdy) { OS_TCB *ptcb; BOOLEAN sched;
ptcb = (OS_TCB *)pnode->OSFlagNodeTCB; ptcb->OSTCBDly = 0;//复原为正常 ptcb->OSTCBFlagsRdy = flags_rdy; //本task悬停的flag事件组已经发生,清除task上下文控制块上的OS_STAT_FLAG位 ptcb->OSTCBStat &= ~(INT8U)OS_STAT_FLAG; ptcb->OSTCBStatPend = OS_STAT_PEND_OK;//正常收到信号 if (ptcb->OSTCBStat == OS_STAT_RDY) { //如果当前task只是等待该flag事件组,那么把该task放到就绪控制矩阵中,允许内核调度本task OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; sched = OS_TRUE; } else { sched = OS_FALSE; } //OS_FlagUnlink()把分配在本task栈空间上的局部变量node,从pgrp事件组双向链表上摘下来, //进而清除pgrp事件组中无本task的相关链接 OS_FlagUnlink(pnode); return (sched); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ PS:"所以从这里来看,os中的各个功能单元管理着自己的事情,就像面向对象的封装一样," "事件控制矩阵和就绪控制矩阵是各个对象独立自治的关键因素" "其他对象,都努力说服自己相信别的对象是独立的、可信任的、安全的"[gliethttp] //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|