ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox。为什么有了queue机制还要用mbox呢,只要设置queue的msg只有一个不就行了?其实很简单,就是为了节约资源,因为使用queue的话需要专门描述queue的机构体os_q,同时需要分配一段内存用来存放msg,而如果直接使用mbox机制的话,就好多了,节约。。。。。
首先从mbox的创建开始,mbox创建的函数是OSMboxCreate ,简化代码如下:
OS_EVENT *OSMboxCreate (void *pmsg) { OS_EVENT *pevent; if (OSIntNesting > 0) { /* See if called from ISR ... */ return ((OS_EVENT *)0); /* ... can‘t CREATE from an ISR */ } OS_ENTER_CRITICAL(); pevent = OSEventFreeList; /* Get next free event control block */ if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */ OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; pevent->OSEventCnt = 0; pevent->OSEventPtr = pmsg; /* Deposit message in event control block */ OS_EventWaitListInit(pevent); } return (pevent); /* Return pointer to event control block */ }
mbox使用同样使用event机制,它与sem的不同之处在于sem使用event中OSEventCnt变量存放信号量,而mbox使用OSEventPtr存放创建时候的msg地址,可以对比sem的create代码,两者的create代码如此相似。
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr) { void *pmsg; if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* Validate event block type */ *perr = OS_ERR_EVENT_TYPE; return ((void *)0); } if (OSIntNesting > 0) { /* See if called from ISR ... */ *perr = OS_ERR_PEND_ISR; /* ... can‘t PEND from an ISR */ return ((void *)0); } if (OSLockNesting > 0) { /* See if called with scheduler locked ... */ *perr = OS_ERR_PEND_LOCKED; /* ... can‘t PEND when locked */ return ((void *)0); } OS_ENTER_CRITICAL(); pmsg = pevent->OSEventPtr; if (pmsg != (void *)0) { /* See if there is already a message */ pevent->OSEventPtr = (void *)0; /* Clear the mailbox */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (pmsg); /* Return the message received (or NULL) */ } OSTCBCur->OSTCBStat |= OS_STAT_MBOX; /* Message not available, task will pend */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Load timeout in TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready to run */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: pmsg = OSTCBCur->OSTCBMsg; *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: pmsg = (void *)0; *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); pmsg = (void *)0; *perr = OS_ERR_TIMEOUT; /* Indicate that we didn‘t get event within TO */ break; } OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */ OSTCBCur->OSTCBMsg = (void *)0; /* Clear received message */ OS_EXIT_CRITICAL(); return (pmsg); /* Return received message */ }
因为好于其他的机制代码对比,所以将整个代码贴出来,其实主要的差别在黄色部分,因为在创建msg的时候,或者在post msg的时候,会将msg放到OSEventPtr,所以直接从中取出msg,判断当前的msg是否存在,如果存在的话就会将msg返回并且对msgbox即OSEventPtr清0操作;如果msg没有则直接将当前任务挂起处于event的等待列表中,进行任务调度也就是黄色部分下面的处理过程,不做过多的赘述。
INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg) { if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* Validate event block type */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL();(1)==================================================================================================== if (pevent->OSEventGrp != 0) { /* See if any task pending on mailbox */ /* Ready HPT waiting on event */ (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find highest priority task ready to run */ return (OS_ERR_NONE); }(2)==================================================================================================== if (pevent->OSEventPtr != (void *)0) { /* Make sure mailbox doesn‘t already have a msg */ OS_EXIT_CRITICAL(); return (OS_ERR_MBOX_FULL); } pevent->OSEventPtr = pmsg; /* Place message in mailbox */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE);(3)==================================================================================================== }
mbox的post函数代码如上所示,其实处理过程和sem等类似,post其实就是填充的功能,首先判断有没有在等待当前event的任务存在,如果有的话直接将msg等交给等待任务,如果没有则进入到第三部分,判断当前的OSEventPtr中是否有msg,如果有表示没有任务从中取出msg,这时会返回错误,如果OSEventPtr中没有msg,则将msg放到OSEventPtr中,等待任务从中取出。
通过学习sem,mutex,queue以及mbox的内核实现可以大体的了解操作系统任务间的通信机制是如何实现的,也可以和其他的操作系统作对别,通过简单的内核实现取理解复杂的操作系统该部分的实现机制,比如所Linux操作系统,这是一个很好的学习方法。下面简单总结一下这四中通信机制的功能:
1. 信号量:信号量其实就是一种通过数字大小来实现限制资源使用的一种机制,设置信号量其实就是设置资源最大可以允许多少个任务同时访问同一个资源,通过信号量pend和post操作即信号量变量的加减实现任务控制,当特殊情况,只有一个信号量的时候就有点互斥的意思。
2. 互斥锁:互斥锁就是同一时间只有一个任务可以占有资源,当有其他任务要访问资源的时候就会将这个任务挂起,放到event的等待列表中,当占有资源的任务释放掉锁的时候,等待任务才可以占有资源并且上锁,为了防止优先级的翻转,使用了优先级继承的机制,就是把占有资源的任务的优先级提升一下比要使用资源的任务的优先级高。
3. 队列:队列就是取一段内存用于存放消息,这个消息是一个地址,真正的消息内容是存放在这个地址中,这样的话可以就可以实现真正的任务间通信,将数据从一个任务传到另一个任务,而不像信号量和互斥锁一下仅仅是一个限制作用。队列使用要注意,如果多个任务在等待不同的消息的话,有可能会出现不同任务获得了不是自己想要的信息并且将消息从队列中去除掉了,所以使用的时候需要注意。
4. 消息:消息其实是队列的一种特殊情况,为了节省资源,之前也有讲到,如果消息数量一定的话同一时间只有一个消息使用,那么采用消息机制更简单,同样实现了数据的传输功能。消息的使用也同样要注意,pend和post的使用,因为如果有多个任务同时使用的话,就会存在是否是当前任务想要的信息,如果不是的话有可能把别的任务的消息给去处并释放掉了,所以使用时需要注意。