UC/OS II 消息队列

消息邮箱只能保存一条消息,消息队列没有这一局限,可以容纳多条信息队列,按照先进先出(FIFO)的原则发送和接受消息。
消息队列的实体不是操作系统提供的,而是由用户任务提供的。操作系统提供的是对其进行管理的程序。
1:消息队列的数据结构
消息队列的数据结构主要包括消息队列、消息队列控制块(QCB)、消息队列控制块数组、空闲链表、事件控制块(ECB)等。

消息队列的数据结构定义为一个指针数组,定义格式为void *MessQ[SIZE],SIZE是消息队列里消息的数量,指针数组的每个成员都是指针,指向 消息实体的地址。

消息队列控制块(QCB)是消息队列管理的核心数据结构。每个QCB管理一个消息队列,是消息队列实体存在的唯一依据。
typedef struct os_q {                   
    struct os_q   *OSQPtr;              //指向下一个QCB,主要是用于空闲QCB链表的时候
    void         **OSQStart;            //指向消息队列的首地址
    void         **OSQEnd;              //指向消息队列的尾地址
    void         **OSQIn;               //插入消息的地址
    void         **OSQOut;              //取消息的地址
    INT16U         OSQSize;             //队列的最大容量
    INT16U         OSQEntries;          //队列中当前的容量
} OS_Q

QCB的实体在UCOSii.h中定义 
OS_EXT  OS_Q              OSQTbl[OS_MAX_QS];   OS_MAX_QS系统默认为4  我们可以根据自己的需求进行重新配置

QCB没有被使用会连接在在一个单向链表,OSQWaitList指针指向该链表表头。
2:消息队列的管理
2.1:消息队列的初始化OS_QInit
我们若使用消息队列,系统初始化函数OS_Init会在进行系统初始化的时候,会调用OS_QInit函数对消息队列控制块进行初始化。
OS_QInit的主要功能跟信号量的初始化很类似。
这里就是将OS_Q              OSQTbl[OS_MAX_QS]中的数组成员进行初始化设置,同时将这些QCB连接成一个单链表,并且OSQWaitList指针指向该链表表头。

2.2:建立消息队列OSQCreate
OSQCreate的功能为从空闲QCB中取出一个QCB进行设置,返回ECB的地址
函数原型OS_EVENT  *OSQCreate (void    **start,INT16U    size)  start是用户定义的消息队列指针数组的地址的首地址。
其主要代码如下:
if (pevent != (OS_EVENT *)0) {               //空闲ECB链表中是否为空
        OS_ENTER_CRITICAL();
        pq = OSQFreeList;                        //取QCB空闲链表表头地址
        if (pq != (OS_Q *)0) {                   //QCB空闲链是否为空
            OSQFreeList            = OSQFreeList->OSQPtr; //去QCB空闲链表的第一个QCB
            OS_EXIT_CRITICAL();
  //初始化QCB
            pq->OSQStart           = start;               
            pq->OSQEnd             = &start[size];
            pq->OSQIn              = start;
            pq->OSQOut             = start;
            pq->OSQSize            = size;
            pq->OSQEntries         = 0u;
//初始化ECB
            pevent->OSEventType    = OS_EVENT_TYPE_Q;
            pevent->OSEventCnt     = 0u;
            pevent->OSEventPtr     = pq;
#if OS_EVENT_NAME_EN > 0u
            pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
            OS_EventWaitListInit(pevent);                 //初始化事件控制块的等任务待数组和表
        } else {
            pevent->OSEventPtr = (void *)OSEventFreeList; //ECB空闲链表为空,没有空闲的QCB
            OSEventFreeList    = pevent;
            OS_EXIT_CRITICAL();
            pevent = (OS_EVENT *)0;  //pevent为空
        }
    }

2.3:删除消息队列OSQDel
删除消息队列与删除信号量差不多,对ECB的操作是一样的,但是消息队列多了一个QCB的处理。
对QCB处理的代码如下:
//下面就是将相关的QCB放到QCB空闲链表中
  pq                     = (OS_Q *)pevent->OSEventPtr;  
  pq->OSQPtr             = OSQFreeList;
  OSQFreeList            = pq;

2.4:消息队列刷新OSQFlush
该函数的功能就是刷新消息队列,清空消息队列。
其源码如下,将队列的队头和队尾都指向消息队列的基地址。
pq             = (OS_Q *)pevent->OSEventPtr;  
pq->OSQIn      = pq->OSQStart;
pq->OSQOut     = pq->OSQStart;

2.5申请消息队列中的消息OSQPend
该函数的功能是任务向消息队列申请消息,若是消息队列中有消息则取消息,若是没有消息,则取阻塞等待。
该函数的原型如下:void  *OSQPend (OS_EVENT  *pevent, INT32U     timeout,  INT8U     *perr)
函数的功能实现和消息邮箱差不多。只是在判断是否有消息上有点区别,如下源码:
pq = (OS_Q *)pevent->OSEventPtr;             //QCB的地址
    if (pq->OSQEntries > 0u) {                   //判断消息队列是否为空
        pmsg = *pq->OSQOut++;                    //取队列中的消息
        pq->OSQEntries--;                        //消息队列消息数目减1
        if (pq->OSQOut == pq->OSQEnd) { //去消息的地址在队列的尾地址
            pq->OSQOut = pq->OSQStart; //去消息的地址赋值为队列的首地址(循环队列)
        }
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;  
        return (pmsg);                           //返回消息地址
    }
//若是为空,就阻塞任务。事件阻塞任务的方式除了事件标志组有点不一样之外,其他的事件得不到满足阻塞任务的方式都是一样的。
需要注意的是阻塞后重新就绪,任务不回在QCB中取消息,而是通过任务控制块中的OSTCBMsg 成员获取消息。
因为会阻塞,所以回复就绪后,取队列中第一个消息。所以可以用OSTCBMsg。
对照后面消息发送,若是在提交消息的时候,没有阻塞,就会放到队列中,有阻塞,唤醒阻塞任务,消息地址存入唤醒人的任务控制快的OSTCBMsg

2.6放弃消息等待函数OSQPendAbort
该函数的主要功能为唤醒所有等待的消息而阻塞的任务。该函数只能由非等待阻塞的任务执行,等待消息阻塞的任务是不可能去执行该函数的。
该函数的原型为:INT8U  OSQPendAbort (OS_EVENT  *pevent, INT8U      opt,  INT8U     *perr)
opt选项,两种情况
OS_POST_OPT_NONE         放弃等待,然后唤醒等待任务中的最高优先级的任务,就是执行OSMboxPost函数一样的功能
OS_POST_OPT_BROADCAST    放弃等待广播给所有的等待任务,唤醒所有等待任务

2.7发送消息到消息队列中OSQPost
该函数的原型为INT8U  OSQPost (OS_EVENT  *pevent, void      *pmsg)
该函数的主要功能为向队列发送一个消息,若是有任务在等待队列消息,唤醒任务,重新调度,如果没有,则更新消息队列。
其主要代码如下:
if (pevent->OSEventGrp != 0u) {                    //是否有任务在等待队列中的消息
                                                       //唤醒最等待消息的最高优先级任务,并且将消息地址赋给阻塞最高优先级任务控制快的OSTCBMsg
        (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
        OS_EXIT_CRITICAL();
        OS_Sched();                                    //执行调度,执行就绪最高优先的任务
        return (OS_ERR_NONE);
    }
    pq = (OS_Q *)pevent->OSEventPtr;                   //
    if (pq->OSQEntries >= pq->OSQSize) {               //如果消息数目大于消息队列容量即队列已满,立即返回,不操作队列
        OS_EXIT_CRITICAL();
        return (OS_ERR_Q_FULL);
    }
    *pq->OSQIn++ = pmsg;                               //队列没满,消息插入队列中
    pq->OSQEntries++;                                  //队列消息数目加一
    if (pq->OSQIn == pq->OSQEnd) {                     //如果插入消息位置在队列的尾地址,则将插入位置移到队列的头地址
        pq->OSQIn = pq->OSQStart;
    }
2.8:发送消息到消息队列中OSQPostFront
该函数跟2.7中的函数最大的不同就是,提交消息插入的位置不是队列的尾部,而是队列的头部。这个就不像是队列的操作,而是有点像堆栈的操作。

2.9:单选项的消息发送OSQPostOpt
函数的原型为INT8U  OSQPostOpt (OS_EVENT  *pevent, void      *pmsg, INT8U      opt)
跟2.8 2.9不同的是,这个函数参数中有一个opt参数。
该参数有四种情况:
OS_POST_OPT_NONE          消息发送,而且唤醒单个等待任务
OS_POST_OPT_BROADCAST    消息广播发送,唤醒所有等待任务
OS_POST_OPT_FRONT        发送将消息,并且其插入到队列的头部。类似于2.8的功能
OS_POST_OPT_NO_SCHED    消息提交之后,但是并不执行任务调度。

2.10:不等待请求消息队列函数OSQAccept
函数的原型为void  *OSQAccept (OS_EVENT  *pevent, INT8U     *perr)
该函数功能为向队列请求消息,若有消息,则返回非空指针。若是没有消息,不等待,返回空指针,转而执行其他部分。
2.11查询消息队列的状态OSQQuery
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值