消息队列相关设计架构
消息队列使用方式
创建消息队列:
OS_Q queue; //声明消息队列
/* 创建消息队列 queue */
OSQCreate ((OS_Q *)&queue, //指向消息队列的指针
(CPU_CHAR *)"Queue For Test", //队列的名字
(OS_MSG_QTY )20, //最多可存放消息的数目
(OS_ERR *)&err); //返回错误类型
发布消息队列:
/* 发布消息到消息队列 queue */
OSQPost ((OS_Q *)&queue, //消息变量指针
(void *)"Fire uC/OS-123", //要发送的数据的指针,将内存块首地址通过队列“发送出去”
(OS_MSG_SIZE )sizeof ( "Fire uC/OS-123" ), //数据字节大小
(OS_OPT )OS_OPT_POST_FIFO | OS_OPT_POST_ALL, //先进先出和发布给全部任务的形式
(OS_ERR *)&err); //返回错误类型
其中的(void *)"Fire uC/OS-123"
表示指向字符串的指针。
请求消息:
/* 请求消息队列 queue 的消息 */
pMsg = OSQPend ((OS_Q *)&queue, //消息变量指针
(OS_TICK )0, //等待时长为无限
(OS_OPT )OS_OPT_PEND_BLOCKING, //如果没有获取到信号量就等待
(OS_MSG_SIZE *)&msg_size, //获取消息的字节大小
(CPU_TS *)0, //获取任务发送时的时间戳
(OS_ERR *)&err); //返回错误
printf ( "\r\n接收消息的长度:%d字节,内容:%s\r\n", msg_size, pMsg );
架构分析
声明的消息队列OS_Q
的结构体内容如下:
其中包括PendList
和MsgQ
两个重要的结构体。
PendList
用于在某个任务想要从OS_Q
中获取消息消息时,如果OS_Q
为空,则该任务被插入到PendList
中排队,等待OS_Q
中消息的到来;当某个任务想要向OS_Q
发布消息时,如果看到这个OS_Q
的PendList
中有任务在排队等待,则直接把消息给正在排队的任务。
MsgQ
用于在某个任务想要向OS_Q
发布消息时,如果当前没有任务在这个OS_Q
上排队等消息,那么它就会把消息放到OS_Q
的MsgQ
中;当某个任务想要从OS_Q
获取消息时,如果它发现OS_Q
的MsgQ
中有消息,那么就直接从MsgQ
中取走消息。
整个过程类似于学生去食堂买饭,食堂就是OS_Q
,学生排队区就是PendList
,食品摆放区就是MsgQ
,当一个学生来到看食品摆放区有食物时,他就直接拿走,没有就去排队区排队,当上饭阿姨上饭时发现有学生正在排队,阿姨就直接把饭给他,如果没有学生排队就把饭放在食品摆放区,等学生来拿。
ucosiii中消息队列的代码实现在os_q.c
文件中,以发布消息为例,函数调用过程如下:
消息队列发布消息被分成3层,OSQPost()
用于用户调用,主要对用户传入参数的正确性进行判断,OS_QPost()
用于判断把消息放到MsgQ
中还是直接给PendList
中正在等待消息的任务,OS_MsgQPut()
用于从消息池中取出一个消息,把他包装一下(设置一下消息指针、消息大小、消息时间戳)放到MsgQ
,OS_Post()
用于把消息给在PendList
中等待的任务,并且将其从PendList
中移除、唤醒。
与MsgQ
有关的操作函数都封装在os_msg.c
中,其中包括以下函数:
OS_MsgPoolInit()
:初始化消息池;
OS_MsgQFreeAll()
:把一个MsgQ
上的所有消息放回到消息池中;
OS_MsgQInit()
:初始化MsgQ
;
OS_MsgQGet
:从MsgQ
中获取一个消息;
OS_MsgQPut
:把一个消息放到MsgQ
中。
编程技巧
1、os_msg.c
中维护一个消息池和所有的MsgQ
,消息池把空闲消息用链表串起来,MsgQ
把一个OS_Q
中正在使用的消息用链表串起来,如果在自己的项目中有类似的资源管理方式需求,可以参考os_msg.c
,对资源进行申请和回收。
2、消息池结构体如下:
struct os_msg_pool { /* OS_MSG POOL */
OS_MSG *NextPtr; /* Pointer to next message */
OS_MSG_QTY NbrFree; /* Number of messages available from this pool */
OS_MSG_QTY NbrUsed; /* Current number of messages used */
OS_MSG_QTY NbrUsedMax; /* Peak number of messages used */
};
这种以Nbr开头的变量表示某种数量的形式可以参考;
消息结构体如下:
struct os_msg { /* MESSAGE CONTROL BLOCK */
OS_MSG *NextPtr; /* Pointer to next message */
void *MsgPtr; /* Actual message */
OS_MSG_SIZE MsgSize; /* Size of the message (in # bytes) */
CPU_TS MsgTS; /* Time stamp of when message was sent */
};
TS表示时间(time stamp),这种变量命名可以参考。