百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Linux系统编程——进程间通信:消息队列

liebian365 2024-11-03 15:46 6 浏览 0 评论

概述

消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法,其特点如下:

  1. 消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
  2. 消息队列允许一个或多个进程向它写入或者读取消息。
  3. 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
  4. 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
  5. 消息队列是消息的链表,存放在内存中,由内核维护。只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。

消息队列常用操作函数如下:

#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
 
key_t ftok(const char *pathname, int proj_id);
int msgget(key_t key, int msgflg);
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

对于消息队列的操作,我们可以类比为这么一个过程:假如 A 有个东西要给 B,因为某些原因 A 不能当面直接给 B,这时候他们需要借助第三方托管(如银行),A 找到某个具体地址的建设银行,然后把东西放到某个保险柜里(如 1 号保险柜),对于 B 而言,要想成功取出 A 的东西,必须保证去同一地址的同一间银行取东西,而且只有 1 号保险柜的东西才是 A 给自己的。

对于上面的例子,涉及到几个比较重要的信息:地址、银行、保险柜号码。

只有同一个地址才能保证是同一个银行,只有是同一个银行双方才能借助它来托管,只有同一个保险柜号码才能保证是对方托管给自己的东西。

而在消息队列操作中,键(key)值相当于地址,消息队列标示符相当于具体的某个银行,消息类型相当于保险柜号码。

同一个键(key)值可以保证是同一个消息队列,同一个消息队列标示符才能保证不同的进程可以相互通信,同一个消息类型才能保证某个进程取出是对方的信息。

需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

键(key)值

System V 提供的进程间通信机制需要一个 key 值,通过 key 值就可在系统内获得一个唯一的消息队列标识符。key 值可以是人为指定的,也可以通过 ftok() 函数获得。

需要的头文件:

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

功能:

获取键(key)值

参数:

pathname: 路径名

proj_id: 项目ID,非 0 整数(只有低 8 位有效)

返回值:

成功:key 值

失败:-1

消息队列的创建

所需头文件:

#include <sys/msg.h>int msgget(key_t key, int msgflg);

功能:

创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的 key 值就能得到同一个消息队列的标识符。

参数:

key: ftok() 返回的 key 值

msgflg: 标识函数的行为及消息队列的权限,其取值如下:

IPC_CREAT:创建消息队列。

IPC_EXCL: 检测消息队列是否存在。

位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open() 函数的 mode_t 一样(open() 的使用请点此链接),但可执行权限未使用。

返回值:

成功:消息队列的标识符

失败:-1

示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
int main(int argc, char *argv[])
{
	key_t key;
	int  msgqid;
	
	key = ftok(".", 2012); // key 值
	
	// 创建消息队列
	msgqid = msgget(key, IPC_CREAT|0666);
	
	return 0;
}

运行结果如下:

消息队列的读写操作

对于消息队列的读写,都是以消息类型为准。消息类型相当于保险柜号码,A 往 1 号保险柜放东西,对方想取出 A 的东西必须也是从 1 号保险柜里取。同理,某一进程往消息队列添加 a 类型的消息,别的进程要想取出这进程添加的信息也必须取 a 类型的消息。

在学习消息队列读写操作前,我们先学习消息队列的消息格式:

typedef struct _msg
{
	long mtype;		 // 消息类型
	char mtext[100]; // 消息正文
	//…… ……          // 消息的正文可以有多个成员
}MSG;

消息类型必须是长整型的,而且必须是结构体类型的第一个成员,类型下面是消息正文,正文可以有多个成员(正文成员可以是任意数据类型的)。至于这个结构体类型叫什么名字,里面成员叫什么名字,自行定义,没有明文规定。

添加信息

所需头文件:

#include <sys/msg.h>int msgsnd( int msqid, const void *msgp, size_t msgsz, int msgflg);

功能:

将新消息添加到消息队列。

参数:

msqid: 消息队列的标识符。

msgp: 待发送消息结构体的地址。

msgsz: 消息正文的字节数。

msgflg:函数的控制属性,其取值如下:

0:msgsnd() 调用阻塞直到条件满足为止。

IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。

返回值:

成功:0

失败:-1

获取信息

所需头文件:

#include <sys/msg.h>ssize_t msgrcv( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg );

功能:

从标识符为 msqid 的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。

参数:

msqid:消息队列的标识符,代表要从哪个消息列中获取消息。

msgp: 存放消息结构体的地址。

msgsz:消息正文的字节数。

msgtyp:消息的类型。可以有以下几种类型:

msgtyp = 0:返回队列中的第一个消息。

msgtyp > 0:返回队列中消息类型为 msgtyp 的消息(常用)。

msgtyp < 0:返回队列中消息类型值小于或等于 msgtyp 绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。

注意:在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。

msgflg:函数的控制属性。其取值如下:

0:msgrcv() 调用阻塞直到接收消息成功为止。

MSG_NOERROR: 若返回的消息字节数比 nbytes 字节数多,则消息就会截短到 nbytes 字节,且不通知消息发送进程。

IPC_NOWAIT: 调用进程会立即返回。若没有收到消息则立即返回 -1。

返回值:

成功:读取消息的长度

失败:-1

消息队列的控制

所需头文件:

#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);

功能:

对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。

参数:

msqid:消息队列的标识符。

cmd:函数功能的控制。其取值如下:

IPC_RMID:删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构。

IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值存入到由 buf 指向的结构中。相对于,把消息队列的属性备份到 buf 里。

IPC_SET:将 msqid 相关的数据结构中的元素设置为由 buf 指向的结构中的对应值。相当于,消息队列原来的属性值清空,再由 buf 来替换。

buf:msqid_ds 数据类型的地址,用来存放或更改消息队列的属性。

返回值:

成功:0

失败:-1

实践示例

写端示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
typedef struct _msg
{
	long mtype;
	char mtext[50];
}MSG;
 
int main(int argc, char *argv[])
{
	key_t key;
	int  msgqid;
	MSG msg;
	
	key = ftok("./", 2015); // key 值
	
	// 创建消息队列
	msgqid = msgget(key, IPC_CREAT|0666);
	if(msgqid == -1)
	{
		perror("msgget");
		exit(-1);
	}
	
	msg.mtype = 10;	// 消息类型
	strcpy(msg.mtext, "hello mike"); // 正文内容
	
	/* 添加消息
	msg_id:消息队列标识符
	&msg:消息结构体地址
	sizeof(msg)-sizeof(long):消息正文大小
	0:习惯用0
	*/
	msgsnd(msgqid, &msg, sizeof(msg)-sizeof(long), 0);
	
	return 0;
}

读端示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
typedef struct _msg
{
	long mtype;
	char mtext[50];
}MSG;
 
int main(int argc, char *argv[])
{
	key_t key;
	int  msgqid;
	
	
	key = ftok("./", 2015); // key 值
	
	// 创建消息队列
	msgqid = msgget(key, IPC_CREAT|0666);
	if(msgqid == -1)
	{
		perror("msgget");
		exit(-1);
	}
	
	MSG msg;
	memset(&msg, 0, sizeof(msg));
	
	/* 取出类型为 10 的消息
	msg_id:消息队列标识符
	&msg:消息结构体地址
	sizeof(msg)-sizeof(long):消息正文大小
	(long)10:消息的类型
	0:习惯用0
	*/
	msgrcv(msgqid, &msg, sizeof(msg)-sizeof(long), (long)10, 0);
	printf("msg.mtext=%s\n", msg.mtext); 
	
	// 把消息队列删除
	// IPC_RMID:删除标志位
	msgctl(msgqid, IPC_RMID, NULL);
	
	return 0;
}

运行结果如下:

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: