消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。
消息队列和管道和FIFO有很大的区别,主要有以下两点:
一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,
而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必需已经打开来读,
那么内核会产生SIGPIPE信号。
IPC的持续性不同。管道和FIFO是随进程的持续性,当管道和FIFO最后一次关闭发生时,
写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核
没有重新自举,消息队列没有被删除。
消息队列中的每条消息通常具有以下属性:
一个表示优先级的整数;
消息的数据部分的长度;
消息数据本身;
POSIX消息队列的一个可能的设计是一个如下图所示的消息链表,链表头部有消息队列的属性信息。
POSIX消息队列的创建,关闭和删除用到以下三个函数接口:
#include
mqd_t mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */);
//成功返回消息队列描述符,失败返回-1
mq_open用于打开或创建一个消息队列。
name:表示消息队列的名字,即将要被打开或创建的消息队列对象,其所创建的真正路径名和具体的系统实现有关。为了便于移植,需要指定为“/name”的格式。
oflag:表示打开的方式,和open函数的类似。有必须的选项:O_RDONLY,O_WRONLY,O_RDWR,还有可选的选项:O_NONBLOCK,O_CREAT,O_EXCL。
mode:是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时,才需要提供该参数。表示默认访问权限。可以参考open。
attr:也是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时才需要。该参数用于给新队列设定某些属性,如果是空指针,那么就采用默认属性。
mq_open返回值是mqd_t类型的值,被称为消息队列描述符。
mqd_t mq_close(mqd_t mqdes);
mq_close用于关闭一个消息队列,和文件的close相似,关闭后,消息队列并不从系统中删除。一个进程结束,会自动调用关闭打开着的消息队列。
mqd_t mq_unlink(const char *name);//成功返回0,失败返回-1
mq_unlink用于删除一个消息队列。消息队列创建后只有通过调用该函数或者是内核自举才能进行删除。每个消息队列都有一个保存当前打开着描述符数的引用计数器,和文件一样,因此本函数能够实现类似于unlink函数删除一个文件的机制。
POSIX标准规定消息队列属性mq_attr必须要含有以下四个内容:
long mq_flags //消息队列的标志:0或O_NONBLOCK,用来表示是否阻塞
long mq_maxmsg //消息队列的最大消息数
long mq_msgsize //消息队列中每个消息的最大字节数
long mq_curmsgs //消息队列中当前的消息数目
在Linux 2.6.18中mq_attr结构的定义如下:
#include
struct mq_attr
{
long int mq_flags; /* Message queue flags. */
long int mq_maxmsg; /* Maximum number of messages. */
long int mq_msgsize; /* Maximum message size. */
long int mq_curmsgs; /* Number of messages currently queued. */
long int __pad[4];
};
POSIX消息队列的属性设置和获取可以通过下面两个函数实现:
#include
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr,struct mq_attr *oldattr);
//成功返回0,失败返回-1
mq_getattr用于获取当前消息队列的属性,mq_setattr用于设置当前消息队列的属性。其中mq_setattr中的oldattr用于保存修改前的消息队列的属性,可以为空。
mq_setattr可以设置的属性只有mq_flags,用来设置或清除消息队列的非阻塞标志。mq_attr结构的其他属性被忽略。mq_maxmsg和mq_msgsize属性只能在创建消息队列时通过mq_open来设置。mq_open只会设置该两个属性,忽略另外两个属性。mq_curmsgs属性只能被获取而不能被设置。
#include
#include
#include
#include
#include
int main()
{
mqd_t mqID;
mqID = mq_open("/test_mq", O_RDWR | O_CREAT, 0666, NULL);
if (mqID < 0)
{
printf("open message queue error...\n");
return -1;
}
struct mq_attr mqAttr;
if (mq_getattr(mqID, &mqAttr) < 0)
{
printf("open message queue error\n");
return -1;
}
printf("mq_flags:%d\n",mqAttr.mq_flags);
printf("mq_maxmsg:%d\n",mqAttr.mq_maxmsg);
printf("mq_msgsize:%d\n",mqAttr.mq_msgsize);
printf("mq_curmsgs:%d\n",mqAttr.mq_curmsgs);
}
POSIX消息队列可以通过以下函数来进行发送和接收消息:
#include
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned msg_prio);
//成功返回0,出错返回-1
mqd_t mq_receive(mqd_t mqdes, char *msg_ptr,size_t msg_len,unsigned *msg_prio);
//成功返回接收到消息的字节数,出错返回-1
mq_send向消息队列中写入一条消息,mq_receive从消息队列中读取一条消息。
mqdes:消息队列描述符;
msg_ptr:指向消息体缓冲区的指针;
msg_len:消息体的长度。
其中mq_receive的该参数不能小于能写入队列中消息的最大大小,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。如果mq_receive中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度可以为0。
msg_prio:消息的优先级;
它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send中置msg_prio为0,mq_receive的msg_prio置为NULL。
如果消息队列已满,mq_send()函数将阻塞,直到队列有可用空间再次允许放置消息或该调用被信号打断;如果O_NONBLOCK被指定,mq_send()那么将不会阻塞,而是返回EAGAIN错误。如果队列空,mq_receive()函数将阻塞,直到消息队列中有新的消息;如果O_NONBLOCK被指定,mq_receive()那么将不会阻塞,而是返回EAGAIN错误。
#include
#include
#include
#include
#include
#include
int main()
{
mqd_t mqID;
int i = 0;
char msg[] = "test_string";
mqID = mq_open("/test_mq", O_RDWR | O_CREAT | O_EXCL, 0666, NULL);
if (mqID < 0)
{
if (errno == EEXIST)
{
mq_unlink("/test_mq");
mqID = mq_open("/test_mq", O_RDWR | O_CREAT, 0666, NULL);
}
else
{
printf("open message queue error...\n");
return -1;
}
}
if (fork() == 0)
{
struct mq_attr mqAttr;
mq_getattr(mqID, &mqAttr);
char *buf = (char*) malloc(sizeof(mqAttr.mq_msgsize));
for (i = 1; i < 5; ++i)
{
if (mq_receive(mqID, buf, mqAttr.mq_msgsize, NULL) < 0)
{
printf("receive message failed.\n");
continue;
}
printf("receive message:%d\t%s\n",i,buf);
}
exit(0);
free(buf);
}
for (i = 1; i < 5; ++i)
{
if (mq_send(mqID, msg, sizeof(msg), i) < 0)
{
printf("send message:%d failed\n",i);
}
printf("send message %d success.\n",i);
sleep(1);
}
}
参考:
《UNIX网络编程卷二 》第五章 posix消息队列
http://blog.csdn.net/anonymalias/article/details/9799645