进程间通信方式--消息队列

消息队列作为进程间通信的一种方式,允许进程发送数据块并通过消息类型进行区分。每个数据块都有最大长度限制。Linux提供了msgget、msgsnd、msgrcv和msgctl等函数接口来创建、发送、接收和控制消息队列,实现进程间的通信。消息队列采用链表结构,由内核维护,通过消息队列标识符(qid)区分。
摘要由CSDN通过智能技术生成

消息队列

  消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。消息队列存放在内核中并由消息队列标识符标识,是一种链表队列。
  我们可以通过发送消息来避免命名管道的同步和阻塞问题。但消息队列与命名管道一样,每个数据块都有一个最大长度的限制。我们可以将每个数据块当作是一种消息类型(频道),发送和接收的内容就是这个类型(频道)对应的消息(节目),每个类型(频道)相当于一个独立的管道,相互之间互不影响。Linux提供了一系列消息队列的函数接口来让我们方便地使用它来实现进程间的通信。它的用法与其他两个System V IPC机制,即信号量和共享内存相似。

消息队列的原理

  MQ 传递的是消息,消息即是我们需要在进程间传递的数据。MQ 采用链表来实现消息队列,该链表是由系统内核维护,系统中可能有很多的 MQ,每个 MQ 用消息队列描述符(消息队列 ID:qid)来区分,qid 是唯一的,用来区分不同的 MQ。在进行进程间通信时,一个进程将消息加到 MQ 尾端,另一个进程从消息队列中取消息(不一定以先进先出来取消息,也可以按照消息类型字段取消息),这样就实现了进程间的通信。如下 MQ 的模型:
在这里插入图片描述
进程 A 向内核维护的消息队列中发消息,进程 B 从消息队列中取消息,从而实现了 A 和 B 的进程间通信。

消息队列读取数据工作模式

在这里插入图片描述

Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。

消息队列的三个数据结构

  • linux内核采用msqid_ds用来设置或返回消息队列的信息,存在于用户空间。
struct msqid_ds
  {
    struct ipc_perm msg_perm;  //消息队列访问权限
    struct msg *msg_first;    //指向第一个消息的指针
    struct msg *msg_last;     //指向最后一个消息的指针
	ulong  msg_cbytes;       //消息队列当前的字节数
	ulong  msg_qnum;        //消息队列当前的消息个数
	ulong  msg_qbytes;     //消息队列可容纳的最大字节数
	pid_t  msg_lsqid;     //最后发送消息的进程号ID
	pid_t  msg_lrqid;     //最后接收消息的进程号ID
	time_t msg_stime;     //最后发送消息的时间
	time_t msg_rtime;     //最后接收消息的时间
	time_t msg_ctime;    //最近修改消息队列的时间
};
  • linux内核采用的结构msg_queue来描述消息队列头信息,存在于系统空间
struct msg_queue {
    structkern_ipc_perm q_perm;
    time_tq_stime;         /* last msgsndtime */
    time_tq_rtime;         /* last msgrcvtime */
    time_tq_ctime;         /* last changetime */
    unsignedlong q_cbytes;     /* current number of bytes on queue*/
    unsignedlong q_qnum;       /* number of messages inqueue */
    unsignedlong q_qbytes;     /* max number of bytes on queue */
    pid_tq_lspid;          /* pid oflast msgsnd */
    pid_tq_lrpid;          /* lastreceive pid */
    structlist_head q_messages;
    structlist_head q_receivers;
    structlist_head q_senders;
};      
  • syetem V IPC 为每一个IPC结构设置了一个ipc_perm结构,该结构规定了许可权和所有者。
struct ipc_perm
{
   key_t  key;         //调用shmget()时给出的关键字
   uid_t  uid;        //共享内存所有者的有效用户ID 
   gid_t  gid;        //共享内存所有者所属组的有效组ID 
   uid_t  cuid;       //共享内存创建 者的有效用户ID
   gid_t  cgid;      //共享内存创建者所属组的有效组ID
  unsigned short  mode; //Permissions + SHM_DEST和SHM_LOCKED标志
  unsignedshort   seq;         //序列号
}

在这里插入图片描述

与消息队列有关的函数

(1)msgget:创建或使用消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

/*
 * key:用来指定返回 MQ 的 ID
 * msgflg:创建的标志,例如 IPC_CREAT
 * return:成功返回队列 ID,失败返回 -1, 并设置 erron
 */
int msgget(key_t key, int msgflg);

  注意:msgflg是权限标志位,可以设置为IPC_CREAT表示创建消息队列,如果存在就直接打开;也可以设置为IPC_CREAT | IPC_EXCL表示创建消息队列,如果key所命名的消息队列存在时,就返回错误。

  在程序中若要使用消息队列,必须要能知道消息队列的key,因为应用进程无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标识,让应用进程知道当前操作的是哪个消息队列,同时也要保证每个消息队列key值的唯一性。

  申请一块内存,创建一个新的消息队列(数据结构msqid_ds),将其初始化后加入到msgque向量表中的某个空位置处,返回标示符。或者在msgque向量表中找键值为key的消息队列。

(2)msgsnd:添加消息到消息队列中(即发送)

如果函数调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.
在这里插入图片描述
msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。

(3)msgrcv:从消息队列中取走消息(读取消息)

   使用 msgrcv 来从 msgqid 标识的 MQ 中读取一个消息放到 msgp 指定的内存(缓冲区)中,必须要有读消息队列的权限。
  函数调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.在这里插入图片描述

(4)msgctl:控制消息队列(即:获得或修改消息队列或者删除消息队列)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

/*
* msqid:消息队列 ID
* cmd:控制命令,例如 IPC_RMID 删除命令
* buf:存储消息队列的相关信息的 buf
* return:成功根据不同的 cmd 有不同的返回值,失败返回 -1, 并设置 erron
*/
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

command是将要采取的动作,它可以取3个值,

  • IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
  • IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值。
  • IPC_RMID:删除消息队列。

使用消息队列来进行进程间通信

一个进程A写入字符串到消息队列,另一个进程B从队列中取出数据。

发送消息的一方:

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

struct msgbuf {
	long mtype;
	char mtext[255];
};

int main(){
	//1,创建一个消息队列,用key=123来唯一表示这个队列
	int msg_id = msgget(123, IPC_CREAT | 0666);
	if(msg_id != -1){
		//2,初始化要发送的消息
		struct msgbuf mybuf;
		mybuf.mtype = 1;
		strcpy(mybug.mtext, "I'm send process.\n");

		//3,发送消息
		if(msgsnd(msg_id, &mybuf, sizeof(mybuf.mtext), 0)){
			printf("success.\n");
		}
		else
			perror("msgsnd:");
	}else{
		perror("msgget:");
	}
	return 0;
}

接收消息的一方:

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

struct msgbuf {
    long mtype;
    char mtext[255];
};

int main(){
	//1,获取消息队列
	int msg_id = msgget(123, IPC_CREAT | 0666);
	if(msg_id != -1){
		struct msgbug mybuf;
		//2,接收第一条消息,存到mybuf中
		if(msgrcv(msg_id, &mybuf, sizeof(mybuf.mtext), 0, IPC_NOWAIT) != -1){
			printf("read success: %s\n", mybuf.mtext);
			//3,接收完消息,就删除这个消息队列
			if(msgctl(msg_id, IPC_RMID, 0) != -1){
				printf("delete msg success\n");
			}else{
				perror("msgctl:");
			}
		}
	}
	else{
		perror("msgget:");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值