浅析μ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]
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
文章评论(0条评论)
登录后参与讨论