1.什么是消息队列?消息队列提供了一种将数据块从一个进程发送到另一个进程的方法。每个数据块都被认为有一个类型,接收进程可以独立地接收包含不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列和命名管道一样,每个数据块都有最大长度限制。Linux使用宏MSGMAX和MSGMNB来限制消息的最大长度和队列的最大长度。2、在Linux中使用消息队列Linux为消息队列提供了一系列的函数接口,让我们可以方便的使用它来实现进程间通信。它的用法类似于另外两种SystemVPIC机制,信号量和共享内存。1.msgget函数这个函数用来创建和访问一个消息队列。它的原型是:intmsgget(key_t,key,intmsgflg);与其他IPC机制一样,程序必须提供一个键来命名特定的消息队列。msgflg是权限标志,表示消息队列的访问权限,与文件的访问权限相同。msgflg可以和IPC_CREAT进行OR运算,表示当key命名的消息队列不存在时,创建一个消息队列。如果key命名的消息队列存在,IPC_CREAT标志将被忽略,只返回一个标识符。它返回一个由key命名的消息队列的标识符(非零整数),失败时返回-1.2。msgsnd函数用于向消息队列添加消息。其原型为:intmsgsend(intmsgid,constvoid*msg_ptr,size_tmsg_sz,intmsgflg);msgid是msgget函数返回的消息队列标识符。msg_ptr是一个指向要发送的消息的指针,但是消息的数据结构有一定的要求。指针msg_ptr指向的消息结构必须以一个长整型成员变量开头,接收函数会通过这个成员来判断消息的类型。所以消息结构应该这样定义:structmy_message{longintmessage_type;/*Thedatayouwishtotransfer*/};msg_sz是msg_ptr指向的消息的长度,注意是消息的长度,不是整个结构体的长度,也就是说msg_sz不包括length整型消息类型成员变量的长度.msgflg用于控制当前消息队列已满或队列消息达到系统范围限制时会发生什么。如果调用成功,会在消息队列中放入一份消息数据并返回0,失败则返回-1.3。msgrcv函数用于从消息队列中获取消息。它的原型是intmsgrcv(intmsgid,void*msg_ptr,size_tmsg_st,longintmsgtype,intmsgflg);msgid、msg_ptr、msg_st函数与msgsnd函数相同。msgtype可以实现一个简单的接收优先级。如果msgtype为0,则获取队列中的第一条消息。如果其值大于零,则将获取具有相同消息类型的第一条消息。如果小于零,则获取类型等于或小于msgtype的绝对值的第一条消息。msgflg用于控制当队列中没有相应类型的消息可接收时会发生什么。当调用成功时,函数返回放置在接收缓冲区中的字节数,将消息复制到msg_ptr指向的用户分配缓冲区,然后删除消息队列中相应的消息。如果失败,则返回-1.4。msgctl函数用于控制消息队列。类似于共享内存的shmctl函数。其原型为:intmsgctl(intmsgid,intcommand,structmsgid_ds*buf);command是要采取的Action,可以取3个值,IPC_STAT:设置msgid_ds结构体中的数据为消息队列当前关联值,即用消息队列当前关联值覆盖msgid_ds的值。IPC_SET:如果进程有足够的权限,将消息队列当前关联值设置为msgid_ds结构中给定的值IPC_RMID:删除消息队列buf是一个指向msgid_ds结构的指针,指向消息队列的结构模式和访问权限。msgid_ds结构至少包括以下成员:structmsgid_ds{uid_tshm_perm.uid;uid_tshm_perm.gid;mode_tshm_perm.mode;};成功返回0,失败返回-1。在定义和可用的接口之后,让我们看看它如何允许进程进行通信。由于不相关的进程可以通信,我们这里写两个程序msgreceive和msgsned来表示接收和发送信息。按照正常情况,我们允许两个程序都创建消息,但只有接收方收到最后一条消息后,才会将其删除。接收信息的程序源文件为msgreceive.c,源代码为:#include#include#include#include#include#includestructmsg_st{longintmsg_type;chartext[BUFSIZ];};intmain(){intrunning=1;intmsgid=-1;structmsg_stdata;longintmsgtype=0;//注意1//构建消息队列msgid=msgget((key_t)1234,0666|IPC_CREAT);if(msgid==-1){fprintf(stderr,"msggetfailedwitherror:%d\n",errno);exit(EXIT_FAILURE);}//获取消息从队列中,直到遇到结束消息while(running){if(msgrcv(msgid,(void*)&data,BUFSIZ,msgtype,0)==-1){fprintf(stderr,"msgrcvfailedwitherrno:%d\n",errno);exit(EXIT_FAILURE);}printf("你写了:%s\n",data.text);//结束if(strncmp(data.text,"end",3)==0)running=0;}//删除消息队列if(msgctl(msgid,IPC_RMID,0)==-1){fprintf(stderr,"msgctl(IPC_RMID)failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}发送信息的程序的源文件msgsend.c的源代码为:#include#include#include#include<string.h>#include#include#defineMAX_TEXT512structmsg_st{longintmsg_type;chartext[MAX_TEXT];};intmain(){intrunning=1;structmsg_stdata;charbuffer[BUFSIZ];intmsgid=-1;//创建消息队列msgid=msgget((key_t)1234,0666|IPC_CREAT);if(msgid==-1){fprintf(stderr,"msggetfailedwitherror:%d\n",errno);exit(EXIT_FAILURE);}//向消息队列写入消息直到endwhile(running){//输入数据printf("Entersometext:");fgets(buffer,BUFSIZ,stdin);data.msg_type=1;//注2strcpy(data.text,buffer);//发送数据到队列if(msgsnd(msgid,(void*)&data,MAX_TEXT,0)==-1){fprintf(stderr,"msgsndfailed\n");exit(EXIT_FAILURE);}//进入端到端输入if(strncmp(buffer,"end",3)==0)running=0;sleep(1);}exit(EXIT_SUCCESS);}运行结果如下:4.实例分析——消息类型这里主要解释一下消息类型是什么。注意msgreceive.c文件的main函数中定义的变量msgtype(注1)。为0,表示获取队列中第一个可用的消息。我们看一下msgsend.c文件中while循环中的语句data.msg_type=1(注为注2),用来设置发送信息的信息类型。即它发送的信息类型是1。所以程序msgreceive可以接收到程序msgsend发送的信息。如果更改注1,即msgreceive.c文件的main函数中的语句fromlongintmsgtype=0;到longintmsgtype=2;会发生什么,msgreceive将无法接收程序msgsend发送的信息。因为在调用msgrcv函数时,如果msgtype(第四个参数)大于0,则只会获取第一条相同消息类型的消息,修改后的消息类型为2,msgsend发送的消息类型为1,所以它不能被msgreceive程序接收。重新编译msgreceive.c文件,再次执行,结果如下:我们可以看到msgreceive还没有接收到信息和输出,而当msgsend输入结束的时候,msgreceive还没有结束,可以看到它还在workingthrough在后台运行的作业命令。5、消息队列与命名管道的比较消息队列与命名管道有很多相似之处。和命名管道一样,消息队列的通信过程可以是一个无关的过程,都是发送和接收。传输数据的方式。在命名管道中,write用于发送数据,read用于接收数据。在消息队列中,msgsnd用于发送数据,msgrcv用于接收数据。并且它们对每条数据都有最大长度限制。与命名管道相比,消息队列的优点是:1、消息队列也可以独立于发送进程和接收进程而存在,从而消除了同步开启和关闭命名管道时可能出现的困难。2、同时也可以通过发送消息的方式避免命名管道的同步和阻塞问题,不需要进程自己提供同步方法。3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道那样默认只接收。