Linux进程间通信之消息队列作者:武汉华嵌嵌入式培训中心技术部在linux下有两种消息队列,一种是POSIX的消息队列,另外一种是SystemV消息队列。在这里只讲SystemV队列的使用。SystemV消息队列使用消息队列标识符标识,在某个进程往一个队列中写入一个消息之前,不求另外某个进程正在等待该队列上一个消息的到达。对于系统中每个消息队列,内核维护一个定义在sys/msg.h头文件中的信息结构。该结构如下:structmsqid_ds{structipc_permmsg_perm;/*read_writeperms:Section3.3*/structmsg*msg_first;/*ptrtofirstmessageonqueue*/structmsg*msg_last;/*ptrtolastmessageonqueue*/msglen_tmsg_cbytes;/*current#bytesonqueue*/……}消息队列由内核进行维护,我们可以将内核中某个选定的消息队列画为一个消息链表,如下图所示:以下都是操作消息队列的API函数,在这里将对其一一讲解。创建一个消息队列。#includesys/msg.hintmsgget(key_tkey,intoflag);该函数在执行正常返回时,返回的是一个整数标识符,其他三个函数就用它来指代该队列。该标识符是基于指定的key产生的,而key即可以是ftok的返回值,也可以是常值IPC_PRIVATE。oflag可以是读写权限的值的组合,它还可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。当创建一个新消息队列时,msqid_ds结构的一些成员被初始化。往一个打开的消息队列上放置一个消息。#includesys/msg.hintmsgsnd(intmsqid,constvoid*ptr,size_tlength,intflag);该函数的第一参数msqid是由msgget的返回值提供。Ptr是一个结构指针,该结构可以如下定义:structmy_msg{longtypes;chartext[MAX_LENGTH];}该结构中的types是这个消息的类型,text这个数组就是发送的消息的正文部份。msgsnd的length参数以字节为单位指定待发送消息的长度。如果待发送消息的结构如上定义,则length的取值是sizeof(structmy_msg)–sizeof(long)。第四个参数flag即可以是0,也可以是IPC_NOWAIT。当flag是0时,使得msgsnd调用阻塞,如果是IPC_NOWAIT,使得msgsnd调用非阻塞。使用msgrcv函数从某个消息队列中读出一个消息。#includesys/msg.hssize_tsmgrcv(intmsqid,void*ptr,size_tlength,longtype,intflag);其中ptr参数指定所接收消息的存放位置,和msgsnd一个,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段。Length指定了由ptr指向的缓冲区中数据部分的大小,这是该函数能返回的最大数据量,该长度不包含消息类型占的字节数。type指定希望从消息队列中读出什么类型的消息,type以下的几种取值有不同的效果,如下:⑴、type为0时,则返回队列中的第一个消息,也就是最早的那个消息。⑵、type为大于0时,则返回其类型值为type的第一个消息。⑶、type为小于0时,则返回其类型值小于或等于type参数的绝对值的消息中类型最小的第一个消息。flag的可能取值同msgsnd一样,代表着同样的意思。只不过msgrcv多了一个选择,那就是MSG_NOERROR,如果设置了该位,当所接收消息的真正数据部分大于length参数时,msgrcv只截短数据部分,面不返回错误。该函数成功返回时,msgrcv返回的是所接收消息中数据的字节数。它不包括消息类型所占有字节数。使用msgctl对消息队列进行控制。#includesys/msg.hintmsgctl(intmsqid,intcmd,structmsqid_ds*buff);该函数提供了三个控制命令,分别如下:IPC_RMID从系统中删除由msqid指定的消息队列,对于该命令,msgctl第三个参数被忽略。IPC_SET给所指定的消息队列设置其msqid_ds结构的几个成员。IPC_STAT给调用者返回与所指定消息队列对应的当前msqid_ds结构。总结:消息队列是由内核维护的,也就是说消息队列是随内核持续性的。在进程结束的时候,消息队列并消失,只有在显示的删除消息队列或者在内核自检的时候,消息队列才会删除。以下是使用消息队列例子:发送进程:#includestdio.h#includeerrno.h#includestring.h#includesys/ipc.h#includesys/msg.h#includesys/types.h#defineMAX_MSG_LENGTH1024//自己定义的消息格式typedefstructmsgbuf{longtypes;chartexts[MAX_MSG_LENGTH];}SYS_MSG;intmain(){intmsgid;key_tkey;intretval;SYS_MSGmybuf;if((key=ftok(/,4))0){perror(ftok);}if((msgid=msgget(key,IPC_CREAT|0777))0){perror(msgget);}mybuf.types=1;strcpy(mybuf.texts,helloworld);if((msgsnd(msgid,&mybuf,MAX_MSG_LENGTH,0)))//发送消息类型为1的消息。{perror(msgsnd);}mybuf.types=2;strcpy(mybuf.texts,howareyou);if((msgsnd(msgid,&mybuf,MAX_MSG_LENGTH,0)))//发送消息类型为2的消息。{perror(msgsnd);}mybuf.types=3;strcpy(mybuf.texts,i'mfine);if((msgsnd(msgid,&mybuf,MAX_MSG_LENGTH,0)))//发送消息类型为3的消息。{perror(msgsnd);}return0;}接收进程:#includestdio.h#includeerrno.h#includestdlib.h#includestring.h#includesys/ipc.h#includesys/msg.h#includesys/types.h#defineMAX_MSG_LENGTH5typedefstructmsgbuf{longtypes;chartexts[MAX_MSG_LENGTH];}SYS_MSG;intmain(){intmsgid;key_tkey;intretval;SYS_MSGmybuf;if((key=ftok(/,4))0){perror(ftok);}if((msgid=msgget(key,IPC_CREAT|0777))0){perror(msgget);}while(1){//输入想接收的消息类型scanf(%ld,&mybuf.types);memset(mybuf.texts,0,MAX_MSG_LENGTH);if(0==mybuf.types){break;}//接收消息类型为上面输入的消息类型if((msgrcv(msgid,&mybuf,MAX_MSG_LENGTH,mybuf.types,MSG_NOERROR))0){perror(msgrcv);}printf(msgrcv:%s\n,mybuf.texts);}if((msgctl(msgid,IPC_RMID,NULL))0)//从内核中删除消息队列{perror(msgctl);}return0;}