精华内容
下载资源
问答
  • 进程创建消息队列和两个子进程p1和p2 子进程p1打开给定文件(如果没有,则创建文件),并向文件中写数据,写完关闭文件,然后向消息队列写入一条消息“ok”,目的是通知进程p2可以读取文件内容了。 子进程p2从消息...
  • 进程通信消息队列

    千次阅读 2019-01-27 12:00:22
    有写权限的进程可以向消息队列中添加新消息;有读权限的进程则可以从消息队列中读走消息。 (二)通信流程 基本函数:msgrcv(接收)、msgsnd(发送)、msgget(新建) (1)新建消息队列 //创建消息队列--类比于open...

    目录

    • 消息队列的概念
    • msgsend.c, msgreceive.c
    • 输出
    (一)消息队列的概念

    定义:是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。有写权限的进程可以向消息队列中添加新消息;有读权限的进程则可以从消息队列中读走消息。消息本质上是一种数据结构。暂且这样,先要会用,会用了可以继续研究内部实现过程。用都不会用,太纠结实现原理,在有限时间内,是得不偿失的。

    (二)通信流程

    下面只对用到的函数参数分析及代码解析,最后给一个编程思路。
    下面涉及两个文件,msgsend.cmsgreceive.c
    基本函数:msgrcv(接收)、msgsnd(发送)、msgget(新建)、msgctl(删除)

    msgsend.c

    (1)新建消息队列

    //创建消息队列--类比于open,其返回的标识id,用于msgrcv(接收)、msgsnd(发送)
    int msgget(key_t key, int msgflg);
    

    参数:
    (1)key:消息队列关联的标识符,本质是整数,可以弄个整数强制转化下就可以了,如(key_t)1234
    (2)msgflg:消息队列的建立标志存取权限

    • IPC_CREAT:如果内核中没有此队列则创建它
    • 当IPC_EXCL和IPC_CREAT一起使用时,如果队列已经存在,则失败
      例如: 用msgget创建一个消息队列
    	msg = msgget((key_t)1234, 0777|IPC_CREAT);					//IPC_CREAT没有就创建及存取权限s
    	if(msg == -1)
    	{
    		fprintf(stderr, "The filure code is %d!!!\n",errno);	//errno--number of last error
    		exit(EXIT_FAILURE);
    	}
    

    (2)消息队列的发送

    //消息队列的发送--类比于write
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    

    参数:

    • msqid : 消息队列的标识码(msgget返回的id)
    • msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个是字符数组), 这里是void,要强制转换下,最好这样。
    //这个结构体要用户自己写一个,只要第一个参数值大于0,第二个是字符数组,都可以了,如:
    #define BUF_TEXT 512
    struct msg_st
    {
    	long msg_type;       										//标志位
    	char msg_text[BUF_TEXT];  									//发送数据缓冲区
    };
    
    
    • msgsz : 消息的长短
    • msgflg: 标志位–如果为0,表示忽略该标志位
    • 返回值–On success, zero is returned. On error, -1 is returned

    在这里插入图片描述
    //msgsend.c

    #include <stdio.h>
    #include <sys/stat.h>
    //msgget
    #include <sys/types.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    //exit头文件
    #include <stdlib.h>
    #include <unistd.h>
    //errno头文件
    #include <errno.h>
    #include <string.h>
    
    #define BUF_TEXT 512
    
    
    /****************************************************************************************
    **									消息队列
    ** msgget():创建消息队列--类比于open
    **	 int msgget(key_t key, int msgflg);
    ** key:
    **	 消息队列关联的标识符
    ** msgflg:消息队列的建立标志和存取权限
    **	 IPC_CREAT:如果内核中没有此队列则创建它
    **	 当IPC_EXCL和IPC_CREAT一起使用时,如果队列已经存在,则失败
    ** RETURN VALUE
    ** 	 执行成功则返回消息队列的标识符(a nonnegative integer-非负整数),否则返回-1
    ** 注意:
    **		key_t本质是整数,你自己弄个整数强制转化下就可以了,如(key_t)1234	 		
    ** msgsnd():消息队列的发送--类比于write
    ** 	 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    ** msqid : 消息队列的标识码(msgget返回的id)
    ** *msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个数字符数组)
    ** 		   这里是void*,要强制转换下,最好这样。
    ** msgsz : 消息的长短
    ** msgflg: 标志位
    ** RETURN VALUE
    **   On success, zero is returned. On error, -1 is returned
    ** msgrcv():消息队列的接受--类比于read
    ** 	 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    ** msqid : 消息队列的标识码(msgget返回的id)
    ** *msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个数字符数组)
    ** 		   这里是void*,要强制转换下,最好这样。
    ** msgsz : 消息的长短
    ** msgtyp: msgtyp等于0 ,则返回队列的最早的一个消息
    ** 		   msgtyp大于0,则返回其类型为mtype的第一个消息
    ** 		   msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息
    ** msgflg: 标志位,如果为0,表示忽略该标志位
    ** RETURN VALUE
    **   成功返回数据长度,错误返回-1
    ** fgets():从指定流中读出size-1个字符,若读入新行,则存在buffer中,最后要以NULL结尾
    ** 	 char *fgets(char *s, int size, FILE *stream);
    ** stream: 几种标准的流/文件句柄
    ** stpcpy(): 字符串的复制
    ** 	 char *stpcpy(char *dest, const char *src);
    ** strncmp(): 字符串比较
    ** 	 char *strncmp(char *dest, const char *src, size_t n);
    ****************************************************************************************/ 
    
    //msgsnd/msgrcv 第二个参数
    struct msg_st
    {
    	long msg_type;       										//标志位
    	char msg_text[BUF_TEXT];  									//发送数据缓冲区
    };
    
    int main(void)
    {
    	struct msg_st data;							
    	int msg = -1;												//标识id
    	int running = 1;
    	char buffer[BUFSIZ];										//系统常量BUFSIZ是8192--定义在stdio.h中
    	
    	msg = msgget((key_t)1234, 0777|IPC_CREAT);					//IPC_CREAT没有就创建及存取权限s
    	if(msg == -1)
    	{
    		fprintf(stderr, "The filure code is %d!!!\n",errno);	//errno--number of last error
    		exit(EXIT_FAILURE);
    	}
    	
    	while(running)
    	{
    		printf("Please enter data: \n");
    		fgets(buffer, BUFSIZ, stdin);							// 标准输入向buffer获取BUFSIZ-1个字符
    		data.msg_type = 1;										//标志置1,表示要通信了。其他数也可以
    		stpcpy(data.msg_text, buffer);							//将buffer的数据复制到结构体的msg_text中
    		
    		if(msgsnd(msg, (void *)&data, BUF_TEXT, 0) == -1)		//发送数据到data结构体的数组中
    		{
    			fprintf(stderr, "magsnd error!!!\n");
    			exit(EXIT_FAILURE);
    		}
    		if(strncmp(data.msg_text, "end", 3) == 0)				//判断字符串是否相等,自己弄个结束标志	
    		{
    			running = 0;										//推出while
    		}
    		sleep(1);
    	}
    	exit(EXIT_SUCCESS);	   
    }
    

    msgreceive.c

    (1)新建消息队列
    和msgsend.c一样,都需要打开同一个消息队列才可以通信。见上

    (2)消息队列的接收

     msgrcv():消息队列的接受--类比于read
     ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    
    • msqid : 消息队列的标识码(msgget返回的id)
    • *msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个数字符数组), 这里是void,要强制转换下,最好这样。*—也就是说在msgreceive.c中也要定义一样的结构体。
    • msgsz : 消息的长短
    • msgtyp: msgtyp等于0 ,则返回队列的最早的一个消息,这里给0
      msgtyp大于0,则返回其类型为mtype的第一个消息
      msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息
    • msgflg: 标志位–如果为0,表示忽略该标志位

    (3)断开消息队列的连接

     msgctl():消息队列的控制函数,里面有一项可以删除消息队列
     int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    
    • msqid : 消息队列的标识码(msgget返回的id)
    • cmd : IPC_RMID: 立即断开连接
    • 返回值 —On success, IPC_STAT, IPC_SET, and IPC_RMID return 0.On error, -1 is returned
    	//断开消息队列的连接
    	if(msgctl(msg, IPC_RMID, 0) == -1)										//msgctl删除消息队列
    	{
    		fprintf(stderr, "magctl error code %d !!!\n",errno);
    		exit(EXIT_FAILURE);
    	}
    

    在这里插入图片描述

    //msgreceive.c

    #include <stdio.h>
    #include <sys/stat.h>
    //msgget
    #include <sys/types.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    //exit头文件
    #include <stdlib.h>
    #include <unistd.h>
    //errno头文件
    #include <errno.h>
    #include <string.h>
    
    /****************************************************************************************
    **									消息队列
    ** msgrcv():消息队列的接受--类比于read
    ** 	 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    ** msqid : 消息队列的标识码(msgget返回的id)
    ** *msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个数字符数组)
    ** 		   这里是void*,要强制转换下,最好这样。
    ** msgsz : 消息的长短
    ** msgtyp: msgtyp等于0 ,则返回队列的最早的一个消息,这里给0
    ** 		   msgtyp大于0,则返回其类型为mtype的第一个消息
    ** 		   msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息
    ** msgflg: 标志位,如果为0,表示忽略该标志位
    ** RETURN VALUE
    **   On success, zero is returned. On error, -1 is returned
    ** msgctl():
    **   int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    ** msqid : 消息队列的标识码(msgget返回的id)
    ** cmd   : 
    **   IPC_RMID: 立即断开连接
    ** RETURN VALUE
    **   On success, IPC_STAT, IPC_SET, and IPC_RMID return 0.On error, -1 is returned
    ** fgets():从指定流中读出size-1个字符,若读入新行,则存在buffer中,最后要以NULL结尾
    ** 	 char *fgets(char *s, int size, FILE *stream);
    ** stream: 几种标准的流/文件句柄
    ** stpcpy(): 字符串的复制
    ** 	 char *stpcpy(char *dest, const char *src);
    ** strncmp(): 字符串比较
    ** 	 char *strncmp(char *dest, const char *src, size_t n);
    ****************************************************************************************/
    
    //msgsnd/msgrcv 第二个参数
    struct msg_st
    {
    	long msg_type;       													//标志位
    	char msg_text[BUFSIZ];  												//系统常量BUFSIZ是8192--定义在stdio.h中
    };
    
    
    int main(void)
    {
    	struct msg_st data;
    	int msg = -1;															//标识id
    	int running = 1;
    	char buffer[BUFSIZ];
    	long int msgtype = 0;													//这里给0,返回队列的最早的一个消息
    	
    	//打开消息队列
    	msg = msgget((key_t)1234, 0777|IPC_CREAT);								//IPC_CREAT没有就创建及存取权限s
    	if(msg == -1)
    	{
    		fprintf(stderr, "The filure code is %d!!!\n",errno);				//errno--number of last error
    		exit(EXIT_FAILURE);
    	}
    	
    	//接受消息队列的数据---以end结束
    	while(running)
    	{
    		if(msgrcv(msg, (void *)&data, BUFSIZ, msgtype, 0) == -1)			//将结构体中数据通过msgrcv接收  
    		{
    			fprintf(stderr, "magcve error code %d !!!\n",errno);
    			exit(EXIT_FAILURE);
    		}
    		printf("You just wrote:\n%s\n",data.msg_text);
    		
    		if(strncmp(data.msg_text, "end", 3) == 0)							//以end结束	
    		{
    			running = 0;
    		}
    	}
    	
    	//断开消息队列的连接
    	if(msgctl(msg, IPC_RMID, 0) == -1)										//msgctl删除消息队列
    	{
    		fprintf(stderr, "magctl error code %d !!!\n",errno);
    		exit(EXIT_FAILURE);
    	}
    	exit(EXIT_SUCCESS);	
    }
    
    (三)输出

    在这里插入图片描述
    最后:下面图大致概述了下
    在这里插入图片描述

    展开全文
  • 主要介绍了Linux消息队列实现进程通信实例详解的相关资料,需要的朋友可以参考下
  • 进程通信消息队列 ( message queue ) 消息队列消息的链表,具有特定的格式,并由消息队列标识符标识. 七种进程通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四....
  • 进程间的通信——消息队列

    千次阅读 2018-04-15 18:40:38
    进程间的通信——共享内存https://blog.csdn.net/q496958148/article/details/79953349 进程间的通信——信号量https://blog.csdn.net/q496958148/article/details/79977093 进程间的通信——管 道...

    进程间的通信——共享内存https://blog.csdn.net/q496958148/article/details/79953349
    进程间的通信——信号量https://blog.csdn.net/q496958148/article/details/79977093
    进程间的通信——管 道https://blog.csdn.net/q496958148/article/etails/79948367

    什么是消息队列?

    消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。也就是说进程的退出,如果不自主去释放资源,消息队列是会悄无声息的存在的。消息队列与管道不同的是,消息队列是基于消息的, 而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一 样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限

    进程通信我们有了管道,为什么还要引入消息队列呢?

    因为根据管道的特性,我们知道其在一定程度上存在或多或少的局限性,首先匿名管道以及命名管道是随进程的,进程的退出,意味着管道生命周期的结束;其次,管道传送数据时以无格式字节流的形式传送,这有时会给程序的开发带来不便;再者,担当数据传送媒介的管道,其缓冲区的大小也有较大的限制。所以作为IPC方式下的另一种应用于进程间通信的消息队列方式便应运而生了。
    IPC对象数据结构:

    //内核为每个IPC对象维护了一个数据结构
    struct ipc_perm
    {
        __kernel_key_t  key;//key值是消息队列的一个验证码(我理解为暗号,首先一个进程要向消息队列放入一个东西,得先生成一个key值,这个key值是函数计算得来的,输入值确定会固定得到一个key值,而又有一个进程需要取刚才进程放的东西,这时候就得暗号匹配才能取。)
        __kernel_uid_t  uid;
        __kernel_gid_t  gid;
        __kernel_uid_t  cuid;
        __kernel_gid_t  cgid;
        __kernel_mode_t mode; //权限
        unsigned short  seq;
    };

    消息队列结构:

    struct msqid_ds {
        struct ipc_perm msg_perm;
        struct msg *msg_first;      /* first message on queue,unused  */
        struct msg *msg_last;       /* last message in queue,unused */
        __kernel_time_t msg_stime;  /* last msgsnd time */
        __kernel_time_t msg_rtime;  /* last msgrcv time */
        __kernel_time_t msg_ctime;  /* last change time */
        unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
        unsigned long  msg_lqbytes; /* ditto */
        unsigned short msg_cbytes;  /* current number of bytes on queue */
        unsigned short msg_qnum;    /* number of messages in queue */
        unsigned short msg_qbytes;  /* max number of bytes on queue */
        __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
        __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
    };
    

    和消息队列有关的函数:

    • 生成key值
    key_t ftok(const char *pathname, int proj_id);
    • 用来创建或者访问一个消息队列
    int msgget(key_t key, int msgflg);
    //key 上面解释过了,相当于一个暗号由ftok函数创建
    //msgflg: IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
    //返回值: 调用成功返回队列标识符,否则返回-1.
    • 消息队列的控制函数
     int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    //msqid:有msgget返回的标识码
    //cmd:将要采取的动作,IPC_STAT把msqid_ds结构的数据设置为消息队列的当前关联值, IPC_SET在权限允许的情况下,吧消息队列的当前关联值设置为msqid_ds数据结构中给出的值,IPC_RMID删除消息队列
    • 插入一条消息到消息队列
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    //msqid:由msgget函数返回的消息队列标识码
    //msgp:指向准备发送消息的指针
    //msgsz:是msgp的长度
    //msgflg:控制这当前消息队列是否满或者达到系统上限
    //返回值成功为0;失败为-1
    • 从消息队列接收一个消息
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    //msqid:由msgget函数返回的消息队列标识码
    //msgp:指向准备接收消息的指针
    //msgsz:是msgp的长度
    //msgtyp:它可以实现接收优先级的简单形式
    //msgflg:控制这当前消息队列是否满或者达到系统上限
    //返回值成功为实际接收到接收缓存区的字符个数;失败为-1

    说明:
    msgtyp等于0 则返回队列的最早的一个消息。
    msgtyp大于0,则返回其类型为mtype的第一个消息。
    msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息。
    msgflg:这个参数依然是是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回。如果进程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回*。MSG*_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出。当消息从队列内取出后,相应的消息就从队列中删除了。

    实例:
    功能是实现俩个窗口之间的相互通信:
    最终的成果图
    这里写图片描述

    具体实现如下:

    这里写图片描述

    这里写图片描述

    msgges.h:

    #pragma once
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.h>
    
    //这俩个宏定义是生成key值
    #define PATH "."
    #define NUM 0x6666
    
    //
    #define SERVER_TYPE 1
    #define CLIENT_TYPE 2
    
    //消息队列中消息的结构体
    struct msgbuf{
        long mtype;//上面进行宏定义的SERVER_TYPE与CLINENT_TYPE就是这个
        char mtext[1024];
    };
    
    int createMsgQueue();
    
    int getMsgQueut();
    
    int destroyMsgQueut(int msgid);
    
    int sendMsg(int msgid,int who,char *msg);
    
    int recvMsg(int msgid,int recvType,char out[]);
    

    msgges.c:

    #include "msgges.h"
    
    //创建一个消息队列,如果已经有了,返回错误
    int createMsgQueue()
    {
        key_t key = ftok(PATH,NUM);//生成key值(暗号)
        if(key < 0)
        {
            perror("ftok");
            return -1;
        }
        int msgid = msgget(key, IPC_CREAT|IPC_EXCL|0666);
        if(msgid < 0)
        {
            perror("msgget");
            return -1;
        }
        return msgid;
    }
    
    //创建一个消息队列,如果已经有消息队列了,返回已经有的消息队列msgid
    int getMsgQueue()
    {
        key_t key = ftok(PATH,NUM);
        if(key < 0)
        {
            perror("ftok");
            return -1;
        }
        int msgid = msgget(key, IPC_CREAT);
        if(msgid < 0)
        {
            perror("msgget");
            return -1;
        }
        return msgid;
    }
    
    //删除消息队列
    int destroyMsgQueue(int msgid)
    {
        printf("111\n");
        if(msgctl(msgid,IPC_RMID,NULL) < 0)
        {
            perror("msgctl");
            return -1;
        }
        return 0;
    }
    
    //往消息队列里发数据
    int sendMsg(int msgid,int who,char *msg)
    {
       struct msgbuf buf;
       buf.mtype = who;
       strcpy(buf.mtext,msg);
    
       if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0) < 0)
       {
           perror("msgsnd");
           return -1;
       }
       return 0;
    }
    
    //从消息队列里取数据
    int recvMsg(int msgid,int recvType, char out[])
    {
        struct msgbuf buf;
        if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0) < 0)
        {
            perror("msgrcv");
            return -1;
        }
        strcpy(out,buf.mtext);
        return 0;
    }
    

    server.c:

    #include "msgges.h"
    
    int main()
    {
        int msgid = createMsgQueue();
        char buf[1024];
        while(1)
        {
            buf[0] = 0;
            recvMsg(msgid,CLIENT_TYPE,buf);
            printf("小明:%s\n",buf);
    
            printf("你:");
            fflush(stdout);
            ssize_t s = read(0,buf,sizeof(buf));
            if(s > 0)
            {
                buf[s-1] = 0;
                sendMsg(msgid,SERVER_TYPE,buf);
            }
        }
        destroyMsgQueue(msgid);
        return 0;
    }
    

    client.c:

    #include "msgges.h"
    
    int main()
    {
        int msgid = getMsgQueue();
        char buf[1024];
    
        while(1)
        {
            buf[0] = 0;
            printf("你:");
            fflush(stdout);
            ssize_t s = read(0,buf,sizeof(buf));
            if(s > 0)
            {
                buf[s-1] = 0;
                sendMsg(msgid,CLIENT_TYPE,buf);
            }
    
            recvMsg(msgid,SERVER_TYPE,buf);
            printf("张三:%s\n",buf);
        }
        return 0;
    }
    

    Makefile:

    .PHONY:all
    all:client server
    
    
    client:client.c msgges.c
        gcc $^ -o $@;
    server:server.c msgges.c
        gcc $^ -o $@;
    
    
    .PHONY:clean
    clean:
        rm -f client server

    最后如果你出现了下面的错误:
    这里写图片描述
    解决办法如下:
    这里写图片描述
    造成这个原因是,你只要编译通过,并且运行了 你再用ctrl+c 终止掉进程,进程不会走这个函数的destroyMsgQueue(msgid); 一旦你发出ctrl+c信号,操作系统就会马上杀死该进程。这样就会造成IPC没有及时被回收。想要解决这个问题我们可以将ctrl+c 的执行函数改为destroyMsgQueue(msgid); 并且执行完后退出即可。

    展开全文
  •  进程间基于消息队列通信 2[实验目的]  系统了解linux系统的通信机构IPC ,掌握IPC中消息通信机制,理解消息通信的方法及特征。 3[实验内容]  编写一段程序,同时父进程创建两个子进程p1和p2;并使子进程...
  • 本文实例讲述了Python进程通信Queue消息队列用法。分享给大家供大家参考,具体如下: 进程通信-Queue Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。 1. Queue的使用 可以使用...
  • 【Linux】进程通信消息队列

    万次阅读 多人点赞 2018-03-22 23:32:28
    在上一篇博客里,我们学习了进程通信的一种方式,那就是管道,今天我们继续学习另一种方式消息队列消息队列 一. 什么是消息队列?  消息队列消息的链表,存放在内核中并由消息队列标识符表示。  ...

    在上一篇博客里,我们学习了进程间通信的一种方式,那就是管道,今天我们继续学习另一种方式消息队列

    消息队列

    一. 什么是消息队列?

      消息队列是消息的链表,存放在内核中并由消息队列标识符表示。
      消息队列提供了一个从一个进程向另一个进程发送数据块的方法,每个数据块都可以被认为是有一个类型,接受者接受的数据块可以有不同的类型。
      但是同管道类似,它有一个不足就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数(MSGMNB),系统上消息队列的总数上限(MSGMNI)。可以用cat /proc/sys/kernel/msgmax查看具体的数据。
      内核为每个IPC对象维护了一个数据结构struct ipc_perm,用于标识消息队列,让进程知道当前操作的是哪个消息队列。每一个msqid_ds表示一个消息队列,并通过msqid_ds.msg_first、msg_last维护一个先进先出的msg链表队列,当发送一个消息到该消息队列时,把发送的消息构造成一个msg的结构对象,并添加到msqid_ds.msg_first、msg_last维护的链表队列。在内核中的表示如下:
    这里写图片描述


    二. 特点:

    • 生命周期随内核,消息队列会一直存在,需要我们显示的调用接口删除或使用命令删除
    • 消息队列可以双向通信
    • 克服了管道只能承载无格式字节流的缺点

    三. 消息队列函数

    1.msgget

    功能:创建和访问一个消息队列
    原型

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

    参数
    key:某个消息队列的名字,用ftok()产生
    msgflag:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。
    返回值:成功返回一个非负整数,即消息队列的标识码,失败返回-1

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

    调用成功返回一个key值,用于创建消息队列,如果失败,返回-1

    2.msgctl

    功能:消息队列的控制函数
    原型

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

    参数
    msqid:由msgget函数返回的消息队列标识码
    cmd:有三个可选的值,在此我们使用IPC_RMID

    • IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
    • IPC_SET 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
    • IPC_RMID 删除消息队列

    返回值
    成功返回0,失败返回-1

    3.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);

    参数
    msgid:由msgget函数返回的消息队列标识码
    msgp:指针指向准备发送的消息
    msgze:msgp指向的消息的长度(不包括消息类型的long int长整型)
    msgflg:默认为0
    返回值:成功返回0,失败返回-1

    消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型

    struct msgbuf
    {
         long mtye;
         char mtext[1];
    };

    4.msgrcv

    功能:是从一个消息队列接受消息
    原型
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    参数:与msgsnd相同
    返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1


    此外,我们还需要学习两个重要的命令
    前面我们说过,消息队列需要手动删除IPC资源
    ipcs:显示IPC资源
    ipcrm:手动删除IPC资源
    这里写图片描述


    下面用代码模拟实现client与server之间的通信
    comm.h

    #ifndef _COMM_H_
    #define _COMM_H_
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/types.h>
    #include <sys/msg.h>
    
    struct msgbuf
    {
        long mtype;
        char mtext[1024];
    };
    
    #define SERVER_TYPE 1
    #define CLIENT_TYPE 2
    
    int createMsgQueue();
    int getMsgQueue();
    int destoryMsgQueue(int msg_id);
    int sendMsgQueue(int msg_id, int who, char* msg);
    int recvMsgQueue(int msg_id, int recvType, char out[]);
    
    #endif
    

    comm.c

    #include "comm.h"
    
    static int commMsgQueue(int flags)
    {
        key_t key = ftok("/tmp", 0x6666);
        if(key < 0)
        {
            perror("ftok");
            return -1;
        }
    
        int msg_id = msgget(key, flags);
        if(msg_id < 0)
        {
            perror("msgget");
        }
        return msg_id;
    }
    
    int createMsgQueue()
    {
        return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
    }
    
    int getMsgQueue()
    {
        return commMsgQueue(IPC_CREAT);
    }
    
    int destoryMsgQueue(int msg_id)
    {
        if(msgctl(msg_id, IPC_RMID, NULL) < 0)
        {
            perror("msgctl");
            return -1;
        }
        return 0;
    }
    
    int sendMsgQueue(int msg_id, int who, char* msg)
    {
        struct msgbuf buf;
        buf.mtype = who;
        strcpy(buf.mtext, msg);
    
        if(msgsnd(msg_id, (void*)&buf, sizeof(buf.mtext), 0) < 0)
        {
            perror("msgsnd");
            return -1;
        }
        return 0;
    }
    
    int recvMsgQueue(int msg_id, int recvType, char out[])
    {
        struct msgbuf buf;
        int size=sizeof(buf.mtext);
        if(msgrcv(msg_id, (void*)&buf, size, recvType, 0) < 0)
        {
            perror("msgrcv");
            return -1;
        }
    
        strncpy(out, buf.mtext, size);
        out[size] = 0;
        return 0;
    }
    

    server.c

    #include "comm.h"
    
    int main()
    {
        int msgid = createMsgQueue();
    
        char buf[1024] = {0};
        while(1)
        {
            recvMsgQueue(msgid, CLIENT_TYPE, buf);
            if(strcasecmp("quit", buf) == 0)
                break;
            printf("client# %s\n", buf);
    
            printf("Please enter# ");
            fflush(stdout);
            ssize_t s = read(0, buf, sizeof(buf));
            if(s>0)
            {
                buf[s-1]=0;
                sendMsgQueue(msgid, SERVER_TYPE, buf);
                printf("send done, wait recv...\n");
            }
        }
    
        destoryMsgQueue(msgid);
        return 0;
    }
    

    client.c

    #include "comm.h"
    
    int main()
    {
        int msgid = getMsgQueue();
    
        char buf[1024] = {0};
        while(1)
        {
            printf("Please Enter# ");
            fflush(stdout);
            ssize_t s = read(0, buf, sizeof(buf));
            if(s > 0)
            {
                buf[s-1]=0;
                sendMsgQueue(msgid, CLIENT_TYPE, buf);
                if(strcasecmp("quit", buf) == 0)
                    break;
                printf("send done, wait recv...\n");
            }
    
            recvMsgQueue(msgid, SERVER_TYPE, buf);
            printf("server# %s\n", buf);
        }
        return 0;
    }
    

    这里写图片描述
    这里写图片描述

    展开全文
  • 主要介绍了PHP通信-消息队列使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 操作系统课程实验报告 实习题目 进程间基于消息队列通信 指导教 学生姓名 学 号 日 期 实现工具 C 语言 实现环境 Linux 系统 实验目的 系统了解linux 系统的通信机构IPC 掌握IPC 中消息通信机 制理解消息通信的...
  • 1.2 消息队列通信过程(五步) 1 生成一个键值 2 创建或者打开一个消息队列 3 将数据放到消息队列中 4 从消息队列中读数据 5 消息队列操作 实验 进程间通信 一、实验目的: Linux系统的进程通信机构(IPC)允许在...

    实验 进程间通信

    一、实验目的:
    Linux系统的进程通信机构(IPC)允许在任意进程间大批量的交换数据。本实验的目的是了解和熟悉Linux支持的通信机制、共享存储区机制及信号量机制。

    二、实验预备内容:
    阅读Linux系统的msg.c sem.c shm.c等源码文件,熟悉Linux的三种通信机制。

    三、实验内容:

    1. 消息的创建,发送和接收
    2. 使用系统调用msgget(),msgsnd(),msgrev()及msgctl()编制一长度为1k的消息发送和接收程序。

    <程序设计>

    1. 为了便于操作和观察结果,用一个程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT,进行通信。
    2. SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接收到一个消息后显示一句“(server)received”。
    3. CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条信息后显示一句“(client)sent”。
    4. 父进程在SERVER和CLENT均退出后结束。
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    
    #define MSGKEY 75 
    
    struct msgform
    { 
      long mtype;  
      char mtext[1030];
    }msg;
    
    int msgqid,i;
    
    void CLIENT()
    { 
       int i;
       
       msgqid=msgget(MSGKEY,0777);
       
       for (i=10;i>=1;i--)
       {     
         msg.mtype=i;
         printf("(client)sent \n");
         msgsnd(msgqid,&msg,1024,0);  /*发送消息msg入msgid消息队列*/
       }
       
       exit(0);
    }
    void SERVER()
    {
      msgqid=msgget(MSGKEY,0777|IPC_CREAT);  /*由关键字获得消息队列*/
      
      do
      {
        msgrcv(msgqid,&msg,1030,0,0);   /*从msgid消息队列接收消息msg*/
        printf("(server)received \n");
      }while(msg.mtype!=1);             /*消息类型为1时,释放队列*/
      
      msgctl(msgqid,IPC_RMID,0);
      
      exit(0);
    }
    void main()
    {
       while((i=fork())==-1);//创建子进程
       if(!i) SERVER();//若子进程创建成功(i = 0),在子进程中运行SERVER
       
       while((i=fork())==-1);//创建子进程
       if(!i) CLIENT();//若子进程创建成功(i = 0),在子进程中运行CLIENT
       
       wait(0);//等待子进程结束
       wait(0);//等待子进程结束
    }
    

    在这里插入图片描述


    1. 代码详解

    1.1清楚两个概念

    1. IPC(Inter-Process Communication):进程间通信,是指两个或两个以上进程间相互通信,交换信息。
    2. 消息队列:消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应唯一的键值,这个键的数据类型为key_t,通常在头文件<sys/type.h>中被定义为长整型,键值由内核转换为标识。内核中的消息队列是通过IPC的标识符来区别的,不同的消队列之间是相互独立的。要获得一个消息队列的描述符,只需要提供该消息队列的键值即可,该键值可以在公共头文件定义,也可以用ftok函数生成键值。

    1.2 消息队列通信过程(五步)

    1 生成一个键值

     include <sys/msg.h>
     key_t ftok(const char *path, int id);   //成功返回键值 出错返回-1
    
    pathid
    must refer to an existing, accessible file,在系统中一定要存在,且进程有访问权限取值范围为1-255

    2 创建或者打开一个消息队列

     include <sys/msg.h>
     int msgqid = msgget(key_t key, int flag);
    
    • 该函数如果调用成功返回一个消息队列的描述符,否则返回-1
    • 参数key即为ftok函数的返回值。flag是一个标志参数
    flag取值
    IPC_CREATE 表示创建一个新的IPC对象或者打开已有的IPC对象
    IPC_EXCL单独使用返回-1,没有什么实际意义
    IPC_CREAT | IPC_EXCL 组合使用,用于创建一个新的IPC对象,如果键值对应的IPC对象已经存在则报错

    3 将数据放到消息队队列中

    include <sys/msg.h>
    int msgsnd(int msqid, const void *ptr, size_t msgsz, int msgflg); 
    /*成功则返回0, 出错则返回-1*/
    

    发送成功内核会更新与消息队列相关联的msqid_ds结构,指示调用者的进程ID以及调用时间,并且消息数加1。

    常见错误码
    EAGAIN说明消息队列已满
    EIDRM单独使用返回-1,没有什么实际意义说明消息队列已被删除
    EACCES 说明无权访问消息队列
    msgid 函数向msgid标识的消息队列发送一个消息
    ptr指向发送的消息
    msgsz 要发送消息的大小,不包含消息类型占用的4个字节
    msgflg操作标识位。可以设置为0或者IPC_NOWAIT。
    如果为0,则当消息队列已满的时候,msgsnd将会阻塞,直到消息可写进消息队列;
    如果msgflg 为IPC_NOWAIT,当消息队列已满的时候,msgsnd函数将不等待立即返回
    1. ptr是指向一个长整型数,表示消息类型,紧接着是消息数据。如果消息数据长度最大512,可以定义消息结构如下:
    struct mymsg
    {
       long mytype;       /*正的长整型量,通过它来区分不同的消息数据类型*/
       char msgtext[512]; /*消息数据的内容,长度是nbytes*/
    };
    

    通过设定mtype 值,我们可以进行单个消息队列的多向通讯。比如,client 可以给它向server 发送的信息赋于一个特定的mtype 值,而server 向client 的信息则用另一个mtype值来标志。这样,通过mtype 值就可以区分这两向不同的数据。nbytes参数是mymsg结构中msgtext的长度,不是结构体的长度

    1. msgqid_ds 结构被系统内核用来保存消息队列对象有关数据。内核中存在的每个消息队列对象系统都保存一个msgqid_ds 结构的数据存放该对象的各种信息。在Linux 的库文件linux/msg.h 中,它的定义是这样的:
    /* one msqid structure for each queue on the system */
    
    struct msqid_ds {
    
    struct ipc_perm msg_perm;/*保存了消息队列的存取权限以及其他一些信息 */
    
    struct msg *msg_first; /*保存了消息队列(链表)中第一个成员的地址 */
    
    struct msg *msg_last; /*保存了消息队列中最后一个成员的地址 */
    
    __kernel_time_t msg_stime; /* 保存了最近一次队列接受消息的时间 */
    
    __kernel_time_t msg_rtime; /* 保存了最近一次从队列中取出消息的时间 */
    
    __kernel_time_t msg_ctime; /* 保存了最近一次队列发生改动的时间 */
    
    struct wait_queue *wwait; /* 指向系统内部等待队列的指针 */
    
    struct wait_queue *rwait; /* 指向系统内部等待队列的指针 */
    
    unsigned short msg_cbytes; /* 保存着队列总共占用内存的字节数 */
    
    unsigned short msg_qnum; /* 保存着队列里保存的消息数目 */
    
    unsigned short msg_qbytes; /* 保存着队列所占用内存的最大字节数 */
    
    __kernel_ipc_pid_t msg_lspid; /* 保存着最近一次向队列发送消息的进程的pid */
    
    __kernel_ipc_pid_t msg_lrpid; /* 保存着最近一次从队列中取出消息的进程的pid */
    
    };
    

    1. ipc_perm。系统中,每个消息队列都维护一个结构体,此结构体保存着消息队列当前状态信息。在Linux 的库文件linux/ipc.h中,它是这样定义的:
    struct ipc_perm
    
    {
    
    key_t key;//IPC 对象的关键字
    
    ushort uid; /* owner euid and egid */
    
    ushort gid;
    
    ushort cuid; /* creator euid and egid */
    
    ushort cgid;
    
    ushort mode; /* IPC 对象的存取权限 */
    
    ushort seq; /* 系统保存的IPC 对象的使用频率信息 */
    
    };
    
    

    4 从消息队列中读数据

    include <sys/msg.h>
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, 
    			   long msgtyp, int msgflg); 
    /*功成功则返回消息数据部分的长度, 出错则返回-1*/
    

    读取成功内核会更新与消息队列相关联的msqid_ds结构,指示调用者的进程ID以及调用时间,并且消息数减1

    msgid 消息队列描述符
    ptr读取的消息存储到msgp指向的消息结构中
    msgsz 消息缓冲区的大小
    msgtype为请求读取的消息类型
    msgflg操作标志位。msgflg可以为IPC_NOWAIT, MSG_EXCEPT ,MSG_NOERROR,0:表示忽略
    msgtype
    msgtype==0读取队列中的第一个消息
    msgtype>0获取具有相同消息类型的第一个信息
    msgtype<0 获取类型等于或小于msgtype的绝对值的消息,如果有多个,返回类型值最小的消息
    msgflg
    IPC_NOWAIT如果没有满足条件的消息,调用立即返回,此时错误码为ENOMSG。
    如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。
    如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回如果进程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回
    MSG_EXCEPT与msgtype配合使用,返回队列中第一个类型不为msgtype的消息
    MSG_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将被丢弃。
    如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出。当消息从队列内取出后,相应的消息就从队列中删除了

    5 消息队列操作

     include <sys/msg.h>
     int msgctl(int msgid, int cmd, struct msgid_ds *buf);  
    

    功能:查询消息队列描述符状态,或设置描述符状态,或删除描述符。成功返回0,失败返回-1

    msgid消息队列描述符
    cmd 命令类型
    buf 消息队列头结构 msgid_ds 指针
    参数cmd
    IPC_STAT队列 id 的消息队列头结构读入 buf 中
    IPC_SET 把 buf 所指向的信息复制到 id 的消息队列头结构中
    IPC_RMID 删除 id 消息队列
    展开全文
  • 进程通信方式总结——消息队列

    千次阅读 2017-04-16 11:52:25
    Linux/Unix系统IPC是各种进程通信方式的统称,...一般来说,Linux/Unix常见的进程通信方式有:管道、消息队列、信号、信号量、共享内存、套接字等。博主将在《进程通信方式总结》系列博文中和大家一起探讨学习进
  • C#进程通信的几种方式:消息队列

    千次阅读 2019-03-07 17:09:11
    方式三:消息队列消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。消息队列(Message Queue)是在消息的传输过程中保存...
  • 利用消息队列基本函数,实现Linux系统下进程A和进程B之间的消息收发。 msgsend输入‘end’,退出程序
  • POSIX消息队列的读操作总是返回消息队列中优先级最高的最早消息,而对于SystemV消息队列可以返回任意指定优先级(通过消息类型)的消息。当向一个空消息队列中写入一个消息时,POSIX消息队列允许产生一个信号或启动...
  • Linux系统下进程通信处理比较原始的管道方式以外,消息队列也是很多应用场合考虑使用的通信方式之一。消息队列通信方式也可以称为报文队列,在系统内核中消息队列实际上是实现消息的链表结构。Linux系统下包含Posix...
  • Qt 多进程通信消息队列

    千次阅读 2017-08-07 10:52:33
    服务端: #include /* For O_* constants */ #include /* For mode constants */ #include #include #include #define MQNAME "/fifo1" mqd_t m_mq;//message queue object bool bExitThread
  • C#进程通信-消息队列代码实例。用实现了2个进程,他们之间使用消息队列方式进行通信
  • 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是...
  • Linux进程通信——使用消息队列

    万次阅读 多人点赞 2013-08-25 00:09:57
    下面来说说如何用不用消息队列来进行进程间的通信消息队列与命名管道有很多相似之处。 一、什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,...
  • 消息队列允许一个或多个进程向它写入与读取消息. 3.管道和命名管道都是通信数据都是先进先出的原则。 4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIF
  • 消息队列允许一个或多个进程向它写入与读取消息.  3.管道和命名管道都是通信数据都是先进先出的原则。  4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更...
  • linux进程通信———Posix消息队列简介及基础库函数 引言:消息队列可认为是一个消息链表,有足够写权限的线程可向队列中放置消息,有足够读权限的线程可从队列中取走消息。本篇笔记将简要介绍Posix消息队列特性、...
  • 通过对进程通信消息队列)的设计,深入理解进程之间是如何通过消息 队列进行通信的。 实验原理 消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。 对消息队列有写权限的...
  • #使用队列,将消息写进队列,需要的进程队列取 #队列由父进程创建,子进程共享队列 def write(qe): print("启动子进程 write") for chr in ['A','B','C','D','E']: qe.put(chr) time.sleep(1)...
  • 当一个进程收到多个消息时,可将它们排成一个消息队列。 1、消息机制的数据结构 (1)消息首部 记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。 (2)消息队列头...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 192,772
精华内容 77,108
关键字:

进程通信消息队列