注:以下讨论都是基于UCOSII V2.92.07,其他版本可能略微存在差异。
引用任哲书上的一句话解释什么是信号量最合适不过,应用程序中的各个人物,必须通过彼此之间的有效合作,才能完成一项大规模的工作,因为这些任务在运行的时候,经常需要无冲突的访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要相互制约才能保证任务的顺利运行,因此操作系统必须具有对任务运行进行协调的能力,从而使任务无冲突、流畅的同步运行,这就是为什么我们要使用任务间同步机制。
谈到任务的同步,UCOSII中一共有4种同步方式。(信号量、互斥信号量、消息邮箱、消息队列)而要想深入理解这一系列的机制。那免不了说说他们的基本组成单元(事件控制块),我们通过几幅图片来理解一下。
通过上面的图片来理解信号量是不是比较直观。不过开始也就让大家有个印象,接下来说说任务同步的基本组成-事件控制块。
事件控制块:
事件控制块的基本数据结构
typedef struct os_event {
INT8U OSEventType; //说明具体的同步机制类型,如消息邮箱
void *OSEventPtr; //未使用时用于链接控制块为单向链表,使用时根据具体情况使用
INT16U OSEventCnt;
OS_PRIO OSEventGrp; //事件组
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; //事件标志,主要存放等待相应信号的任务标志
#if OS_EVENT_NAME_EN > 0u
INT8U *OSEventName; //事件名
#endif
} OS_EVENT;
而在一开始,事件控制块会被系统初始化为一个单向的链表。
上面就是一个基本的事件控制块结构,他是组成其他同步机制的基础。而关于其基本的操作函数,也
有3个。分别为
void OS_EventWaitListInit (OS_EVENT *pevent); //初始化一个事件控制块
void OS_EventTaskWait (OS_EVENT *pevent); //使一个任务进入等待某事件发生的状态
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg,
INT8U msk, INT8U pend_stat); //使一个任务进入就绪态
// void OS_EventTO(OS_EVENT *pevent); //等待超时而将任务置于就绪态 在V2.53版本中存在
void OS_EventTaskRemove (OS_TCB *ptcb, OS_EVENT *pevent) //V2.9版本,其作用和OS_EventTO()相同
接下来我们来分析分析这四个函数的具体实现
void OS_EventWaitListInit (OS_EVENT *pevent)
{
INT8U i;
pevent->OSEventGrp = 0u; //初始化传进来的时间控制块OSEventGrp域为0
for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) {
pevent->OSEventTbl[i] = 0u; //初始化对应的标志数组
}
}
总而言之是对OS_EVENT事件控制块域的初始化。
void OS_EventTaskWait (OS_EVENT *pevent)
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent; //保存任务的事件控制块指针
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;//置起当前任务到等待事件表
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
y = OSTCBCur->OSTCBY; /* Task no longer ready */
OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0u) { //如果任务没有就绪,则清除标志组
OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
}
}
其主要是将调用本函数的任务挂起,具体挂起的类型主要看该控制块的数据类型
INT8U OS_EventTaskRdy (OS_EVENT *pevent,
void *pmsg,
INT8U msk,
INT8U pend_stat)
{
OS_TCB *ptcb;
INT8U y;
INT8U x;
INT8U prio;
#if OS_LOWEST_PRIO > 63u
OS_PRIO *ptbl;
#endif
#if OS_LOWEST_PRIO <= 63u
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (INT8U)((y << 3u) + x); //获取当前挂起的最高优先级任务
#else
if ((pevent->OSEventGrp & 0xFFu) != 0u) { //当最低优先级大于63时,启用16*16事件标志组
y = OSUnMapTbl[ pevent->OSEventGrp & 0xFFu];
} else {
y = OSUnMapTbl[(OS_PRIO)(pevent->OSEventGrp >> 8u) & 0xFFu] + 8u;
}
ptbl = &pevent->OSEventTbl[y];
if ((*ptbl & 0xFFu) != 0u) {
x = OSUnMapTbl[*ptbl & 0xFFu];
} else {
x = OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u;
}
prio = (INT8U)((y << 4u) + x); /* Find priority of task getting the msg */
#endif
ptcb = OSTCBPrioTbl[prio]; /* Point to