进程通信之消息队列

目录

1、IPC对象

2、查看IPC对象的指令

3、IPC对象 的 key值申请

4、消息队列

5、使用消息队列通信的步骤

 6、消息队列相关函数说明

7、消息队列测试代码

1、IPC对象

在linux下,IPC对象指的是 消息队列、共享内存、信号量

如果用户需要使用IPC对象来进行进程之间的通信,首先必须为IPC对象申请对应的资源

IPC对象是进程通信方式的子集

信息队列:Message Queuing 共享内存:Shared memory 信号量:Semaphores

1)想要操作文件(设备文件/dev): 需要获得的资源: >文件的路径名 >文件的文件描述符

2)想要操作消息队列: 需要获得的资源: >需要获得key值------文件的路径名 >ID号 -----文件的文件描述符

2、查看IPC对象的指令

1)查看IPC对象: ipcs -a

key值: 类似于 文件的路径名

ID号: 类似于 文件描述符

2)删除IPC对象

想删除消息队列: ipcrm -q 消息队列的key值 / ipcrm -q 消息队列的ID值 (ipc remove -queue)

想删除共享内存: ipcrm -m 共享内存的key值 / ipcrm -m 共享内存的ID值 (ipc remove -memory)

想删除信号量: ipcrm -s 信号量的key值 / ipcrm -s 信号量的ID值 (ipc remove sem)

3、IPC对象 的 key值申请

申请key值    ftok  --> man 3 ftok
#include <sys/types.h>
#include <sys/ipc.h>
key的定义:key_t key;
key_t ftok(const char *pathname, int proj_id);
函数作用: 获得一个key值
参数:pathname: 一个合法的路径。  常用 "."
    proj_id: 非0整数。   常用 10
返回值:成功 key值
    失败 -1

  The resulting value is the same for all pathnames that  name  the  same file,  when  the  same  value  of  proj_id is used.  
 当文件路径pathname 和 proj_id 是一样的时候,两个ftok函数的返回值---key是一样的。
 
 The value returned should be different when the (simultaneously  existing)  files  or  the project IDs differ.
  只要文件路径pathname 或者 proj_id 有一个不一样,返回的key值 就是不一样的。
  
  结论:如果想要使用IPC对象实现两个进程之间的通信,那么两个进程的IPC对象的key值必须是一样的。

4、消息队列

消息队列是属于IPC对象,所以使用之前一定要先申请key值

2.管道通信跟消息队列的区别

1)管道通信: 不能读取指定的数据,只要管道中有数据,就一定要读取出来,操作的时候使用 open / write read -->文件描述符(系统IO来操作 write read)

2)消息队列: 消息队列是一种带有数据标识的特殊管道,消息队列可以读取指定的数据,如果里面有多个数据,但是不符合我的类型,我可以不读取, 操作时候使用消息队列中独有的函数接口:msgsnd / msgrcv

消息队列机制:

进程1往消息队列中写入数据时,“类型”+ “数据正文” //类型 ---数据的编号

进程2从消息队列中读取数据时,只需要提供数据的编号就可以读取到指定的数据了

消息队列作用的范围: linux下任意两个进程

5、使用消息队列通信的步骤

1.申请消息队列的key值

2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget

3.发送 /写入数据 ----> man 2 msgsnd

4.从消息队列中读取数据(接收数据) --->man 2 msgrcv

5.删除消息队列 msgctl = message contol

 6、消息队列相关函数说明

发送端和接收端都需要定义一个数据结构体,并且使用memset对结构体对象清0
struct msgbuf 
{
    long mtype;       // 消息类型/数据的编号  而且是>0
    char mtext[1024];    // 数据的正文 
};

根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 --->  man 2 msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgid=msgget(key_t key,IPC_CREAT|0666);
int msgget(key_t key, int msgflg);
函数作用:得到一个消息队列的ID号
参数:key:消息队列的key值
    msgflg: IPC_CREAT|0666  如果不存在则创建。并且给权限
返回值:成功返回 消息队列的ID号
    失败返回 -1

从消息队列中写入数据 --->  man 2 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:消息队列的ID号
      msgp:你要写入的数据,注意传递的是写入的数据结构体的地址
      msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
      msgflg:一般属性,默认为0
返回值:成功返回 0
        失败返回 -1
 
从消息队列中读取数据(接收数据) -->  man 2 msgrcv
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:msqid:消息队列的ID号
      msgp:读取的数据存储到这里,注意 传递的是 读取的数据结构体的地址
      msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
      msgtyp:读取的数据的类型或者说数据的编号
      msgflg:一般属性,默认为0
返回值:成功返回  读取的字节数
        失败 返回 -1
 
删除消息队列  msgctl (message contol)   -->  man 2 msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //cmd --->command
参数:msqid:你要删除哪条消息队列,将消息队列的ID号传递过来
    cmd: 操作的命令
    IPC_STAT -->获取消息队列的状态  -->  最后一个参数要填
         struct msqid_ds 变量名(&变量名)
    IPC_RMID-->删除消息队列 --->  删除不需要第三个参数,但是要填NULL
    buf: 获取的那些数据 存储到这个结构体里面
返回值:成功返回 0
        失败返回 -1

比如删除消息队列:
msgctl(msgid,IPC_RMID,NULL);(msg的通常用法)
父进程利用sleep,多数是子进程使用msgctl删除消息队列

7、消息队列测试代码如下:

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>

struct msgbuf
{
	long mtype;        //根据结构体的类型变量来决定读取哪种类型的数据
	char mtext[1024];
};


//消息队列_rd.c
int main()
{
    //1、申请key值
	key_t key = ftok(".",10);
	
    //2、获取id
	int msgid = msgget(key,IPC_CREAT|0666);
	if(msgid == -1)
	{
		printf("msgget msgid fail\n");
		return -1;
	}
	printf("消息队列 key:%#x msgid:%d\n",key,msgid);
	
    //3、初始化消息队列信息结构体
	struct msgbuf msgdata;
	memset(&msgdata,0,sizeof(struct msgbuf));
	msgdata.mtype = 20;
		
	pid_t id = fork();
	if(id < 0)
	{
		printf("fork fail\n");
		return -1;
	}
	else if(id > 0)
	{
		while(1)
		{
			//printf("请输入消息队列正文:");
			scanf("%s",msgdata.mtext);
			
            //发送消息
			msgsnd(msgid,&msgdata,strlen(msgdata.mtext),0);
			if(strcmp(msgdata.mtext,"byebye") == 0)
			{
				break;
			}
		}
		wait(NULL);
		msgctl(msgid,IPC_RMID,NULL);
	}
	else if(id == 0)
	{
		while(1)
		{
			memset(&msgdata,0,sizeof(struct msgbuf));
			
            //接收消息
			msgrcv(msgid,&msgdata,sizeof(msgdata),10,0);
			
			printf("子进程:消息队列的正文:%s\n",msgdata.mtext);
			if(strcmp(msgdata.mtext,"byebye") == 0)
			{
				break;
			}
		}	
	}
	
	return 0;
}

//消息队列_wr.c
int main()
{
    //1、申请key值
	key_t key = ftok(".",10);
	
    //2、获取id
	int msgid = msgget(key,IPC_CREAT|0666);
	if(msgid == -1)
	{
		printf("msgget msgid fail\n");
		return -1;
	}
	printf("消息队列 key:%#x msgid:%d\n",key,msgid);

    //3、初始化消息队列信息结构体
	struct msgbuf msgdata;
	memset(&msgdata,0,sizeof(struct msgbuf));
	msgdata.mtype = 10;
	
	pid_t id = fork();
	if(id < 0)
	{
		printf("fork fail\n");
		return -1;
	}
	else if(id > 0)
	{
		while(1)
		{
			//printf("请输入消息队列正文:");
			scanf("%s",msgdata.mtext);
			
            //发送消息
			msgsnd(msgid,&msgdata,strlen(msgdata.mtext),0);
			if(strcmp(msgdata.mtext,"byebye") == 0)
			{
				break;
			}
		}
		wait(NULL);
		msgctl(msgid,IPC_RMID,NULL);
	}
	else if(id == 0)
	{
		while(1)
		{
			memset(&msgdata,0,sizeof(struct msgbuf));
			
            //接收消息
			msgrcv(msgid,&msgdata,sizeof(msgdata),20,0);
			
			printf("子进程:消息队列的正文:%s\n",msgdata.mtext);
			if(strcmp(msgdata.mtext,"byebye") == 0)
			{
				break;
			}
		}	
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值