消息队列MSG(多进程ipc)

进程间通讯(ipc)之消息队列:

1.在任意进程间有选择性地进行通讯的方式

2.发送数据时需要携带一定的消息标志

3.消息队列创建后由于是存在于内核中由内核管理,使用过后需要人工删除以释放资源

4.当一个发送方往消息队列中发送数据时,接收方一直还没接收,数据会一直在消息队列里等待接收方的出现

这是关于ipc的一些操作,点击跳转


消息队列图解

我们换个通俗点的说法:小红和小绿是同班同学,小绿喜欢小红,但是小红不喜欢小绿,很明显哪怕他们在一起了也是没有结果的,爱情是要双方互相喜欢的情况下才会有好的结果,于是在我们没有遇到生命中的那个人时我们就会等他/她的出现;消息队列就如爱情❤,如果他们的消息标识符不一致是不能进行通讯的,它会一直等待接收(或发送)它消息的进程出现

相关函数原型:
//创建键值函数
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>

//函数原型
key_t ftok(const char *pathname, int proj_id);

//函数介绍:
/********************************************************
函数作用:用于创建一个键值,
				键值作用 :用于进程间通讯(ipc)时的一个标识符
				
形参:pathname:路径名-->通过哪一个路径来获得键值

	 proj_id( project identifier):项目id:即我们指定给它一个8位(一个字节,最大256)的整数,


注:其实可以看成路径下的消息队名为给它的数

返回值:
		成功:新生成且未被使用的键值
		失败:-1


********************************************************/

//获取消息队列的id号,也可使用参数在不存在的情况下创建
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

//函数原型
int msgget(key_t key, int msgflg);

//函数介绍:
/********************************************************
函数作用:用于获取一个消息队列的id号或者创建一个消息队列

形参:key:键值,通过使用ftok函数创建返回的一个键值

	 msgflg:所对应的操作:
	 					IPC_CREAT :键值key所对应的消息队列不存在则创建
(创建时需要给它一个权限使用方法类似于open函数,在后面使用位或或上操作权限)
						
						O_EXCL:键值key对应的消息队列存在则报错
	 						
注:当键值key被指定为IPC_PRIVATE时,系统自动分配一个键值来对应消息队列

返回值:
		成功:返回消息队列的标识符
		失败:-1


********************************************************/
测试代码:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>

int main(int argc,char **argv)
{
	//合成键值,在当前目录下合成键值
	key_t key = ftok(".",1);
	if(key == -1)
	{
		perror("ftok failed");
		return -1;
	}

	//创建消息队列,返回一个msgid
	int msgid = msgget(key,IPC_CREAT | 0666);
	if(msgid == -1)
	{
		perror("msgget failed");
		return -1;
	}

	return 0;
}

(1)当路径与pro_t值都一样时,返回的是同一个值(即合成的键值是同一个数)
(2)合成键值其实有点类似于创建一个文件的(但本质不一样),我们指定一个路径加上我们自定义的一个数字(类似我们指定一个路径下创建一个名字为什么的文件)


关于消息队列用于通讯的函数
//消息队列用于发送和接收的函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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


/********************************************************
函数作用:

形参:msqid:通过msgget函数获取到的消息队列id号

	 msgp:一个自己定义的消息队列结构体的地址
	 		这个结构体一般形式有:
	 		struct msgbuf {
            	long mtype;       //消息的标识符,且必须大于0
            	char mtext[1];   //消息正文,数组大小由自己定义
           };
           注:结构体第一个元素必须为long类型的标识符!!!
     
     msgsz:发送数据的正文的大小,取决于消息队列结构体中mtext数组的大小

	msgtyp:消息队列接收函数中用于选择接收的数据的消息标识符

	msgflg:参数有
				0 :阻塞写入或读取数据
				IPC_NOWAIT:往消息队列或写入或读取数据时不阻塞等待
				MSG_NOERROR:消息长度超出 msgsz时截断信息且不报错
				 		

返回值:
	msgsnd:
		成功: 0
		失败:-1
	msgrcv:
		成功:读取到的字节数
		失败:-1

********************************************************/


//获取或设置删除消息队列函数
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

//函数原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

/********************************************************
函数作用:获取消息队列属性,设置消息队列属性或删除消息队列

形参:msqid:通过msgget函数获取到的消息队列id号

	 cmd:IPC_STAT:获取消息队列的属性,储存到struct msqid_ds *buf中
	 	  IPC_SET:把消息队列的属性设置为struct msqid_ds *buf中的属性
	 	  IPC_RMID:删除消息队列
	 	  IPC_INFO:获取当前系统消息队列的限制值信息
	 	  MSG_INFO:获取当前系统消息队列的相关资源消耗情况
	 	  
     buf:一个struct msqid_ds类型的变量的地址,这个结构体的成员有:
     struct msqid_ds {
               struct ipc_perm msg_perm;     //消息队列的所有权和权限
               time_t          msg_stime;    //消息队列最后一次发送信息的时间
               time_t          msg_rtime;    // 最后一次一次接收信息的时间
               time_t          msg_ctime;    // 最后一次更改消息队列状态时间 
               unsigned long   __msg_cbytes; //当前消息队列的数据大小
               msgqnum_t       msg_qnum;     //当前消息队列的消息个数
               msglen_t        msg_qbytes;   //消息队列最大的数据大小
               pid_t           msg_lspid;    //最后一次发送信息的进程id号
               pid_t           msg_lrpid;    //最后一次接收信息的进程id号
              };
              
返回值:
		成功:
		  IPC_STAT:0
	 	  IPC_SET:0
	 	  IPC_RMID:0
	 	  IPC_INFO:内核中记录所有消息队列信息的数组的下标最大值
	 	  MSG_INFO:内核中记录所有消息队列信息的数组的下标最大值
		失败:-1


********************************************************/

测试代码:

发送端:

/*
	消息队列发送
 */
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>

//设计消息队列结构体,数组大小为30
struct msgdata
{
	long type;
	char data[30];
};


int main()
{
	//申请一个消息队列结构体变量
	struct msgdata msg_sed;
	//清空一下结构体变量
	bzero(&msg_sed,sizeof(msg_sed));

	//产生键值,第二个值可以为随机值,只要前面创建其他队列没有使用过
	key_t key = ftok(".",1);
	if(key == -1)
	{
		//创建失败直接退出
		perror("ftok failed");
		return -1;
	}
	
	//创建消息队列给0666权限,返回一个msgid
	int msgid = msgget(key,IPC_CREAT | 0666);
	if(msgid == -1)
	{
		//创建失败直接退出
		perror("msgget failed");
		return -1;
	}

	//不断发送数据
	while(1)
	{
		//每次发送前清空一下结构体里的内容
		bzero(&msg_sed,sizeof(msg_sed));

		printf("请输入需要发送的数据:");
		//发送类型为1
		msg_sed.type = 1;
		fgets(msg_sed.data,30,stdin);

		//打印一下字符串长度和内容
		//printf("%ld\n",strlen(msg_sed.data) );
		//printf("%s\n",msg_sed.data );
		//往消息队列发送数据
		msgsnd(msgid,&msg_sed,strlen(msg_sed.data),0);
	}
	
	//删除消息队列
	msgctl(msgid,IPC_RMID,NULL);

	return 0;
}

接收端:


/*
	消息队列接收
 */

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

//设计消息队列结构体
struct msgdata
{
	long type;
	char data[30];
};


int main()
{
	//申请一个消息队列结构体变量
	struct msgdata msg_rcv;
	//
	key_t key = ftok("./",1);
	if(key == -1)
	{
		perror("ftok failed");
		return -1;
	}
	
	//获取消息队列id号,如果不存在则创建
	int msgid = msgget(key,IPC_CREAT | 0666);
	if(msgid == -1)
	{
		perror("msgget failed");
		return -1;
	}

	//接收数据
	while(1)
	{
		//每次清空一下结构体变量里的内容
		bzero(&msg_rcv,sizeof(msg_rcv));
		
		//msgrcv(msgid,&msg_rcv,30,1,0);
		//从消息队列读取数据,并判断是否读取成功
		int ret = msgrcv(msgid, &msg_rcv, 30 ,1, 0);
		if(ret == -1)
		{
			perror("error");
		}
		printf("接收到的数据%s", msg_rcv.data);

	}
	
	//删除消息队列
	msgctl(msgid,IPC_RMID,NULL);

	return 0;
}

运行结果

在这里插入图片描述



总结:
(1)管道与消息队列都是由内核来管理的进程间通讯机制
(2)消息队列类似于管道却又比管道更加人性化,管道的读端只要是管道里有消息都会将其读走,而消息队列是有选择性地去读取数据,只要不是它想要地都不会读走
(3)当消息队列的写端往消息队列写入数据时,读端还没打开,数据会一直在消息队列中等待它读端打开
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值