Linux C消息队列实现进程间通信

来源:互联网 发布:c语言字符串的复制 编辑:程序博客网 时间:2024/05/17 07:33

Linux C消息队列实现进程间通信

消息队列
消息队列是内核地址空间中的内部链表,通过Linux内核在各个进程之间传递内容,内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是相对独立的。每个消息队列中的消息,又构成一个独立的链表。
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制,消息总的大小不能超过8192个字节

键值构建ftok()函数
ftok()函数将路径名和项目的表示符转变为一个系统V的IPC键值,原型如下:
#include<sys/types.h>#include<sys/ipc.h>key_t ftok(const char *pathname,int proj_id);
其中pathname必须是已经存在的目录,而proj_id则是一个8位的值,通常用a,b等表示。

获得消息msgget()函数
原型如下:
#include<sys/types.h>#include<sys/ipc.h>#includE<sys/msg.h>int msgget(key_t key,int msgflg);
第一个参数是键值,可以用ftok()函数生成,这个关键字的值将被拿来与内核中的其他消息队列的现有关键字相比较。比较之后,打开或者访问操作依赖于msgflg参数的内容:
(1)IPC_CREAT:如果在内核中不存在该队列,则创建它
(2)IPC_EXCL:当与IPC_CREAT一起使用时,如果队列早已存在则将出错。
注:如果只使用了IPC_CREAT,megget()函数或者返回新创建消息队列的消息队列标识符,或者会返回现有的具有同一个关键字值的队列标识符;如果同时使用了IPC_CREAT和IPC_EXCL,那么将可能会有两个结果:或者创建一个新的队列;或者如果该队列存在,则调用出错,并且返回-1.IPC_EXCL本身是没有什么用处的,但在与IPC_CREAT组合使用时,它可以保证没有一个现存的队列为了访问而被打开。

发送消息msgsnd()函数
为了向队列传递消息,用户可以使用msgsnd()数,原型如下:
#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
第一个参数是对列标识符,它是前面调用msgget()获得的返回值。
第二个参数是一个void类型的指针,指向一个消息缓冲区。
第三个参数表示消息的大小,它是以字节为单位的,其中不包括消息类型的长度(4个字节长)
第四个参数可以设置为0(表示忽略),也可以设置为IPC_NOWAIT。如果消息队列已满,则消息不会写入到队列中;如果没有指定IPC_NOWAIT,则调用进程将被中断(阻塞),直到可以写消息为止。

接收消息msgrcv()函数
此函数用于从队列中取消息,原型如下:
#include<sys/types.h>#icnlude<sys/ipc.h>#include<sys/msg.h>ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
第一个参数是对列标识符,它是前面调用msgget()获得的返回值。
第二个参数表示消息缓存区变量的地址,获取的消息将存放在这里。
第三个参数表示缓冲区结构的大小,其中不包括消息类型的长度(4个字节长)
第四个参数表示指定要从队列中获取的消息类型。内核将查找对列中具有匹配类型的第一个到达的消息,并把它复制返回到由msgp参数所指定的地址中。如果mtype参数传递一个为0的值,则将返回队列中最老的消息,不管该消息的类型是什么。

消息控制msgctl()函数
此函数对一些特定的消息队列相联系的内部结构进行操作,原型如下:
#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>int msgctl(int msqid,int cmd,struct msqid_ds *buf);
第一个参数是对列标识符,它是前面调用msgget()获得的返回值。
第二个参数表示执行的控制命令,包括以下选项:
IPC_STAT:读取消息队列属性,取得此队列的msqid_ds结构,并将其存放在buf指向的结构中。
IPC_SET:设置对列的,msqid_ds结构的ipc_perm成员值,它是从buf中取得该值的。通过它,应用层可以设置消息队列的状态,例如修改消息队列的权限等。
IPC_RMID:内核删除队列,使用此此命令执行后,内核会把此消息队列从系统中删除。
第三个参数表示一个临时的msqid_ds结构体类型的变量。用于存储读取的消息队列属性或者需要修改的消息队列属性。

下面来看一个例子:
发送消息端
send
#include<unistd.h>#include<stdlib.h>#include<stdio.h>#include<sys/msg.h>#include<errno.h>#include<string.h>#define MAX 1024struct msg_st{    long int msg_type;    char text[MAX];};int main(){   int running =1;   struct msg_st data;   char buffer[BUFSIZ];   printf("bufsiz size is %d\n",BUFSIZ);   printf("MAX is %d\n",MAX);   int msgid=-1;   msgid=msgget((key_t)12345,0666|IPC_CREAT);   if(msgid==-1)   {     printf("msgget fail\n");     exit(EXIT_FAILURE);   }   while(running)   {      printf("input data");      fgets(buffer,BUFSIZ,stdin);      data.msg_type=2;      strcpy(data.text,buffer);      if(msgsnd(msgid,(void *)&data,MAX,0)==-1)      {        printf("send msg fail\n");        exit(EXIT_FAILURE);      }      if(strncmp(buffer,"end",3)==0)          running=0;  }      printf("exit success");      exit(EXIT_SUCCESS);}

接收消息端
get
#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<sys/msg.h>#include<sys/types.h>#include<sys/stat.h>struct msg_st{    long int msg_type;    char text[BUFSIZ];};int main(){  int running =1;  int msgid=-1;  struct msg_st data;  long int msgtype=0;  msgid=msgget((key_t)12345,0666|IPC_CREAT);  if(msgid==-1)  {     puts("msgget fail!\n");     exit(EXIT_FAILURE);  }  while(running)  {    if(msgrcv(msgid,(void *)&data, BUFSIZ,msgtype,0)==-1)    {        puts("msgrcv fail\n");        exit(EXIT_FAILURE);    }    printf("rcv data msg_type is %ld\n",data.msg_type);    printf("content is :%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);}

先运行发送消息端(read),再运行接收消息端(get),在前者中输入消息,会在后者中接收到相应的消息,直到在前者中输入end结束通信。