IPC SystemV Message Queue

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

如果msgflg0,则只返回消息队列的msgid

此函数成功时,返回非负消息队列标示;失败时返回-1

<!--[if !supportLists]-->Ø <!--[endif]-->int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

#include <sys/msg.h>

其中msgidmsgget返回的消息队列标识符,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_NOWAITmsgtyp指定待接收消息的类型。

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.uidmsg_perm_gidmsg_perm.modemsg_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");

//ftokpathname参数所指向的文件必须存在

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
/* Template for struct to be used as argument for `msgsnd' and `msgrcv'. */
struct msgbuf
{
long int mtype; /* type of received/sent message */
char mtext[1]; /* text of the message */
};
#endif

#ifdef _GNU_SOURCE
# define __USE_GNU 1
#endif

因此要想使用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

自定义消息结构体进行消息的发送和接收。

msgsndmsgrcv函数的void* msgp是具有struct msgbuf模板的结构体变量。

首先分析下struct msgbuf的结构。

struct msgbuf
{
long int mtype; /* type of received/sent message */
char mtext[1]; /* text of the message */
};
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

有两个成员,一个为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]-->消息队列和管道(pipeFIFO)的区别:

管道可以比作为“传输带”,货物(数据流)是按照放上去的顺序达到的;

消息队列可以比作为“仓库”,货物是静态地存储的。

管道对于存货和取货的双方有一定的要求:没有存货方,就不能有取货方(管道只能先以写方式打开后,才能以读方式打开);没有货,就不能取货(对管道写数据后,才能读数据);并且对货物形式有要求(存货和取货双方必须定义一致的数据协议)。

消息队列的取货和存货方不存在彼此阻塞约束的现象,双方都是独立自由的,消息队列只是一个公共的存货的仓库;双方也不需要关心数据协议,消息队列已经将数据以消息形式保存在队列中。

<!--[if !supportLists]-->Ø <!--[endif]-->Posix消息队列和SystemV消息队列区别

新的应用程序应考虑使用Posix消息队列,不过大量的现有代码使用SystemV消息队列。

Posix消息队列遗漏的主要特性是从队列中读取指定优先级消息的能力;这两种消息都不适用真正的描述字,因此从而造成在消息队列上使用selectpoll的困难。

<!--[if !supportLists]-->Ø <!--[endif]-->ipcs命令

此命令用于显示系统中进程间通信的内核对象。

#ipcs –qa

显示所有的进程间通信对象

#ipcs –q

显示消息队列对象

<!--[if !supportLists]-->Ø <!--[endif]-->消息复用是指在一个消息队列上存储多个种类的消息,这些消息通过struct msgbufmtype来区分。

<!--[if !supportLists]-->Ø <!--[endif]-->消息队列存在一定的限制,包括:每个消息的最大字节数;任何一个消息队列上的最大字节数;系统范围的最大消息队列数;系统范围的最大消息数。

你可能感兴趣的:(message)