一、概述
在linux应用开发中,消息队列的应用场景很普遍,比如最常见得 生产 -- 消费模型,一方产生数据,并把数据放入队列中,而另一方从队列中取数据。linux中的消息队列的主要用途为进程间通信,当然,也可以进行 “线程间通信”。
我们可以简单的理解为:
(1)创建了一个消息队列,相当于在内核层面,创建了一个链表,这也就意味着这个消息队列,在不主动删除的情况下,会一直存在,即便是创建它的进程退出后,该消息队列仍然存在。消息队列在内存中可能的布局如下:
(2)消息队列 相当于是“全局”的,我们知道要知道消息队列的名字 或者 id,那么任何进程或线程都是可以访问该消息队列的,这也是为什么可以通过消息队列实现IPC。
在linux中,消息队列有两种,分别为Posix消息队列 和 System V,这里不详述这两者的区别,只简单的提一下,System V是内核级别的 老版本 消息队列,而Posix是标准接口的消息队列,Posix 消息队列应该也是基于System V 实现的,所以这里重点说 Posix消息队列。
二、Posix消息队列 API
#include <mqueue.h>
// 打开或创建一个消息队列, name 必须是 "/" 开头,而且只能有一个 /
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode,
struct mq_attr *attr);
// 向消息队列 mqdes中发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio);
// 删除 消息队列
int mq_unlink(const char *name);
// 从消息队列中接收消息
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
// 关闭消息队列, 注意:关闭消息队列,并不会删除该消息队列
int mq_close(mqd_t mqdes);
// 每个消息队列有4个属性, 消息状态标志(阻塞...)|消息最多条数|每条消息的最大长度|当前消息cur
// 获取消息队列属性
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
// 设置消息队列属性
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);
三、代码示例
1、创建 Posix消息队列
#include <mqueue.h>
static mqd_t mqd;
int create_mqueue(void)
{
struct mq_attr attr;
attr.mq_flags = 0;
attr.mq_maxmsg = 10; // 最多10条消息
attr.mq_msgsize = 100; // 每条消息长度最大 100byte
attr.mq_curmsgs = 0;
// 读写|创建 消息队列,名字为 "/my_queue", name 必须是 "/" 开头,而且只能有一个'/', 规定
mqd = mq_open("/my_queue", O_RDWR|O_CREAT, 0666, &attr);
if(mqd < 0){
printf("mq_open error:%d\n", mqd);
return -1;
}
return 0;
}
2、发送消息
int mqueue_send(char *msg, int len)
{
int rc;
mqd_t mq_cmd;
struct mq_attr attr;
attr.mq_flags = 0;
attr.mq_maxmsg = OTA_MQUEUE_MSG_MAX;
attr.mq_msgsize = OTA_MQUEUE_MSG_SIZE;
attr.mq_curmsgs = 0;
// 只写 的方式 打开或创建 消息队列
mq_cmd = mq_open("/my_queue", O_WRONLY|O_CREAT, 0666, &attr);
if(mq_cmd < 0){
printf("mq open error:%d\n", mq_cmd);
return -1;
}
rc = mq_send(mq_cmd, msg, len, 0);
if(rc < 0){
printf("mq send error:%d\n", rc);
return -1;
}
if(mq_close(mq_cmd) < 0){
printf("mq close error.\n");
return -1;
}
return 0;
}
3、接收消息
Posix 默认为 阻塞的,所以我们可以在一个线程中阻塞接收 消息,这样实时性更好一些:
int mqueue_receive(char *msg)
{
int rc;
if(mqd < 0){
printf("mqueue has no open.\n");
return -1;
}
rc = mq_receive(mqd, msg, 100, NULL);
if(rc < 0){
printf("mq_receive error: %d \n", rc);
}
return rc;
}
注意:我们在 使用 mq_receive时,参数 “消息长度”,可以设置成该消息队列属性的 “消息最大长度”,这样就能实现在未知消息多长的情况下,接收消息的所有内容,返回的就是 接收的消息 长度。
小结
1、Posix 消息队列使用起来还是比较方便的。
2、一个进程终止时,它所打开着的消息队列都会关闭,就像调用了mq_close一样,不过此时的时候,可能是自己的愿意,发下如果没有close,再次打开可能会异常,所以进行保持好习惯,随用随open,然后close