消息队列可看成是一个消息链表,有足够写权限的线程可往队列中放置消息,有足够读权限的线程可从队列中取走消息。
Posix消息队列与System V消息队列的主要区别:
队列中每个消息的属性:
由上图可知,在消息队列的链表头中保存有队列中允许的最大消息数(mq_maxmsg)以及每个消息的最大大小(mq_msgsize),其后以链表形式连接每个消息。
创建:
#include
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr);
name为IPC对象名称,oflag参数是O_RDONLY, O_WRONLY或O_RDWR之一,可能按位或上O_CREAT、O_EXCL或O_NONBLOCK。mode和mq_attr在指定O_CREAT标志时需要。
mode常值有:
常值 | 说明 |
S_IRUSR | 用户(属主)读 |
S_IWUSR | 用户(属主)写 |
S_IRGRP | 属组成员读 |
S_IWGRP | 属组成员写 |
S_IROTH | 其他用户读 |
S_IWOTH | 其他用户写 |
mq_attr结构体成员:
struct mq_attr
{
long mq_flags;//消息队列标志:0,O_NONBLOCK
long mq_maxmsg;//队列中最大允许消息数
long mq_msgsize;//队列中消息的最大大小
long mq_curmsgs;//队列中现有消息数
};
返回值:返回一个消息队列描述符,这个值用作其余消息队列函数的第一个参数。
关闭:
#include
int mq_close(mqd_t mqdes);
成功返回0,失败返回-1.
创建一个消息队列:
#include
int
main(int argc, char **argv)
{
int c,flags;
mqd_t mqd;
flags=O_RDWR|O_CREAT;
while ((c=Getopt(argc,argv,"em:z:"))!=-1) {//获取选项内容
switch (c) {
case 'e':
flags|=O_EXCL;
break;
case 'm':
attr.mq_maxmsg=atol(optarg);
break;
case 'z':
attr.mq_msgsize=atol(optarg);
break;
}
}
if(optind!=argc-1)
err_quit("usage: mqcreate [-e] [-m maxmsg -z msgsize] ");
if((attr.mq_maxmsg!=0&&attr.mq_msgsize==0||
attr.mq_maxmsg==0&&attr.mq_msgsize!=0))
err_quit("must specify both -m maxmsg and -z msgsize");//最大消息数和每个消息的最大大小需同时指定
mqd=Mq_open(argv[optind],flags,FILE_MODE,
(attr.mq_maxmsg!=0)?&attr:NULL);//创建一个消息队列
Mq_close(mqd);//mq_close也会将队列的引用计数减1,此时,引用计数变为1
exit(0);
}
删除:
#include
int mq_unlink(const char *name);
mq_close()和mq_unlink()都会使当前消息队列的引用计数减一,若变为0则附带拆除该队列。在Ubuntu系统中可以看到创建在/dev/mqueue处的消息队列文件name被删除。
#include "unpipc.h"
int
main(int argc, char **argv)
{
if(argc!=2)
err_quit("usage: mqunlink ");
Mq_unlink(argv[1]);//当引用计数变为0时,会实际删除消息队列文件name
exit(0);
}
设置或获取消息队列属性:
#include
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);
mq_getattr()将所指定队列的当前属性回填到attr中,成功返回0,失败返回-1;
mq_setattr()给所指定队列设置属性,但只能修改mq_attr结构中的mq_flags以设置或清除非阻塞标志,其他三个成员忽略。若oattr非空,先前的属性回填到oattr中。成功返回0,失败返回-1.
往队列发送或取走消息:
#include
int mq_send(mqd_t mqdes,const char *ptr,size_t len,unsigned int prio);
ssize_t mq_receive(mqd_t mqdes,char *ptr,size_t len,unsigned int *priop);
mqdes:目的地消息队列的描述符;
ptr:消息数据指针;
len:消息长度;
prio:消息优先级,需小于MQ_PRIO_MAX;
priop:消息优先级指针,若不指定则为NULL。
发送消息步骤:先调用mq_open()函数打开指定队列文件并获得队列文件的文件描述符,mq_send()函数将消息写入。
取消息步骤:先调用mq_open()函数打开指定队列文件并获得队列文件的文件描述符,使用mq_getattr()函数获得消息大小,使用mq_receive()获得从队列中取消息。可加参数-n指定非阻塞属性。
#include"unpipc.h"
int
main(int argc, char **argv)
{
mqd_t mqd;
void *ptr;
size_t len;
uint_t prio;
if(argc!=4)
err_quit("usage: mqsend <#bytes> ");
len=atol(argv[2]);
prio=atol(argv[3]);
mqd=Mq_open(argv[1],O_RDONLY);//只读形式打开队列文件并获得相应的队列描述符
ptr=Calloc(len,sizeof(char));
Mq_send(mqd,ptr,len,prio);//发送消息
exit(0);
}
int
main(int argc, char **argv)
{
int c,flags;
mqd_t mqd;
ssize_t n;
uint_t prio;
void *buff;
struct mq_attr attr;
flags=O_RDONLY;
while((c=Getopt(argc,argv,"n"))!=-1)
{
switch (c) {
case 'n':
flags|=O_NONBLOCK;
break;
}
}
if(optind!=argc-1)
err_quit("usage:mqreceive [-n] ");
mqd=Mq_open(argv[optind],flags);//打开名字为name的队列文件并获取其队列描述符
Mq_getattr(mqd,&attr);//获取队列属性
buff=Malloc(attr.mq_msgsize);
n=Mq_receive(mqd,buff,attr.mq_msgsize,&prio);//取走对应prio的消息
printf("read %ld bytes, priority = %u\n",(long)n,prio);
exit(0);
}
消息队列的限制:
mq_mqxmsg:队列中的最大消息数;
mq_msgsize:给定消息的最大字节数;
MQ_OPEN_MAX:一个进程能够同时拥有的打开着消息队列数目(Posix最小为8);
MQ_PRIO_MAX:任意消息的最大优先级加1(Posix最小为32)。