Ucos源码分析
1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结
事件标志组----OS_FLAG.C
事件标志组的等待任务表的实现与其它事件不同:
事件标志组的waitlist指针指向等待节点,代替了其它事件类型的任务等待表。
一个事件标志组的多个任务标志组节点,代表等待该事件标志组的的任务。
1.事件标志组
当某任务要与多个事件同步时,使用事件标志组。
同步方式有独立型同步和关联型同步。
独立型同步:等待多个事件时,任何一个事件发生 ,任务都被同步。
关联型同步:等待多个事件时,所有的事件都发生 ,任务才被同步。
任务标志组控制块的基本成员如下
控制块成员 | |
---|---|
OSFlagType | 事件类型,默认为OS_EVENT_TYPE_FLAG |
*OSFlagWaitList | 等待事件标志组的任务节点指针 |
OSFlagFlags | 指定位数的任务标志组 |
任务标志组控制节点,是等待列表的实现形式,是任务标志组事件与任务 连接的媒介
任务标志组控制节点的基本成员如下
节点成员 | |
---|---|
OSFlagNodeNext | 等待列表中下一个节点的指针 |
OSFlagNodePrev | 等待列表中上一个节点的指针 |
OSFlagNodeTCB | 该节点所指向的任务,即任务控制块 |
OSFlagNodeFlagGrp | 该节点所指向的件标志组控制块 |
OSFlagNodeFlags | 该节点所指向的任务标志组 |
OSFlagNodeWaitType | 事件等待形式 |
2.任务的等待与就绪
因为事件标志组的等待任务列表与其他事件不一样,所以任务等待和就绪实现也不一样。
任务等待
事件标志组控制块的等待任务列表的指针(pgrp->OSFlagWaitList)指向第一个调用的任务
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;
// 当前任务的 等待事件标志组 状态置位
OSTCBCur->OSTCBStat |= OS_STAT_FLAG;
// 当前任务的等待延时
OSTCBCur->OSTCBDly = timeout; /* Store timeout in task's TCB */
#if OS_TASK_DEL_EN > 0
OSTCBCur->OSTCBFlagNode = pnode; /* TCB to link to node */
#endif
//设置事件标志组节点的 事件标志组标志 ,
pnode->OSFlagNodeFlags = flags; /* Save the flags that we need to wait for */
//设置事件标志组节点的 事件等待类型
pnode->OSFlagNodeWaitType = wait_type; /* Save the type of wait we are doing */
//设置事件标志组节点的 任务控制块 为当前任务
pnode->OSFlagNodeTCB = (void *)OSTCBCur; /* Link to task's TCB */
pnode->OSFlagNodeNext = pgrp->OSFlagWaitList; /* Add node at beginning of event flag wait list */
pnode->OSFlagNodePrev = (void *)0;
//设置事件标志组节点的 事件控制块
pnode->OSFlagNodeFlagGrp = (void *)pgrp; /* Link to Event Flag Group */
pnode_next = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;
//当前节点是不是加入的第一个节点
if (pnode_next != (void *)0) { /* Is this the first NODE to insert? */
//不是形成双向链表
pnode_next->OSFlagNodePrev = pnode; /* No, link in doubly linked list */
}
//将节点加入到 事件标志组事件的 任务等待表
pgrp->OSFlagWaitList = (void *)pnode;
/* Suspend current task until flag(s) received */
//当前任务移出就绪表
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
}
任务就绪
static BOOLEAN OS_FlagTaskRdy (OS_FLAG_NODE *pnode, OS_FLAGS flags_rdy)
{
OS_TCB *ptcb;
BOOLEAN sched;
//获取当前时间标志组节点的任务
ptcb = (OS_TCB *)pnode->OSFlagNodeTCB; /* Point to TCB of waiting task */
//任务超时清除
ptcb->OSTCBDly = 0;
//任务标志组 等待的事件标志置位
ptcb->OSTCBFlagsRdy = flags_rdy;
//任务等待事件标志组 状态清除
ptcb->OSTCBStat &= ~OS_STAT_FLAG;
//判断任务是否是就绪态
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* Put task into ready list */
//是就绪态 将任务移到任务就绪表
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
//任务是就绪态 移到任务就绪表 返回允许调度
sched = TRUE;
} else {
//任务是就绪态 移到任务就绪表 返回允许调度
sched = FALSE;
}
//将任务标志组节点 移除,将等待任务清除
OS_FlagUnlink(pnode);
return (sched);
}
事件标志组节点的删除
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;
//待删除节点的前一个节点
pnode_prev = (OS_FLAG_NODE *)pnode->OSFlagNodePrev;
//待删除节点的后一个节点
pnode_next = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;
//如果待删除节点是等待表的第一个节点
if (pnode_prev == (OS_FLAG_NODE *)0) { /* Is it first node in wait list? */
//获取该节点对应的事件标志组事件控制块
pgrp = (OS_FLAG_GRP *)pnode->OSFlagNodeFlagGrp;
//事件标志组事件控制块的 等待表指针指向待删除节点的下一个节点
pgrp->OSFlagWaitList = (void *)pnode_next; /* Update list for new 1st node */
//如果后一个节点存在
if (pnode_next != (OS_FLAG_NODE *)0) {
//后一个节点 成为等待表的第一个节点
pnode_next->OSFlagNodePrev = (OS_FLAG_NODE *)0; /* Link new 1st node PREV to NULL */
}
} else {
//节点删除 /* No, A node somewhere in the list */
pnode_prev->OSFlagNodeNext = pnode_next; /* Link around the node to unlink */
//如果后一个节点存在
if (pnode_next != (OS_FLAG_NODE *)0) { /* Was this the LAST node? */
//形成双向链表
pnode_next->OSFlagNodePrev = pnode_prev; /* No, Link around current node */
}
}
#if OS_TASK_DEL_EN > 0
ptcb = (OS_TCB *)pnode->OSFlagNodeTCB;
ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0;
#endif
}
3.任务标志组的获取
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err)
参数:
pgrp:事件标志组控制块
flags :事件标志组
wait_type :等待类型
OS_FLAG_WAIT_SET_ALL:所有位都置1
OS_FLAG_WAIT_SET_ANY:任一位置1
OS_FLAG_WAIT_SET_ALL:所有位都置0
OS_FLAG_WAIT_SET_ANY:任一位置0
timeout :超时
err:获取错误信息的指针
函数还要注意使用完事件标志位后,事件标志位是否清除
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_FLAG_NODE node;
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
BOOLEAN consume;
//不能中断调用
if (OSIntNesting > 0) { /* See if called from ISR ... */
*err = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return ((OS_FLAGS)0);
}
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { /* Validate 'pgrp' */
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { /* Validate event block type */
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
//收到指定事件后,复位flag事件组,将相应的事件标志清0
if (wait_type & OS_FLAG_CONSUME) { /* See if we need to consume the flags */
wait_type &= ~OS_FLAG_CONSUME;
consume = TRUE;
} else {
consume = FALSE;
}
/*$PAGE*/
OS_ENTER_CRITICAL();
switch (wait_type) {
//事件标志组 所有标志都置1
case OS_FLAG_WAIT_SET_ALL: /* See if all required flags are set */
//事件标志组中已经发生的标志 与 任务所需标志“与”运算,取出任务 关心的任务标志组中的标志
flags_rdy = pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
//事件标志组中的标志状态,满足任务标志需求
//flags_rdy与任务请求的标志 要相等
if (flags_rdy == flags) { /* Must match ALL the bits that we want */
//判断使用到的标志是否需要清除
if (consume == TRUE) { /* See if we need to consume the flags */
//清除使用到的标志位 置0
pgrp->OSFlagFlags &= ~flags_rdy; /* Clear ONLY the flags that we wanted */
}
//获取现在任务标志组中的标志
flags_cur = pgrp->OSFlagFlags; /* Will return the state of the group */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*err = OS_NO_ERR;
//返回
return (flags_cur);
}
//事件标志组中的标志状态,不满足任务需要的标志需求
else { /* Block task until events occur or timeout */
//当前任务移出就绪表,当前任务借助事件标志组节点完成等待事件表
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
//事件标志组 任一标志置1
case OS_FLAG_WAIT_SET_ANY:
//事件标志组中已经发生的标志 与 任务所需标志“与”运算,取出任务 关心的任务标志组中的标志
flags_rdy = pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
//事件标志组中的标志状态,满足任务标志需求
// 满足只发生一个 flags_rdy不为0就可
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag set */
//判断使用到的标志是否需要清除
if (consume == TRUE) { /* See if we need to consume the flags */
//清除使用到的标志位 置0
pgrp->OSFlagFlags &= ~flags_rdy; /* Clear ONLY the flags that we got */
}
//获取现在任务标志组中的标志
flags_cur = pgrp->OSFlagFlags; /* Will return the state of the group */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*err = OS_NO_ERR;
//返回
return (flags_cur);
}
//事件标志组中的标志状态,不满足任务需要的标志需求
else { /* Block task until events occur or timeout */
//当前任务移出就绪表,当前任务借助事件标志组节点完成等待事件表
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0
//事件标志组 所有标志都置0
case OS_FLAG_WAIT_CLR_ALL: /* See if all required flags are cleared */
// flags_rdy = (~pgrp->OSFlagFlags) & flags;
// 取出关心的位,将0 翻转位1 进行判断
flags_rdy = ~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
//事件标志组中的标志状态,满足任务标志需求
//flags_rdy与任务请求的标志 要相等
if (flags_rdy == flags) { /* Must match ALL the bits that we want */
//判断使用到的标志是否需要清除
if (consume == TRUE) { /* See if we need to consume the flags */
//清除使用到的标志位 置1
pgrp->OSFlagFlags |= flags_rdy; /* Set ONLY the flags that we wanted */
}
//获取现在任务标志组中的标志
flags_cur = pgrp->OSFlagFlags; /* Will return the state of the group */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*err = OS_NO_ERR;
return (flags_cur);
}
//事件标志组中的标志状态,不满足任务需要的标志需求
else { /* Block task until events occur or timeout */
//当前任务移出就绪表,当前任务借助事件标志组节点完成等待事件表
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
//事件标志组 任一标志置0
case OS_FLAG_WAIT_CLR_ANY:
// 取出关心的位,将0 翻转位1 进行判断
flags_rdy = ~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
//事件标志组中的标志状态,满足任务标志需求
//flags_rdy不为0
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag cleared */
//判断使用到的标志是否需要清除
if (consume == TRUE) { /* See if we need to consume the flags */
//清除使用到的标志位 置1
pgrp->OSFlagFlags |= flags_rdy; /* Set ONLY the flags that we got */
}
//获取现在任务标志组中的标志
flags_cur = pgrp->OSFlagFlags; /* Will return the state of the group */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*err = OS_NO_ERR;
return (flags_cur);
}
//事件标志组中的标志状态,不满足任务需要的标志需求
else { /* Block task until events occur or timeout */
//当前任务移出就绪表,当前任务借助事件标志组节点完成等待事件表
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#endif
default:
OS_EXIT_CRITICAL();
flags_cur = (OS_FLAGS)0;
*err = OS_FLAG_ERR_WAIT_TYPE;
return (flags_cur);
}
//任务调度
OS_Sched(); /* Find next HPT ready to run */
OS_ENTER_CRITICAL();
//任务超时判断
if (OSTCBCur->OSTCBStat & OS_STAT_FLAG) { /* Have we timed-out? */
OS_FlagUnlink(&node);
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Yes, make task ready-to-run */
OS_EXIT_CRITICAL();
flags_cur = (OS_FLAGS)0;
*err = OS_TIMEOUT; /* Indicate that we timed-out waiting */
} else {
//清除使用到的标志位
if (consume == TRUE) { /* See if we need to consume the flags */
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
case OS_FLAG_WAIT_SET_ANY: /* Clear ONLY the flags we got */
pgrp->OSFlagFlags &= ~OSTCBCur->OSTCBFlagsRdy;
break;
#if OS_FLAG_WAIT_CLR_EN > 0
case OS_FLAG_WAIT_CLR_ALL:
case OS_FLAG_WAIT_CLR_ANY: /* Set ONLY the flags we got */
pgrp->OSFlagFlags |= OSTCBCur->OSTCBFlagsRdy;
break;
#endif
}
}
//获取现在任务标志组中的标志
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR; /* Event(s) must have occurred */
}
//返回
return (flags_cur);
}
4.任务标志组的释放----遍历所有等待任务
OSFlagPost是遍历所有的等待任务,将满足事件标志组的,所有任务从等待列表移除,加入到就绪表。
OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err)
参数:
pgrp:事件标志组控制块
flags :事件标志组
opt:
OS_FLAG_CLR: 事件标志组中指定的位(flags) 置0
OS_FLAG_SET :事件标志组中指定的位(flags) 置1
err:获取错误信息的指针
OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_FLAG_NODE *pnode;
BOOLEAN sched;
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { /* Validate 'pgrp' */
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { /* Make sure we are pointing to an event flag grp */
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
/*$PAGE*/
OS_ENTER_CRITICAL();
switch (opt) {
//将事件标志组(pgrp->OSFlagFlags)中指定的位(flags) 置0
case OS_FLAG_CLR:
pgrp->OSFlagFlags &= ~flags; /* Clear the flags specified in the group */
break;
//将事件标志组(pgrp->OSFlagFlags)中指定的位(flags) 置1
case OS_FLAG_SET:
pgrp->OSFlagFlags |= flags; /* Set the flags specified in the group */
break;
//没有相应操作,报错返回
default:
OS_EXIT_CRITICAL(); /* INVALID option */
*err = OS_FLAG_INVALID_OPT;
return ((OS_FLAGS)0);
}
sched = FALSE; /* Indicate that we don't need rescheduling */
//获取等待任务节点的表头指针
pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;
//遍历所有等待任务
while (pnode != (OS_FLAG_NODE *)0) { /* Go through all tasks waiting on event flag(s) */
//该node管理的任务的等待类型
switch (pnode->OSFlagNodeWaitType) {
//事件标志组中所有事件都置1才唤醒
case OS_FLAG_WAIT_SET_ALL: /* See if all req. flags are set for current node */
//获取该任务标志组关心的位
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy == pnode->OSFlagNodeFlags) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) { /* Make task RTR, event(s) Rx'd */
sched = TRUE; /* When done we will reschedule */
}
}
break;
//事件标志组中所有事件 任一置1都唤醒
case OS_FLAG_WAIT_SET_ANY: /* See if any flag set */
//获取该任务标志组关心的位
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy != (OS_FLAGS)0) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) { /* Make task RTR, event(s) Rx'd */
sched = TRUE; /* When done we will reschedule */
}
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0
//事件标志组中所有事件 所有置0才唤醒
case OS_FLAG_WAIT_CLR_ALL: /* See if all req. flags are set for current node */
//获取该任务标志组关心的位
flags_rdy = ~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy == pnode->OSFlagNodeFlags) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) { /* Make task RTR, event(s) Rx'd */
sched = TRUE; /* When done we will reschedule */
}
}
break;
//事件标志组中所有事件 任一置0都唤醒
case OS_FLAG_WAIT_CLR_ANY: /* See if any flag set */
//获取该任务标志组关心的位
flags_rdy = ~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy != (OS_FLAGS)0) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) { /* Make task RTR, event(s) Rx'd */
sched = TRUE; /* When done we will reschedule */
}
}
break;
#endif
}
//下一个在等待节点上的任务
pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext; /* Point to next task waiting for event flag(s) */
}
OS_EXIT_CRITICAL();
//系统调度
if (sched == TRUE) {
OS_Sched();
}
OS_ENTER_CRITICAL();
//返回当前的事件标志组,如果因为OS_Sched()调度去执行了其它任务
//这里的OSFlagFlags可能已经被其他任务consume属性复了位
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur);
}
5.任务,事件标志组控制块,与事件标志组节点关系
一个事件标志组节点 将一个任务和事件标注组控制块绑定
一个事件标志控制块 可以有多个事件标志组节点