设为首页 加入收藏

TOP

uC/OSIII的消息队列处理机制(一)
2015-11-21 02:13:24 来源: 作者: 【 】 浏览:40
Tags:uC/OSIII 消息 队列 处理 机制

在uC/OSIII中没有邮箱这个概念,而是统一合并到了消息队列MSG_Q。因为消息队列可以看作是很多邮箱的集合,邮箱只是包含单个消息的消息队列。
在分析消息队列之前,必须要对消息的数据结构做一个彻底的分析。
消息队列对象和其他内核对象一样,它的结构定义很简单:
下面看一下消息队列的结构体,记住这个结构体名字叫OS_Q:
struct os_q { /* Message Queue */
OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_Q */
CPU_CHAR *NamePtr; /* Pointer to Message Queue Name (NUL terminated ASCII) */
OS_PEND_LIST PendList; /* List of tasks waiting on message queue */
OS_MSG_Q MsgQ; /* List of messages */
};
typedef struct os_q OS_Q;
在应用程序中创建消息队列的时候,就是要定义这样的一个结构体变量:
举例创建过程如下:
OS_Q taskq;
void main()
{
OS_ERR err;
OSQCreate ((OS_Q *)&p_q,
(CPU_CHAR *)"my task Q",
(OS_MSG_QTY) 10,
(OS_ERR *)&err );
}
这样一个消息队列就创建好了。这里要注意:
OS_Q taskq;这句话应该是全局变量,因为通常都要在其他函数中访问。
有时不注意,很容易依照OSQCreate 的参数创建成这样的队列变量:
OS_Q * taskq;注意这样创建的只是个指针,并没有实体,这样的定义在OSQCreate中参数传入时不会出错,但一运行就会进入hard fault,因为指针跑飞了。
结构体OS_Q的基本信息就是这么多。应当注意的是最后两个成员:
OS_PEND_LIST PendList;
OS_MSG_Q MsgQ;
能看出来,这两个成员又是结构体,一个是OS_PEND_LIST类型,一个是OS_MSG_Q类型。
这两个数据结构是消息队列的核心,只有掌握它们,才能真正了解消息队列的来龙去脉。
首先看一下OS_PEND_LIST,顾名思义,就是所创建的消息队列taskq下面的等待列表。等待者是谁?就是那些调用了OSQPend(&taskq...)的阻塞任务。那这两者之间又是怎么样联系在一起的呢?先告诉你,比较复杂,并不是直接连接在一起的,而是又调用了一个中间结构叫OS_PEND_DATA 。下面先看一下OS_PEND_LIST结构:
struct os_pend_list {
OS_PEND_DATA *HeadPtr;
OS_PEND_DATA *TailPtr;
OS_OBJ_QTY NbrEntries;
};
可见,这个结构又是比较“简单”的:两个指向OS_PEND_DATA的指针,一个指向头,一个指向尾,还有就是计数NbrEntries,记录的是有多少个OS_PEND_DATA。这样的设计是很明显的,典型的一个链表。正是这个链表,将一连串的OS_PEND_DATA链接起来,挂在每个消息队列下边,而每个OS_PEND_DATA里记录的正是等待该消息队列的任务TCB。同时,在该任务TCB中也有指针反向记录着对应的OS_PEND_DATA。下面就仔细看一下OS_PEND_DATA结构,这个分支就到头了,再没有其他结构了:
struct os_pend_data {
OS_PEND_DATA *PrevPtr; /*指向链表中的上一个OS_PEND_DATA */
OS_PEND_DATA *NextPtr; /*指向链表中的下一个OS_PEND_DATA */
OS_TCB *TCBPtr; /*指向等待该队列的任务TCB*/
OS_PEND_OBJ *PendObjPtr; /*反向指着调用它的内核对象(是队列或者信号量)*/
/*以下仅供MultiPend时使用*/
OS_PEND_OBJ *RdyObjPtr;
OS_MSG_SIZE RdyMsgSize;
CPU_TS RdyTS;
};
除了仅供MultiPend时使用的成员,前四个成员很正常,作用一目了然,双向链表,直接指向了等待的任务TCB,不多分析了。另外多说一句,OS_PEND_DATA是在任务调用OSQPend时自动定义的一个变量,这与MultiPend调用略有不同,在MultiPend中等待多内核对象时,OS_PEND_DATA是手动分配的。两种方式中OS_PEND_DATA占用的都是任务自已的堆栈,要注意防止栈溢出。
这样等待该消息队列的“任务挂起表”数据结构就分析完了,主线如下:
OS_Q->OS_PEND_LIST<->OS_PEND_DATA <-> 任务TCB
正是这样的一套数据结构,实现了队列Q和等待它的TCB之间的连接。
题外话,OS_PEND_DATA个人认为它的出现纯粹是uC/OSIII为了实现MultiPend统一入口的作用(因为MultiPend要求任务也可以同时等待信号量),不然直接把 TCB挂在OS_PEND_LIST下面,本是一件多么清爽的事情。

下面再看消息队列OS_Q成员中的另一大结构分支:OS_MSG_Q,它的作用是以队列的形式管理消息。这也正是消息队列名称的由来。既有任务等待列表,又有消息存储列表,这样才构成了完整的消息队列结构。
OS_MSG_Q的结构定义如下:
struct os_msg_q { /* OS_MSG_Q */
OS_MSG *InPtr; /* 将要存储进来的消息 */
OS_MSG *OutPtr; /* 下一个要被推送的消息 */
OS_MSG_QTY NbrEntriesSize; /*允许存储的消息数量*/
OS_MSG_QTY NbrEntries; /* 当前有多少条消息 */
OS_MSG_QTY NbrEntriesMax; /* 最多时达到过多少条消息 */
};
可以认为,OS_MSG_Q就是消息队列OS_Q的管家,掌管着消息队列中的全部消息的你来我往。这个管家有权利指派下一个消息被存储在哪里,以及哪个消息将要被推送出去,场景就像排队买火车票时那个售票员,它有权利让你插队。同时OS_MSG_Q会完全按照主人OS_Q中定义的消息最多数量进行消息队列管理,这又像排队买火车票时那个售票员会突然对你大喊“我要下班了,你们后面的都不要排队了”一样。
可见,对于消息而言,OS_MSG_Q是掌握其命运的,OS_MSG_Q结构里的OS_MSG结构就是代表的这些消息。OS_MSG结构作为消息就需要有实体变量的,这些实体变量是在uCOSIII系统初始化时被定义,并且被永久的定义在那里,默认值为50个,在ucosiii/source文件夹的os_app_cfg.h文件里:
#define OS_CFG_MSG_POOL_SIZE 50u
在初始化的50个OS_MSG变量,由OS_MSG_POOL OSMsgPool来管理,它也是个管家,专门管理“没过门的丫头”,过了门的小姐才交由各自OS_Q的OS_MSG_Q来管理了,用完后OS_MSG_Q会把她们再踢回给OS_MSG_POOL。
那消息的模样究竟如何?下面就看一下消息的结构OS_MSG:
struct os_msg { /* MESSAGE CONTROL BLOCK */
OS_MSG *NextPtr; /* 指向下一条消息 */
void *MsgPtr; /* 消息真身 */
O

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇POJ 1265 多边形格点数Pick公式 下一篇2013级C++第4周(春)项目――再和..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: