Linux——进程间通信(消息队列)

消息队列

管道和共享内存:字节流数据
消息:数据报(类型+数据)
队列:优先级队列
可以指定类型来读取,在相同类型下,按照先进先出的顺序
因此:消息队列就是一个消息的链表,每个消息都有一个队列头,在内核中存放着消息队列的数据结构(struct ipc_ids msg_ids),系统中所有消息队列都可以在结构msg_ids中找到访问入口。消息队列用struct msg_queue来描述,队列头中包含了该消息队列的大量信息,包含消息队列键值、用户ID、组ID和消息队列中的消息数目等,甚至记录了最近对消息队列读写进程的ID,读者可以访问这些消息,甚至可以设置其中的,某些信息。
Linux——进程间通信(消息队列)_第1张图片
Linux——进程间通信(消息队列)_第2张图片

上图描述了内核与消息队列建立起联系的机制,,每一个全局数据结构struct ipc_ids msg_ids都可以访问每个消息队列头的第一个成员struct kern_ipc_perm ,而每一个struct kern_ipc_perm能够与具体的消息队列对应起来,因为在该结构中,有一个key_t成员,它能够唯一确定一个消息队列

struct kern_ipc_perm
{
	key_t		key;     //关键字
//对象拥有者对应进程的有效用户识别号和有效组识别号
	uid_t		uid;     
	gid_t		gid;
//对象创建者对应进程的有效用户识别号和有效组识别号
	uid_t		cuid;    
	gid_t		cgid;
			mode_t		mode;   //存取模式
			unsigned long	seq;   // 序列号
}

消息队列的API:
①ftok函数

该函数一般不直接对消息队列操作,但是在调用ipc(MSGGET,…)或msgget来获取消息队列描述字前,往往要调用该函数Linux——进程间通信(消息队列)_第3张图片
②创建和访问一个消息队列
#include
#include
#include

注意:从队列中取出一个消息最重要的依据就是消息的类型,因此对于发送消息时,需要预置一个msgbuf缓冲区并写入消息的类型和内容,调用相应的发送函数即可,对于读取消息,首先分配一个msgbuf缓冲区,然后把消息读入缓冲区即可。

int msgget(key_t key, int msgflag);
  • key:某个消息队列的名字,给一个整型值即可,即消息队列唯一的键值,也可用ftok产生,
  • msgflag:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。
  • 返回值成功返回一个非负整数,即消息队列的标识码,失败返回-1
    2.把一条消息添加到消息队列中
int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);
  • msgid:由msgget函数返回的消息队列标识码
  • msqp:指针指向准备发送的消息
  • msqsz:消息结构中数据部分的大小,不包括类型大小
  • msgflg:默认为0
  • 返回值:成功返回0,失败返回-1

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

struct msgbuf 
{
	long mtye;    //消息类型,必须大于0
	char mtext[1]; //消息数据
}; 

3.从一个消息队列接受消息

ssize_t msgrcv(int msqid, void *msqp, size_t msqsz, long msqtyp, int msqflg);

参数:与msgsnd相同

  • msgid:由msgget函数返回的消息队列标识码
  • msqp:消息返回后存储在msqp执行的msgbuf结构中
  • msqsz:消息结构中消息内容的长度,不包括类型大小
  • msgflg:默认为0
    long msqtyp:为请求读取的消息类型
    返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
    如果指定的空间不能将消息的数据完全存储下来,则本次读取不成功

4.消息队列的控制函数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • msqid:由msgget函数返回的消息队列标识码
  • cmd:有三个可选的值

IPC_STAT :该命令用来获取消息队列的队列信息,返回的信息存储在buf指向的msqid结构中;
IPC_SET :该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中,可设置的属性包括msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes,同时也影响msg_ctime
成员
IPC_RMID 删除msqid标识的消息队列

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

和之前一样,我们写两个程序,a向消息队列中写入数据,b从消息队列中读取数据。
Linux——进程间通信(消息队列)_第4张图片

Linux——进程间通信(消息队列)_第5张图片
完成下面的操作:

先连续发送三条消息:
Linux——进程间通信(消息队列)_第6张图片
然后,连续接收两个消息后发现消息队列中还剩下最后一条消息:
Linux——进程间通信(消息队列)_第7张图片
一旦消息队列中的消息被读完或者,读取不存在的消息,比如2号类型消息,那么终端显示就是阻塞状态

注意:在接收消息的时候,如果将消息类型改为0,不代表接收0号消息,而是不区分消息类型,直接接收,最后靠接受者自己使用

msg.h

#program once
typedef struct msgbuf
{
	long mtype;
	char mtext[128];
}MsgBuf;

发送

#include
#include
#include
#include
#include

#include
#include
#include

#include"msg.h"

int main(int argc,char *argv[])
{
	//argv[1]:type
	//argv[2]:data
	if(argc < 3)
	{
		printf("Please input type and data\n");
		exit(0);
	}
	
	MsgBuf mess;
	memset(&mess,0,sizeof(mess));
	sscanf(argv[1],"%d",&mss.mtype);
	strcpy(msss.mtext,argv[2]);
	
	int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
	assert(msgid != -1);

	msgsnd(msgid,&mess,strlen(mess,mtext),0);
	
	exit(0);
}

接收(注意读取问题)

#include
#include
#include
#include
#include

#include
#include
#include

#include"msg.h"

int main(int argc,char *argv[])
{
	if(argc < 2)
	{
		printf("please input type\n");
		exit(0);
	}
	
	MsgBuf mess;
	memset(&mess,0,sizeof(mess));
	
	long type = 0;
	sscanf(argv[1],"%d",&type);
	
	int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
	assert(msgid != -1);
	
	msgrcv(msgid,&mess,127,type,0);//改成5看看结果,消息截取
	printf("type:%d,data :%s\n",mess.mtype,mess.mtext);
	
	exit(0);
}

注意:每一个消息队列的容量都有限制,该值因为系统不同而不同;另一个限制就是每个消息所能容纳的最大消息数,收消息队列的容量制约,消息个数要小于消息队列的容量(字节数)。

简单总结:
Linux——进程间通信(消息队列)_第8张图片

你可能感兴趣的:(Linux,linux)