进程间通讯--消息队列

消息队列

概念

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制

本质

是存储在内核中的一个消息的队列(链表)

特点

  • 和管道一样,每个消息的最大长度是有上限的(MSGMAX)(16),每个消息队列的总字节数也是有上限的(MSGMNB)(8192),系统上的消息队列总数也是有上限的(MSGMNI)(16384)
  • 是一个全双工通信,可读可写,双向通讯。
  • 生命内核随周期,关机即结束消息队列。

查看消息队列

命令行输入 ipcs -q

运行结果示例如下
在这里插入图片描述

IPC对象数据结构

IPC对象数据结构:/usr/include/linux/ipc.h

struct ipc_perm 
{ 
    __kernel_key_t  key; 
    __kernel_uid_t  uid; 
    __kernel_gid_t  gid; 
    __kernel_uid_t  cuid; 
    __kernel_gid_t  cgid; 
    __kernel_mode_t mode;  
    unsigned short  seq; 
};

消息队列数据结构

消息队列结构:/usr/include/linux/msg.h

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 */
	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 */
	__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 */
};

消息结构

struct msgbuf {
    long mtype;         /* type of message */
    char mtext[1];      /* message text */
};     

函数

msgget(创建消息队列)

int msgget(key_t  key, int msgflg); //成功时返回消息队列ID,出错返回-1

key:内核中消息队列的标识

msgflg:

IPC_CREAT    不存在则创建,存在则打开

IPC_EXCL      与IPC_CREAT同时使用,存在则报错

msgrcv(从消息队列接收消息)

int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
//调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1。
  • msgid:msgget函数返回的消息队列标识符。
  • msg_ptr:指向准备接收消息的结构体指针
  • msg_st:msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。
  • msgtype:msgtype可以实现一种简单的接收优先级,消息是按照接收顺序存于消息队列中,每条消息对应一个消息类型号(频道号)。如果msgtype为0,就获取队列中的第一条消息。如果它的值大于零,将获取具有相同类型号(频道号)的第一条消息。如果它小于零,就获取频道号等于或小于msgtype的绝对值的第一个消息。
  • msgflg:用于控制当队列中没有相应类型的消息可以接收时将发生的事情。msgflg=0,阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待;msgflg=IPC_NOWAIT,如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG;msgflg=IPC_EXCEPT,与msgtype配合使用返回队列中第一个类型不为msgtype的消息;msgflg=IPC_NOERROR,如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃。

msgsnd (给消息队列发送消息)

int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
//如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1。msgflg=0,当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列;msgflg=IPC_NOWAIT,当消息队列已满的时候,msgsnd函数不等待立即返回;
  • msgid:msgget函数返回的消息队列标识符。
  • msg_ptr:指向准备发送消息的结构体指针。
  • msg_sz:msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。
  • msgflg:用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。

msgctl(释放消息队列)

int msgctl(int msgid, int command, struct msgid_ds *buf);
//返回值:成功时返回0,失败时返回-1。
  • msgid:msgget函数返回的消息队列标识符。

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

    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。

    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值

    IPC_RMID:删除消息队列

  • buf:指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。

ftok(通过文件的inode节点号和一个proj_id计算出一个key值)

key_t ftok(const char *path, int id); 

缺点:如果文件被删除或替换生,通过key值打开的不是同一个msgqueue

示例代码

server1.c

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

#define TYPE_SER 1
#define TYPE_CLI 2

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

int main()
{
	int msgid = -1;

    int	key = ftok("./fork.c", 'a');  //fork.c是我文件夹中任一文件 记得替换成自己的文件
	if(key < 0){
		perror("ftok error");
		return -1;
	}

	msgid = msgget(key, IPC_CREAT | 0664);
	if (msgid < 0) {
		perror("msgget error");
		return -1;
	}
	while(1){
		struct msgbuf buf;

		memset(&buf, 0x00, sizeof(struct msgbuf));
		buf.mtype = TYPE_CLI;
		scanf("%s", buf.mtext);
		msgsnd(msgid, &buf, 1024, 0);

		msgrcv(msgid, &buf, 1024, TYPE_SER, 0);
		printf("lzr:[%s]\n", buf.mtext);
	}
	msgctl(msgid, IPC_RMID, NULL);
	return 0;
}

client1.c

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

#define TYPE_SER 1
#define TYPE_CLI 2

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

int main()
{
	int msgid = -1;

	int key = ftok("./fork.c", 'a');  //fork.c是我文件夹中任一文件 记得替换成自己的文件 保证客户端和服务器的文件名相同
	if(key < 0){
		perror("ftok error");
		return -1;
	}

	msgid = msgget(key, IPC_CREAT | 0664);
	if(msgid < 0){
		perror("msgget error");
		return -1;
	}
	while(1){
		struct msgbuf buf;

		msgrcv(msgid, &buf, 1024, TYPE_CLI, 0);
		printf("sx:[%s]\n", buf.mtext);

		memset(&buf, 0x00, sizeof(struct msgbuf));
		buf.mtype = TYPE_SER;
		scanf("%s", buf.mtext);
		msgsnd(msgid, &buf, 1024, 0);
	}
	msgctl(msgid, IPC_RMID, NULL);
	return 0;
}

运行结果如下图:
在这里插入图片描述
查看消息队列如下图所示:
在这里插入图片描述
注:此代码我更换过key,所以会出现两个key
不过此代码也有不足的地方,因为最佳体现方式是服务器和客户轮流发送消息,否则通讯效果不佳,后续我会尝试用消息队列和父子进程的方式尝试是否能解决此类问题。
如有疑问或者质疑的地方欢迎评论区留言哦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux下,QT可以使用mqueue消息队列实现进程间通信。为了使用消息队列,需要包含头文件#include <mqueue.h>,并在pro文件中添加编译选项LIBS = -lrt。消息队列具有以下特征:可以设置最大消息个数和每个消息的最大字节数,可以向消息队列中写入多条消息,而其他进程读取一条消息后,消息队列就会删除这条消息。 要发送消息到消息队列中,可以使用mq_send函数。该函数的原型是int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio)。其中,mqdes是打开消息队列时返回的消息队列描述符,ptr是指向要发送的消息的指针,len是消息的长度,prio是消息的优先级。 在应用中使用消息队列进行进程间通信,可以发现消息队列具有强大的功能。它可以方便地管理线程,实现互斥量和条件变量等功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Qt实现IPC进程间通信-mqueue消息队列](https://blog.csdn.net/weixin_40355471/article/details/113178838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [进程间通信之深入消息队列的详解](https://download.csdn.net/download/weixin_38517212/14872197)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值