菜鸟学uC/OS_II(4)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
By <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />Norman
2008-7-11
消息邮箱和消息队列
消息邮箱和消息队列其实是类似的,只不过消息个数是不同的,当然,这也让其处理的细节上有所不同,例如消息的数据结构等等。
消息邮箱:
消息邮箱在等待过程中的处理似乎同前面的其他事件的方式不太相同。例如信号,在等待过程中,如果信号计数值为“0”,那么任务挂起,进行调度,调度完成之后,等待信号量的任务会再次运行,或者是因为超时,或者是因为有任务调用了OSSemPost()函数,给了等待任务一个信号量。这里,OSSemPost()函数实际上直接把信号量给了等待任务并让其就绪(如果有的话)——这里需要注意OS_EventTaskRdy()函数的内部机制:函数传入参数为当前事件块指针,消息指针和状态掩码,如果事件为消息邮箱或者队列,则传递消息并置任务状态为“就绪”,否则,忽略消息,直接置任务状态为“就绪”——所以,这种情况下,调度之后,内核不用去检查信号量的多少而是检查任务控制块中的状态标志,OSSemPost()函数直接同等待任务打交道,并未通过信号量事件块。
根据前面对OS_EventTaskRdy()函数的分析,消息邮箱由此可以进行不同的处理,消息要经过邮箱到等待任务。在调用OSMboxPend()发生等待之后,如果有任务调用了OSMboxPost()发送了消息,那么,调用OSEventTaskRdy()函数就会把消息一并送入调度后就绪的任务的TCB中,所以,重新运行的OSMboxPend()函数可以去检查当前就绪任务控制块是否收到消息,如果是,则进行一些处理,包括置状态为“就绪”:OSTCBCur——>OSTCBStat = OS_STAT_RDY。如果不是,那么一定是发生了超时,进行超时的处理。
从这里的分析看来,我有点疑问,就是为什么消息邮箱不跟信号量等待处理做到一致呢?同样是调用OSEventTaskRdy(),为何一个只检查状态标志(当然信号量不传递消息),另一个去检查消息的存在与否呢?如果邮箱也直接检查状态标志,似乎也可以判断是否因为超时而重新就绪。这一点还真的没有闹得清楚,需要再仔细研读内核。
消息邮箱还有两个额外的功能:二值信号量、延时;这是由于邮箱的特性决定的,实现起来也没有什么特别,是一种必然的巧合也说不定。
消息队列:
首先来分析环形缓冲这个消息队列的数据结构。
这句话其实已经说的很清楚了,这就是一个环形栈的数据结构,为这个环形数据结构分配两个指针OSQStart和OSQEnd就是为了作一个简单的标记以实现循环:将OSQIn和OSQOut同时指向OSQStart,此时设没有消息。有任务来取消息,对不起,没有,你就挂起吧;如果有任务调用OSQPost(),那么在OSQIn处插入(一般情况),同时OSQIn++;这时,消息队列中就有了消息,任务可以取消息了,取消息总是从OSQOut处取得,取之后,OSQOut++;,可以看出来,OSQOut仿佛一直在“追击”OSQIn这个指针;如果OSQIn和OSQOut到了OSQEnd,也就是到了消息队列的“物理队尾”了,如何办?不可能一直将地址增加吧?我们可没有这么多内存来消耗。因此,将OSQIn或者OSQOut再次指向OSQStart处,完成一个环形循环结构,就可以了,反正消息还是从OSQIn处插入,从OSQOut出读出,没有破坏消息队列FIFO的机制(其他情况另行讨论)。
LIFO形式的OSQPostFront()函数将OSQOut作为插入点,这样做的目的是或许这个消息比较重要,让新插入消息较快读出。不难想象,这样插入点话,OSQOut就需要自减一次;这样一来,如果OSQOut==OSQStart,那么就将其重新指向OSQEnd就可以完成循环了。
消息队列的合理应用:消息队列并不是只有用来做通信或者同步的,它也可以实现其他很多功能,需要在实践中去总结和积累。
(待续)
文章评论(0条评论)
登录后参与讨论