消息队列的使用
消息队列工作机制
消息队列是RT-Thread中另一种常用的线程间通信方式,消息队列是对邮箱的扩展。
消息队列能够接收来自线程或中断服务例程中发出的不固定长度的消息,并把消息缓存在自己的内存空间中,而其他线程能够从消息队列中读取相应的消息并进行对应的处理。
如下图:
线程和中断服务例程通过消息队列控制块发送消息到消息队列控制块中空闲链表指向的内存空间。对于消息队列控制块,它有一个消息链表头指向消息队列的第一条消息,同时还有一个消息链表尾指向消息队列的最后一条消息。接收消息的线程在等待线程队列中等待去接收消息。
消息队列控制块
在RT-Thread中,消息队列控制块是操作系统用于管理消息队列的一个数据结构。
/* 在rtdef.h中对结构体的定义 */
#ifdef RT_USING_MESSAGEQUEUE
/**
* message queue structure
*/
struct rt_messagequeue
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
//消息队列缓冲区地址
void *msg_pool; /**< start address of message queue */
//消息的大小 由于#define RT_ALIGN_SIZE 4,msg_size为4的整数倍,不满足时自动调整
rt_uint16_t msg_size; /**< message size of each message */
//最大的消息数 计算公式:max_msgs = msg_pool大小 / (msg_size(4的倍数) + 4(指针大小))
rt_uint16_t max_msgs; /**< max number of messages */
//消息队列中消息的数目
rt_uint16_t entry; /**< index of messages in the queue */
//消息链表头 消息链表尾 空闲链表
void *msg_queue_head; /**< list head */
void *msg_queue_tail; /**< list tail */
void *msg_queue_free; /**< pointer indicated the free node of queue */
};
typedef struct rt_messagequeue *rt_mq_t;
#endif
struct rt_messagequeue static_mq //定义静态消息队列
rt_mq_t dynamic_mq //定义动态信号量
消息队列的操作
初始化与脱离
//用于静态消息队列
rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size,
rt_uint8_t flag)
/*
*rt_uint8_t flag的选择:
* RT_IPC_FLAG_FIFO:先进先出顺序
* RT_IPC_FLAG_PRIO:优先级顺序
*/
rt_err_t rt_mq_detach(rt_mq_t mq)
创建和删除
//用于动态消息队列
rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag)
rt_err_t rt_mq_delete(rt_mq_t mq)
发送消息
rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size);
rt_err_t rt_mb_urgent(rt_mq_t mq, void *buffer, rt_size_t size)
/*
*发送消息 二者差别在于放于消息队列中的位置
*rt_mq_send发送消息放于消息队列队尾,rt_mb_urgent发送消息放于消息队列队头
*/
接收消息
rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout)
/* 接收消息 */
小例
代码
/*
* 程序清单:消息队列例程
*
* 这个程序会创建2个动态线程,一个线程会从消息队列中收取消息;一个线程会定时给消
* 息队列发送 普通消息和紧急消息。
*/
#include <rtthread.h>
/* 消息队列控制块 */
static struct rt_messagequeue mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t msg_pool[2048];
ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
/* 线程1入口函数 */
static void thread1_entry(void *parameter)
{
char buf = 0;
rt_uint8_t cnt = 0;
while (1)
{
/* 从消息队列中接收消息 一直等待 */
if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) == RT_EOK)
{
rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf);
/* 打印19次就退出循环 */
if (cnt == 19)
{
break;
}
}
/* 延时50ms */
cnt++;
rt_thread_mdelay(50);
}
rt_kprintf("thread1: detach mq \n");
rt_mq_detach(&mq);
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程2入口 */
static void thread2_entry(void *parameter)
{
int result;
char buf = 'A';
rt_uint8_t cnt = 0;
while (1)
{
//第9次发送一个紧急消息
if (cnt == 8)
{
/* 发送紧急消息到消息队列中 */
result = rt_mq_urgent(&mq, &buf, 1); //1 自动变为 4
if (result != RT_EOK)
{
rt_kprintf("rt_mq_urgent ERR\n");
}
else
{
rt_kprintf("thread2: send urgent message - %c\n", buf);
}
}
else if (cnt >= 20)/* 发送20次消息之后退出 */
{
rt_kprintf("message queue stop send, thread2 quit\n");
break;
}
else
{
/* 发送消息到消息队列中 */
result = rt_mq_send(&mq, &buf, 1);
if (result != RT_EOK)
{
rt_kprintf("rt_mq_send ERR\n");
}
rt_kprintf("thread2: send message - %c\n", buf);
}
buf++;
cnt++;
/* 延时5ms 发送快于接收 */
rt_thread_mdelay(5);
}
}
/* 消息队列示例的初始化 */
int msgq_sample(void)
{
rt_err_t result;
/* 初始化消息队列 */
result = rt_mq_init(&mq,
"mqt",
&msg_pool[0], /* 内存池指向msg_pool */
1, /* 每个消息的大小是 1(4) 字节 */
sizeof(msg_pool), /* 内存池的大小是msg_pool的大小 */
RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
if (result != RT_EOK)
{
rt_kprintf("init message queue failed.\n");
return -1;
}
/* 时间片轮询调度 */
rt_thread_init(&thread1,
"thread1",
thread1_entry,
RT_NULL,
&thread1_stack[0],
sizeof(thread1_stack), 25, 5);
rt_thread_startup(&thread1);
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack), 25, 5);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(msgq_sample, msgq sample);