uC/OS—III外建消息队列及任务内建消息队列深度解析

2013-12-29 00:00:00屈环宇陈丽萍唐晓媚
电脑知识与技术 2013年4期

摘要:在uC/OS-II的基础上,uC/OS-III对消息队列做了较大的改进,并新增一项特有的功能:任务内建消息队列。任务内建消息队列不仅可以降低消息队列占用的存储空间、提高消息与任务间的通信效率,还能实现消息与任务的相互一一对应,从而保证了系统的健壮性。

关键词:消息队列;任务内建消息队列;嵌入式操作系统

中图分类号:TP313 文献标识码:A 文章编号:1009-3044(2013)04-0908-03

Depth Analysis Of uC/OS-III's Message Queue and Task Built-in Message Queue

QU Huan-yu,CHEN Li-ping, TANG Xiao-mei

(School of Mathematics,Physics and Informaiton Engineering, Jiaxing University,Jiaxing 314001,China)

Abstract: On the basis of uC/OS-II, uC/OS-III has made a great improvement on it's message queue function,and add a unique function: the task built-in message queue.Task built-in message queue can not only reduce the occupancy storage space of message queue、improve the communication efficiency of messages and tasks, but also can realize one-to-one correspondence between tasks and messages,ensure the system's robustness

Key words: message queues;task built-in message queue;embedded operation system

多任务调度系统中,任务间互相通信的方法可以是共享全局变量、共享内存、信号量等。但若要区分任务对通信数据占有的优先级、实现通信数据的临界性操作、支配通信数据的可访问次数等更为高级的应用,就必须依靠消息队列这一媒介。

uC/OS-III的非必须功能均具有可裁剪性,用户需在OS_CFG.H文件中设置OS_CFG_TASK_Q_EN为1,任务内建消息队列才会被分配到任务控制块中,否则任务内建消息队列部分就会被裁剪而失效。

1 uC/OS-III 消息队列的结构

1.1 外建消息队列

外建消息队列主要由五个部分组成:

struct os_q {

OS_OBJ_TYPE Type; //结构体标识部分

CPU_CHAR *NamePtr; //消息队列命名部分

OS_PEND_LIST PendList; //消息挂起部分

#if OS_CFG_DBG_EN > 0u //功能调试部分

OS_Q *DbgPrevPtr;

OS_Q *DbgNextPtr;

CPU_CHAR *DbgNamePtr;

#endif

OS_MSG_Q MsgQ; }; //消息存储部分

1)结构体标识部分:存放消息队列的标识数据,是操作系统识别此结构体为消息队列的依据 。

2)消息队列命名部分:存放该消息队列的名字,可直接访问该地址并查看该队列名字相对应的ASCII码或者通过uC/Probe查看其相对应的字符串。该命名功能主要应用于产品设计时的调试,区分各个消息队列。

3)消息挂起部分:该部分由指针HeadPtr、TailPtr和计数变量NbrEntries组成,指针指向的是内建于任务控制块的os_pend_data结构体,多个结构体与上述两个指针一起构成了消息挂起队列,等待该消息的任务都以链的形式存放在该队列中。

当任务被添加进挂起队列中,uC/OS-III会根据该任务的优先级、相等优先级时依据添加顺序进行重新排列,以保证当消息到来时最先获得消息的是队列中优先级最高的任务。计数变量记录了当前该挂起队列中所存放的任务个数。

4)功能调试部分:当配置宏OS_CFG_DBG_EN为1时,便使能了uC/OS-III提供的专用于调试的代码,其作用是将所有的内建和外建消息队列按照创建时的顺序以队列的形式展现给用户,方便用户调试时查看所有消息队列及其数据。

5)消息存储部分:该部分由指针InPtr、OutPtr和计数变量NbrEntriesSize、NbrEntries、NbrEntriesMax组成。指针InPtr、OutPtr指向的是os_msg消息结构体,多个该类型结构体与上述两指针一起构成了消息存储队列,该队列为环形队列,环的大小为NbrEntriesSize所设置的数值。

当消息挂起队列中无任务被挂起时,被提交的消息会存储在消息存储队列中直到该队列所允许的上限后,被提交的消息将不能再存储进该消息存储队列中,此时,欲提交该消息的函数会返回相应的错误代号以告知用户该消息队列存储消息已满,该消息将溢出。

变量NbrEntriesSize记录了该队列存放消息的上限个数,变量NbrEntries记录了该队列当前时刻存放的消息数,变量NbrEntriesSizeMax记录了到当前时刻为止该队列存放消息数的峰值。

1.2任务内建消息队列

内建的消息队列仅包括上述的消息存储部分。

在uC/OS-II中,消息队列均为外建消息队列,可实现任务间的互相通信,中断服务程序ISR对消息队列的通信(注意这是单向的,ISR不能被挂起等待消息,否则会因为调度的混乱而导致系统崩溃)。

考虑到某些应用中一种消息只为一个任务服务等简单的通信操作时,没必要再为这个任务创建单独的一个外建消息队列,因而在uC/OS-III中新添了任务内建消息队列的功能。内建的消息队列仅包括上述的消息存储部分,不仅降低的存储空间的要求,同时也使消息能直接传递给任务而无需通过外建消息队列等媒介,提高了实时系统的效率。

2 消息队列接口函数

2.1外建消息队列的接口函数

uC/OS-III为外建消息队列提供了6个接口函数,如图3所示,任务可以通过接口函数往消息队列中发送消息、接收消息。但中断服务程序却只能往消息队列中发送数据,这是因为若中断服务程序等待消息的到来,那么此中断嵌套将一直被挂起,CPU不能够绕过此中断嵌套以恢复该中断前的状态,从而导致系统的崩溃。

OSQCreate():用于创建外建消息队列,调用该函数前必须定义一个消息队列的结构体,并将该结构体的地址作为参数传递给该函数,该函数的参数同时也定义了消息队列所能存放的消息数。

OSQDel():用于删除不再使用的消息队列,使用该函数时需非常谨慎,最好是先删除与该消息队列相关的所有任务,或者事先解除那些任务与该消息队列间的联系,不然会导致这些任务的失效。如果失效的任务是系统或应用中的关键任务,后果将不堪设想,因此,使用该函数时应当非常慎重。

OSQFlush():清空该消息队列中的所有消息,该功能主要用于批量的消息更替,比如ISR从串口接收到一系列的新数据需更替掉原先的旧数据时,该函数相当有效。

OSQPend():任务调用该函数后将被挂起,有两种挂起方式,OS_OPT_PEND_BLOCKING方式:消息队列中没有消息时,任务等待直到消息队列中有消息分配给它或者所设定的等待期限满而该函数返回个错误代号。OS_OPT_PEND_NON_BLOCKING方式:消息队列中没有消息时,任务不进行等待,该函数直接返回个错误代号。

该函数的其中一个重要参数是设置任务等待消息的时间期限,若超过这个时间期限任务也未能接收到其所等待的消息时,函数便返回一个相应的错误代号。当该参数设置为0时,表示该任务永久地等待消息的到来。

OSQPendAbort():使该消息队列中的等待任务取消挂起,有两种取消方式,OS_OPT_PEND_ABORT_1:使该消息队列中优先级最高的一个任务取消挂起,OS_OPT_PEND_ABORT_ALL:该消息队列中的所有任务均取消挂起。取消任务挂起后,uC/OS-III会自动执行调度。若应用中不需其在取消任务后实现调度,可在上述两种方式后或操作宏OS_OPT_POST_NO_SCHED。

OSQPost():提交消息到对应的消息队列,消息的提交方式有三种,OS_OPT_POST_FIFO:先进先出方式,OS_OPT_POST_LIFO:后进先出方式,OS_OPT_POST_ALL:该消息将传递给所有在消息队列中挂起的任务。同样,或操作宏OS_OPT_POST_NO_SCHED可避免调度。

该函数的另外两个关键参数是所要被传输消息数据的首地址及其数据的大小,数据的大小以字节为单位。

在此需说明的技巧是:当同时提交多个消息时,可先失能提交后的调度,待到最后一个消息被提交时,再使能调度。从而避免了需提交几个消息就得执行几次调度的尴尬局面,提高了系统的实时性。

2.2任务内建消息队列的接口函数

在uC/OS-III中,任务内建消息队列共有3个接口函数,该3个函数几乎是外建消息队列接口函数的精简版。

OSTaskQPend():任务调用该函数后被挂起,不同于外建消息队列的是,任务被挂起的信息不是存放在消息队列中,而是直接由uC/OS-III内核直接管理。

OSTaskQPendAbort():调用该函数后任务被取消挂起,由于目标任务为调用该函数的任务本身,取消方式仅为一种,OS_OPT_POST_NONE:取消该任务的挂起。同样,或操作宏OS_OPT_POST_NO_SCHED可避免调度。

OSTaskQPost():提交消息到任务内建消息队列,消息的提交方式有两种:OS_OPT_POST_FIFO,OS_OPT_POST_LIFO。同样,或操作宏OS_OPT_POST_NO_SCHED可避免调度。

3 结束语

消息队列为任务间提供了异步的通信协议,消息的发送者和接收者均可在适当的时候与消息进行信息的交互。作为媒介,消息队列传递的仅是数据的存放地址以及数据的大小,这使得信息的传递变得非常简单、高效。

作为对时间要求非常严格的嵌入式实时系统,uC/OS-III引入了任务内建消息机制,降低了消息队列占用的存储空间、提高消息与任务间的通信效率、实现了消息与任务的相互对应,从而进一步提高了系统的微型性、实时性和健壮性。

参考文献:

[1] 邵贝贝.嵌入式实时操作系统μC\OS-Ⅱ [M].北京:北京航空航天大学出版社,2003:56-89

[2] 王田苗.嵌入式系统设计与实例开发--基于ARM微处理器与uc/OS-II实时操作系统[M].北京:清华大学出版社,2003:103-135.

[3] 任哲.嵌入式操作系统基础uc/os-II和Linux [M].北京:北京航空航天大学出版社,2006:52.

[4] Micrium Micrium-uCOS-III-UserManual [M] .R&D Books,2011.