进程间通信之消息队列

    xiaoxiao2021-03-25  100

        对于进程我们知道一个进程拥有独立的用户地址空间,在一个进程中的全局变量在另一个进程中是看不到的,那仫进程间是如何通信的呢?在进程与进程间的通信必须要经过内核,在内核中开辟一块缓冲区,其中的一个进程把数据从用户空间拷贝到内核缓冲区,另一个进程则从该内核缓冲区中把数据读走,内核提供的这种机制叫进程间通信(IPC)。在这篇文章中主要提到的是进程间通信的第一种方式-消息队列。

      1.什仫是消息队列?

      答:顾名思义,消息队列就是消息的链表,位于内核中。消息队列中每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。消息队列是基于消息的,管道是基于字节流的,且消息队列的读取不一定是先入先出。。。

      值得注意的是:还存在一种进程间通信的机制就是匿名管道和命名管道,管道的生命周期是随进程的,只要该进程消亡了该管道就会随之消失。但是消息队列却是随内核的,即使该进程消亡了该内核中的消息队列是不会主动消失的,除非使用ipcrm -q + msg_id删除该消息队列,也可以使用ipcs -q查看系统中的消息队列。。。

      2.IPC对象数据结构

        内核为每个IPC对象维护一个数据结构ipc_perm,一般在linux中的该/usr/include/linux/ipc.h下,当然也可以通过查找命名查找啦!!!

        

    struct ipc_perm { key_t __key; /* Key supplied to xxxget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ };

         在systemV版本中的消息队列,共享内存和信号都存在ipc_perm这个结构,比如在/usr/include/linux/msg.h路径下查看消息队列的数据结构。。。

         

        通过观察我们可以发现该数据结构msqid_ds中的第一个对象就是ipc_perm类型的。。。

      3.关于消息队列的操作函数

       1).创建新的消息队列或者是获得已经存在的消息队列。

    #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);

        key:可以认为是端口号,也可以由函数ftok生成。

        msgflg:它一般由这两个宏实现:IPC_CREAT and IPC_EXCL 。             IPC_CREAT:如果IPC不存在则创建一个新的IPC,否则执行打开操作。             IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

        值得注意的是:如果单独使用IPC_CREAT,XXXget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符;如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符;如果该IPC资源已存在,或者返回-1。IPC_CREAT和IPC_EXCL一起使用时可以保证该对象是新建的而不是打开一个已经存在的消息队列。

        

    #include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);

        pathname是路径,proj_id用来指定数字,为生成唯一的key提供。该函数把从pathname导出的信息与id的低序8位组合成个整数IPC键,然后创建队列。这个函数的返回值做msgget的第一个参数使用。

        2).向消息队列中读写消息

         使用msgget函数已经创建了一个消息队列,那仫如何向一个消息队列中发消息和读消息呢?msgsnd和msgrcv提供了这两个功能,现在让我们先来看如何向消息队列中写入消息的函数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);

          msqid:就是该消息队列的标识码;

          magp:是指向一个结构体的指针,这个结构体是msgbuf;msgp指向消息队列的缓冲区的指针,用来暂时存储和发送的消息;

          msgsz:指的是当前发送消息的大小,而不是整个结构体的大小。msgbuf结构在下列给出,就是说msgsz的值不包括mtype消息类型的大小,而这个参数只是用来指明消息的长度的。

          msgflg:这个参数用来指明在没有数据的情况下所需要做的操作,这个操作我们经常使用IPC_NOWAIT,这样当消息队列满了的时候,再向消息队列中发送消息,不会发生阻塞现象。

          

    struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[用户指定大小]; /* message data */ };      

          当调用成功,也就是说此时已经有消息写入消息队列中,接收消息的一方可以调用函数msgrcv:

          

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

          将消息从消息队列中取出函数msgrcv,参数类似msgsnd函数,在这里主要提到的就是msgtyp,它的作用是:从消息队列中读取的消息形态。如果值为0则表示所有消息队列中的所有消息都会被读取。。。

         3).设置消息队列删除

          创建了消息队列就要删除该消息队列,删除一个消息队列就要调用函数msgctl。

          

    #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);

          删除某一特定的消息队列,系统中定义了三种cmd模式:IPC_STAT , IPC_SET , IPC_RMID         IPC_STAT:获取消息队列中的msqid_ds数据结构,并将其保存到buf指定的地址空间。         IPC_SET:设置消息队列的属性,要设置的属性存储在buf中。         IPC_RMID:从内核中删除msqid标识的消息队列。

      4.一个简单的关于C/S的消息队列的通信实例

         C/S就是客户端服务器模型,在这里简单的介绍一下客户端就是主动发起请求的一方,而服务器就是被动响应的一方。。。

         server.c

         

    #include"comm.h" int main() { int msg_id=CreatMsgQueue(); char buf[2*MYSIZE]; while(1) { if(ReceiveMsgQueue(msg_id,CLIENT_TYPE,buf) < 0) { break; } printf("client# %s\n",buf); if(SendMsgQueue(msg_id,SERVER_TYPE,buf) < 0) { break; } } DestroyMsgQueue(msg_id); return 0; }

        client.c

        

    #include"comm.h" int main() { int msg_id=GetMsgQueue(); char buf[MYSIZE]; char out[2*MYSIZE]; while(1) { printf("please enter#"); fflush(stdout); ssize_t size=read(0,buf,sizeof(buf)-1); if(size > 0) { buf[size-1]='\0'; SendMsgQueue(msg_id,CLIENT_TYPE,buf); } if(ReceiveMsgQueue(msg_id,SERVER_TYPE,out) < 0) { break; } printf("server echo#%s\n",out); } return 0; }

        comm.h

        

    #ifndef __COMM__ #define __COMM__ #define PATHNAME "." #define PROCID 0x6666 #define MYSIZE 256 #define CLIENT_TYPE 1 #define SERVER_TYPE 2 #include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include<string.h> #include<unistd.h> struct msgbuf { long msg_type; char text[MYSIZE]; }; int CreatMsgQueue(); int DestroyMsgQueue(int msg_id); int GetMsgQueue(); int SendMsgQueue(int msg_id,long type,const char *msg); int ReceiveMsgQueue(int msg_id,long type,char out[]); #endif

       comm.c

       

    #include"comm.h" static int ComMsgQueue(int flag) { key_t key=ftok(PATHNAME,PROCID); if(key < 0) { perror("ftok"); return -1; } int msg_id=msgget(key,flag); if(msg_id < 0) { perror("msgget"); return -2; } return msg_id; } int CreatMsgQueue() { return ComMsgQueue(IPC_CREAT|IPC_EXCL|0x666); } int GetMsgQueue() { return ComMsgQueue(IPC_CREAT); } int SendMsgQueue(int msg_id,long type,const char *msg) { struct msgbuf buf; buf.msg_type=type; strcpy(buf.text,msg); int ret=msgsnd(msg_id,&buf,sizeof(buf.text),0); if(ret < 0) { perror("msgsnd"); return -1; } return 0; } int ReceiveMsgQueue(int msg_id,long type,char out[]) { struct msgbuf buf; int size=msgrcv(msg_id,&buf,sizeof(buf.text),type,0); if(size > 0) { buf.text[size]='\0'; strncpy(out,buf.text,size); return 0; } perror("msgrcv"); return -1; } int DestroyMsgQueue(int msg_id) { if(msgctl(msg_id,IPC_RMID,NULL) < 0) { perror("msgctl"); return -1; } return 0; }

      下面是测试的一个实例图:

         

      在这里就分享结束了~~~~

    转载请注明原文地址: https://ju.6miu.com/read-24484.html

    最新回复(0)