SystemV消息队列
<!--[if !supportLists]-->1、 <!--[endif]-->函数列表
<!--[if !supportLists]-->Ø <!--[endif]-->key_t ftok(const char *pathname, int proj_id);
//# include <sys/types.h>
//# include <sys/ipc.h>
IPC消息队列有一个key的属性(类型为key_t),一般由此函数产生,产生方法为:根据文件名pathname(必须存在且有权限访问)得到索引节点号,然后将索引节点号和子序列号(proj_id)组成ID标示。
<!--[if !supportLists]-->Ø <!--[endif]-->int msgget(key_t key, int msgflg);
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
此函数有两个作用:创建key属性为key的消息队列和返回key指定消息的msgid,其中msgid是消息队列的标示。
其中msgflg标示符可以指定创建消息队列的访问权限(一般为S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH用户读写,组和其它读);同时可以指定msgget函数执行的操作,在创建消息队列时,msgflg必须有IPC_CREAT。
如果msgflg为0,则只返回消息队列的msgid。
此函数成功时,返回非负消息队列标示;失败时返回-1。
<!--[if !supportLists]-->Ø <!--[endif]-->int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
#include <sys/msg.h>
其中msgid是msgget返回的消息队列标识符,msgp是一个结构体指针,msgsz是结构体中具体数据的长度,msgflg可以为0或者IPC_NOWAIT。
当msgflag指定IPC_NOWAIT时,msgsnd函数成为非阻塞,当由于一些限制条件(例如没有存放消息的可用空间时)出现时,该函数立即放回。
发送成功返回0,发送失败返回-1.
<!--[if !supportLists]-->Ø <!--[endif]-->ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgsz是结构体(msgp)中保存数据的最大值,也是函数能够返回的最大数据量。而具体的接收的数据量,由函数返回值确定。msgflag可以为0或者IPC_NOWAIT。msgtyp指定待接收消息的类型。
当msgflag指定为IPC_NOWAIT时,msgrcv函数成为非阻塞,当由于一些限制条件(例如消息队列中没有消息)时,该函数立即返回。
其中type的规则为:
当type=0时,返回消息队列中的第一个消息;
当type>0时,返回类型值为type的消息;
当type<0时,返回类型值小于或者等于type绝对值的消息中类型值最小的消息。
msgrcv函数成功时返回接收消息的数据量,失败返回-1.
<!--[if !supportLists]-->Ø <!--[endif]-->int msgctl(int msqid, int cmd, struct msqid_ds *buf);
消息队列控制函数。Msgid为消息队列标示,cmd为具体的命令(一共有三种),buf用来设置或取得消息队列的msgid_ds结构体。
IPC_RMID:删除msgid指定的消息队列。Buf参数忽略,为0
IPC_SET:设置消息队列msgid_ds结构体的四个成员:msg_perm.uid,msg_perm_gid,msg_perm.mode和msg_qbytes。它们的值来自由buf指向的结构体中的相应成员。
IPC_STAT:给调用者返回消息队列msgid_ds结构体,通过buf返回。
<!--[if !supportLists]-->2、 <!--[endif]-->实例解析
<!--[if !supportLists]-->Ø <!--[endif]-->create|send|receive|remove
消息队列的创建和删除、消息的发送和接收。采用默认的struct msgbuf作为消息体。
// mymsgcreate.c #include <sys/msg.h> #include <fcntl.h> // O_CREATE #include <sys/types.h> // pid_t #include <sys/stat.h> int main(int argc, char **argv) { int c, oflag, mqid; oflag = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH | IPC_CREAT; //设置权限(用户读写,组和其它读) //指定msgget具体操作(创建消息队列并返回ID) if((mqid = msgget(ftok("test3", 0), oflag))<0) printf("create error!/n"); //ftok的pathname参数所指向的文件必须存在 printf("mqid===%d/n",mqid); exit(0); } //#gcc mymsgcreate.c –o mycreate |
创建一个消息队列必须满足的条件是:ftok函数的pathname指向的文件已经存在;msgflg中有IPC_CREATE标示。权限要设置正确,用户要有读和写权限。
// mymsgsnd.c #include <sys/msg.h> #include <fcntl.h> // O_CREATE #include <sys/types.h> // pid_t #include <sys/stat.h>
int main(int argc, char **argv) { struct msgbuf *ptr; int mqid; if((mqid = msgget(ftok("test3", 0), S_IWUSR))<0) printf("create error!/n"); // S_IWUSR权限可以省略,消息队列的权限已经设置了,这里可以为0 //msgget函数的作用是返回消息队列的ID
ptr = calloc(sizeof(long)+7, sizeof(char)); ptr->mtype = 100; strcpy(ptr->mtext,"abcdefg"); msgsnd(mqid, ptr,7, 0); exit(0); } // gcc -D_GNU_SOURCE mymsgsnd.c -o mysend |
消息队列已经创建了,此时msgget函数只是起获得消息队列标示的作用,因此msgflg可以为0。
关于编译选项-D_GNU_SOURCE说明如下:
/usr/include/src/msg.h文件中
#ifdef __USE_GNU
#ifdef _GNU_SOURCE |
因此要想使用struct msgbuf,必须#define _GNU_SOURCE,也可以通过编译选项加进去。
注意msgbuf成员mtext是大小为1的字符数组,而经测试:消息数据的长度不受此限制。例如上面的例子即发送了7个字符。这可能是与结构体成员内存存储有关,也就是可能已经超出结构体的存储范围,这对普通的数据类型是不允许,但对结构体变量是可以的,正如 strcpy(ptr->mtext,"abcdefg");操作是合法的。
//mymsgrec.c #include <sys/msg.h> #include <fcntl.h> // O_CREATE #include <sys/types.h> // pid_t #include <sys/stat.h> #define MAXMSG (8192 + sizeof(long)) int main(int argc, char **argv) { int mqid,n; struct msgbuf *buff; if((mqid = msgget(ftok("test3", 0), S_IRUSR))<0) printf("create error!/n"); //S_IWUSR权限可以省略,消息队列的权限已经设置了,这里可以为0 //msgget函数的作用是返回消息队列的ID buff = malloc(MAXMSG); n =msgrcv(mqid, buff, MAXMSG, 100, 0); printf("read %d bytes, type = %ld,mtext=%s/n", n, buff->mtype,buff->mtext); exit(0); } // gcc -D_GNU_SOURCE mymsgrec.c -o myrec |
通消息发送,此时消息队列已经创建,msgget函数只起获得消息队列标示的作用,msgflg可以为0.
//mymsgrmid.c #include <sys/msg.h> #include <fcntl.h> // O_CREATE #include <sys/types.h> // pid_t #include <sys/stat.h>
int main(int argc, char **argv) { int mqid; mqid = msgget(ftok("test3", 0), 0); msgctl(mqid, IPC_RMID, 0); exit(0); } //gcc mymsgrmid.c –o myrm |
struct msqid_ds *buf参数被忽略,为0。
<!--[if !supportLists]-->Ø <!--[endif]-->mystruct
自定义消息结构体进行消息的发送和接收。
msgsnd和msgrcv函数的void* msgp是具有struct msgbuf模板的结构体变量。
首先分析下struct msgbuf的结构。
struct msgbuf |
有两个成员,一个为long int保存着消息类型,一个为char数组,保存着具体的消息数据。
因此,以此结构体作为模板的结构体至少有两个成员,一个为long int,一个为char数组。如果结构体的成员变量不只这两个,必须保证保存消息类型的long int和保存消息数据的char数组,连续存储,并位于结构体末尾。
现在自定义一个以struct msgbuf为模板的结构体。
struct mymesg { long mesg_len; /* #bytes in mesg_data, can be 0 */ long mesg_type; /* message type, must be > 0 */ char mesg_data[MAXMESGDATA]; }; |
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
两个函数中msgp结构体指针为部分mymesg结构体指针,指向mseg_type。也就是消息接收和发送函数的void* msgp参数为&(ptr->mesg_type)。也就是消息传输的结构体不包含mesg_len成员数据,在接收端,mesg_len通过msgrcv函数的返回值确定。
从实现方式和结果来看,使用struct msgbuf和自定义结构体没有差异。
//mymesg.h #include <sys/msg.h> #include <fcntl.h> // O_CREATE #include <sys/types.h> // pid_t #include <sys/stat.h> #include <limits.h>
#define MAXMESGDATA (PIPE_BUF - 2*sizeof(long)) #define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA) struct mymesg { long mesg_len; /* #bytes in mesg_data, can be 0 */ long mesg_type; /* message type, must be > 0 */ char mesg_data[MAXMESGDATA]; }; |
//mystructsnd.c #include "mymesg.h" int main(int argc, char **argv) { struct mymesg *ptr; int mqid; if((mqid = msgget(ftok("test3", 0), S_IWUSR))<0) printf("create error!/n"); ptr = calloc(2*sizeof(long)+7, sizeof(char)); ptr->mesg_type = 100; ptr->mesg_len = 7; strcpy(ptr->mesg_data,"abcdefg"); msgsnd(mqid, &(ptr->mesg_type),ptr->mesg_len, 0); exit(0); } // gcc -D_GNU_SOURCE mystructsnd.c -o mystructsend |
// mystructrec.c #include "mymesg.h" #define MAXMSG (8192 + sizeof(long))
int main(int argc, char **argv) { int mqid,n; struct mymesg *buff; if((mqid = msgget(ftok("test3", 0), S_IRUSR))<0) printf("create error!/n"); buff = malloc(MAXMSG); buff->mesg_type=100; n =msgrcv(mqid,&(buff->mesg_type), MAXMSG,buff->mesg_type, 0); buff->mesg_len=n; printf("read %d bytes, type = %ld,mtext=%s/n", buff->mesg_len, buff->mesg_type,buff->mesg_data); exit(0); } // gcc -D_GNU_SOURCE mystructrec.c -o mystructrec |
<!--[if !supportLists]-->3、 <!--[endif]-->小结
<!--[if !supportLists]-->Ø <!--[endif]-->消息队列和管道(pipe和FIFO)的区别:
管道可以比作为“传输带”,货物(数据流)是按照放上去的顺序达到的;
消息队列可以比作为“仓库”,货物是静态地存储的。
管道对于存货和取货的双方有一定的要求:没有存货方,就不能有取货方(管道只能先以写方式打开后,才能以读方式打开);没有货,就不能取货(对管道写数据后,才能读数据);并且对货物形式有要求(存货和取货双方必须定义一致的数据协议)。
消息队列的取货和存货方不存在彼此阻塞约束的现象,双方都是独立自由的,消息队列只是一个公共的存货的仓库;双方也不需要关心数据协议,消息队列已经将数据以消息形式保存在队列中。
<!--[if !supportLists]-->Ø <!--[endif]-->Posix消息队列和SystemV消息队列区别
新的应用程序应考虑使用Posix消息队列,不过大量的现有代码使用SystemV消息队列。
Posix消息队列遗漏的主要特性是从队列中读取指定优先级消息的能力;这两种消息都不适用真正的描述字,因此从而造成在消息队列上使用select或poll的困难。
<!--[if !supportLists]-->Ø <!--[endif]-->ipcs命令
此命令用于显示系统中进程间通信的内核对象。
#ipcs –qa
显示所有的进程间通信对象
#ipcs –q
显示消息队列对象
<!--[if !supportLists]-->Ø <!--[endif]-->消息复用是指在一个消息队列上存储多个种类的消息,这些消息通过struct msgbuf的mtype来区分。
<!--[if !supportLists]-->Ø <!--[endif]-->消息队列存在一定的限制,包括:每个消息的最大字节数;任何一个消息队列上的最大字节数;系统范围的最大消息队列数;系统范围的最大消息数。