【Linux 进程间通信(四)】System V 消息队列

news/2024/5/19 14:49:26

上一篇博客我们讲了共享内存,接下来我们讲一下System V IPC的下一部分:消息队列

含义

原理

系统接口

mssget

作用

参数

返回值 

msgsnd

作用

参数

返回值

msgrcv

作用

参数

返回值

msgctl

作用

参数

返回值

代码示例

server.cc(服务端)

client.cc(客户端)

comm.hpp(头文件)

效果演示

总结:


含义

消息队列允许不同进程之间通过在队列中发送和接收消息来进行通信。每个消息都有一个类型,接收进程可以选择接收特定类型的消息,也可以接收所有类型的消息。消息队列提供了一种异步的通信方式,发送进程可以将消息发送到队列中,而不需要等待接收进程准备好接收。

原理

  1. 消息队列创建:创建消息队列时,内核会分配一块内存区域来存储消息,并为该队列分配一个唯一的标识符。

  2. 发送消息:进程可以使用系统调用(如msgsnd)将消息发送到消息队列。发送消息时,进程需要指定目标队列的标识符、消息类型以及要发送的数据。

  3. 接收消息:进程可以使用系统调用(如msgrcv)从消息队列中接收消息。接收消息时,进程可以选择接收特定类型的消息,也可以接收队列中的下一条消息。

  4. 消息队列操作:除了发送和接收消息外,System V消息队列还提供了其他操作,如获取队列状态、删除队列等。

  5. 进程间同步:消息队列可以用于进程间的同步,比如一个进程等待另一个进程发送消息作为某种信号来进行同步操作。

  6. 消息类型:每个消息都有一个类型标识符,接收进程可以选择性地接收特定类型的消息,而不是仅仅接收队列中的下一条消息。

  7. 消息队列缓冲区:消息队列的内存由内核管理,因此可以确保高效的消息传递,并且能够处理大量消息。

  8. 持久性:与其他进程间通信机制相比,System V消息队列是持久的,即使发送消息的进程退出,消息队列仍然存在,直到显式删除为止。

系统接口

mssget

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

作用

msgget主要用来获取消息队列的标识符,如果消息队列不存在,可以使用msgflg参数中的IPC_CREAT来创建一个消息队列。

参数

  • key_t key:消息队列的键值,通常一个消息队列对应一个键值
  • int msgflg:用于指定创建消息队列时的权限标志。这个参数是一个位掩码,通常使用IPC_CREAT来创建一个新的队列,或者用IPC_EXCL | IPC_CREAT的结合确保指定key对应的消息队列不存在时再创建一个新的队列。

返回值 

如果获取成功,该函数将返回key对应的消息队列的标识符,否则返回-1并将错误信息设置。

msgsnd

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

作用

msgsnd负责向消息队列中发送消息

参数

  • int msqid:想要发送消息的消息队列标识符。
  • const void *msgp:msgp 参数是指向调用方定义的结构的指针,其常规形式如下:
struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */};

这个结构体需要用户自己进行创建。

mtype表示消息的类型,可以被设置为任何非负整数。将其设置成不同的值表示不同的消息类型或者优先级。

mtext是字符串,表示要发送消息的内容。

  • size_t msgsz:要发送数据的大小,通常用  sizeof(msgbuf)-sizeof(long) 计算。
  • int msgflg:可以定义msgsnd函数的行为,可以选择的标志位:0、IPC_NOWAIT、IPC_NOERROR、IPC_EXCEPT、MSG_NOERROR等。

返回值

如果发送成功,返回值为0,否则返回值为-1,并且设置错误信息。

msgrcv

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

作用

msgrcv负责向消息队列中读取信息。

参数

  • int msqid:想要发送消息的消息队列标识符。
  • const void *msgp:指向调用方定义的结构的指针
  • size_t msgsz:想要读取的消息的大小
  • int msgflg:可以定义msgrcv函数的行为,可以选择的标志位:0、IPC_NOWAIT、IPC_NOERROR、IPC_EXCEPT、MSG_NOERROR等。

返回值

如果读取成功,返回值为从mtext中读取到的字节数,否则返回-1,并设置错误信息。

msgctl

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

作用

msgctl函数用于控制控制消息队列,例如获取消息队列的状态、设置消息队列的属性以及删除消息队列。

参数

  • int msqid:想要发送消息的消息队列标识符。
  • int cmd:用于指定要执行的操作,可以选择:IPC_STAT、IPC_SET、IPC_RMID,分别是获取状态、设置属性、删除消息队列。
  • struct msqid_ds *buf: msqid_ds 是系统定义的结构体,可以直接创建,传入这个结构体用来获取msqid对应消息队列的状态或者设置属性,当cmd被设置为IPC_RMID时,这个参数可以忽略或者设置为0.

返回值

msgctl函数的返回值取决于执行的操作(cmd被设置成什么)以及是否出现错误:

  • 如果执行成功,都是返回0
  • 执行失败返回-1,并设置错误信息,可以通过errno进行查看。

代码示例

server.cc(服务端)

#include "comm.hpp"int main()
{// 获取消息的结构体struct mymsg recept_message;// 打印消息队列信息的结构体struct msqid_ds ds;// 获取唯一键值key_t key = GetKey(pathname, proj_id);// 创建队列并获取队列IDint msgid = msgget(key, IPC_CREAT | 0666);if (msgid == -1){std::cerr << "errno:" << errno << ",errstring:" << strerror(errno) << std::endl;return 1;}std::cout << msgid << std::endl;// 打印队列属性if (msgctl(msgid, IPC_STAT, &ds) == -1){std::cerr << "errno:" << errno << ",errstring:" << strerror(errno) << std::endl;return 1;}std::cout << "消息队列属性如下:";std::cout << "所有者的UID: " << ds.msg_perm.uid << std::endl;std::cout << "所有者的GID:" << ds.msg_perm.gid << std::endl;std::cout << "读写权限: " << ds.msg_perm.mode << std::endl;std::cout << "当前消息队列中的消息个数 " << ds.msg_qnum << std::endl;std::cout << "消息队列的最大尺寸: " << ds.msg_qbytes << std::endl;// 服务端读取消息if (msgrcv(msgid, &recept_message, sizeof(recept_message) - sizeof(long), 1, 0) == -1){std::cerr << "errno:" << errno << ",errstring:" << strerror(errno) << std::endl;return 1;}std::cout << "服务端收到消息:" << recept_message.s << std::endl;std::cout << "准备关闭消息队列" << std::endl;sleep(5);// 关闭队列msgctl(msgid, IPC_RMID, 0);std::cout << "已关闭消息队列" << std::endl;return 0;
}

client.cc(客户端)

#include "comm.hpp"int main()
{struct mymsg send_message;// 获取消息队列标识符key_t key = GetKey(pathname, proj_id);// DEBUGstd::cout << "获取消息队列标识符" << std::endl;int msgid = msgget(key, IPC_NOWAIT);std::cout << "msgid:" << msgid << std::endl;// 向消息队列中发送消息 strcpy(send_message.s, "客户端发送的消息");if (msgsnd(msgid, &send_message, sizeof(send_message) - sizeof(long), 0) == -1){std::cerr << "errno:" << errno << ",errstring:" << strerror(errno) << std::endl;return 1;}return 0;
}

comm.hpp(头文件)

#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>#define SIZE 1024
const char *pathname = "/home/gpc/pipeline/message_queue";
const int proj_id = 0x11223344;struct mymsg
{long mtype = 1;char s[SIZE];
};key_t GetKey(const char *pathname,const int proj_id)
{key_t key = ftok(pathname,proj_id);if(key < 0){std::cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<std::endl;return 1;}return key;
}

效果演示

消息队列演示效果

总结:

  • Ⅰ、 System V 消息队列是通过标识符引用的,而不是文件描述符。这意味着各种基于文件描述符的I/O技术将无法应用于消息队列上。
  • Ⅱ、 使用键key而不是文件名来标识消息队列会增加额外的程序设计复杂性,同时还需要使用ipcs和ipcrm来替换ls和rm。ftok()函数通常能产生一个唯一的键,但却无法百分百保证。使用IPC_PRIVATE能确保产生唯一的队列标识符,但需要使这个标识符对需要用到它的其他进程可见。
  • Ⅲ、 消息队列是无法连接的,内核不会像对待管道、FIFO以及socket那样维护引用队列的进程数。因此,就需要注意以下问题:

一个应用程序何时能够安全地删除一个消息队列?(不管是否有进程在后面某个时刻需要从队列中读取数据而过早地删除队列会导致数据丢失。<相对于其他采用文件描述符的I/O在接到删除指令会等待最后一个使用该文件的进程使用完毕后才会删除该文件来说的>)
应用程序如何确保不再使用的队列会被删除?

  • Ⅳ、 消息队列的总数、消息的大小以及单个队列的容量都是有限制的。(可以在/proc/sys/kernel中对相应文件进行修改配置,但是操作麻烦,不方便移植)

http://www.mrgr.cn/p/43070621

相关文章

数据结构-二叉树-二叉搜索树

一、概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者具有以下性质的二叉树&#xff1a; 若它的左子树不为空&#xff0c;则左树上所有节点的值都小于根节点的值。 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值。 它…

OpenStack-容器手册(全)

OpenStack 容器手册(全)原文:zh.annas-archive.org/md5/D8A2C6F8428362E7663D33F30363BDEB 译者:飞龙 协议:CC BY-NC-SA 4.0前言 容器是近年来最受关注的技术之一。随着它们改变了我们开发、部署和运行软件应用程序的方式,它们变得越来越受欢迎。OpenStack 因被全球许多组…

MySQL夺命16问,你能坚持到第几问(转)

原文:https://zhuanlan.zhihu.com/p/534415409 1、数据库三大范式是什么?** 第一范式:每个列都不可以再拆分。 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主…

MySQL夺命16问,你能坚持到第几问?

原文:https://zhuanlan.zhihu.com/p/534415409 1、数据库三大范式是什么?** 第一范式:每个列都不可以再拆分。 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主…

Docker-DevOps-入门手册(全)

Docker DevOps 入门手册(全)原文:zh.annas-archive.org/md5/A074DB026A63DFD63D361454222593A5 译者:飞龙 协议:CC BY-NC-SA 4.0前言 Docker 与 DevOps 概述了容器化的强大力量以及这种创新对开发团队和一般运营的影响。我们还将了解 DevOps 的真正含义,涉及的原则,以及…

【论文阅读】Learning Texture Transformer Network for Image Super-Resolution

Learning Texture Transformer Network for Image Super-Resolution 论文地址Abstract1. 简介2.相关工作2.1单图像超分辨率2.2 Reference-based Image Super-Resolution 3. 方法3.1. Texture TransformerLearnable Texture Extractor 可学习的纹理提取器。Relevance Embedding.…

ScienceDirect文献如何下载

ScienceDirect是爱思唯尔公司的全文数据库平台&#xff0c;是全球最大的科学、技术与医学全文电子资源数据库&#xff0c;是我们在查找外文文献常用的数据库。但是&#xff0c;ScienceDirect数据库的文献是需要使用权限才可获取的。如果你没有该数据库资源要如何查询下载文献呢…

07. C语言程序执行流程控制

【有条件执行语句】 if esle 语句 if else 语句根据一个条件确定是否执行一段代码,执行条件是一个布尔值,布尔值为true则执行,为false则不执行,同时可以设置不符合条件时执行的语句。 if(执行条件) {符合条件时执行的代码; } else {不符合条件时执行的代码; } 使用事项:1.…

如何利用仪表构造InfiniBand流量在数据中心测试中的应用

一、什么是Infiniband&#xff1f; 在当今数据爆炸的时代&#xff0c;数据中心作为信息处理的中心枢纽&#xff0c;面临着前所未有的挑战。传统的通信方式已经难以满足日益增长的数据传输需求&#xff0c;而InfiniBand技术的出现&#xff0c;为数据中心带来了全新的通信解决方…

用蒙特卡罗方法求积分

实验任务 采用 Monte-Carlo 法计算函数 y=x2 在 0~10 之间的积分值 实验目的 熟悉 MPI_Reduce() 函数的用法 实验方法 该算法的思想是通过随机数把函数划分成小的矩形块,通过求矩形块的面积和来求积分值,我们生成 n 个 0~10 之间的随机数,求出该随机数所对应的函数值作为矩…

Apache Seata基于改良版雪花算法的分布式UUID生成器分析1

title: Seata基于改良版雪花算法的分布式UUID生成器分析 author: selfishlover keywords: [Seata, snowflake, UUID] date: 2021/05/08 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Seata基于改良版雪花算法的分布式UUID生成器分析…

Hexview工具使用说明

一般Davinci工具都会在Misc路径下面配一个hexview工具。Hexview工具是免安装的&#xff0c;功能非常强大&#xff0c;可以打开并解析hex文件和srec文件&#xff0c;哪怕这两种文件格式不一样&#xff0c;解析出来的结果是一样的。 文件描述 _examples是例子 _expdatproc是用…

一次通过dump文件分析OutOfMemoryError异常代码定位过程

OutOfMemoryError是Java程序中常见的异常,通常出现在内存不足时,导致程序无法运行。借助MAT内存分析工具分析可能的内存泄漏代码问题定位。OutOfMemoryError是Java程序中常见的异常,通常出现在内存不足时,导致程序无法运行。 当出现OutOfMemoryError异常时,可能的现象是这…

前端埋点数据采集(二)mock应用系统10万条前端埋点数据

前端埋点数据采集(二)mock应用系统10万条前端埋点数据 上一期我们分享了前端埋点数据采集(一)采集系统架构设计 我们说应用系统的数据,采集到大数据平台来,然后再到数仓。但是很多实际场景是应用系统、大数据平台、数仓平台各自并没有完成系统的搭建和开发。假设现在一个…

ES的脑裂现象

目录 0 集群结点的职责1 什么是脑裂现象2 造成脑裂现象的原因2.1 网络问题&#xff08;最常见&#xff09;2.2 主节点负载过大&#xff0c;资源耗尽&#xff0c;别的结点ping不到主节点2.3 主节点JVM内存回收时间过长导致 3 脑裂现象的解决方案3.1 局域网部署3.2 角色分离&…

Android(一)

坏境 java版本 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 进入安卓官网下载 勾选协议 next 如果本地有设置文件&#xff0c;选择Config or installation folder 如果本地没有设置文件&#xff0c;选择Do not import settings 同意两个协议 耐…

allure功能使用-测试时添加文件attach

allure.attach("get env", "附加文本", attachment_type=allure.attachment_type.TEXT) allure.attach("<body>测试body</body>", "html代码", attachment_type=allure.attachment_type.HTML) allure.attach.file(".…

windows下安装Jenkins以及配置分布式agent节点

安装Jenkins: 1.Jenkins稳定版本的war包路径:https://get.jenkins.io/war-stable/ 2.jdk下载:https://www.oracle.com/java/technologies/downloads/ 3.启动Jenkins:命令行运行java -jar jenkins.war 至此可以通过浏览器127.0.0.1:8080,连接上本地Jenkins配置分布式agent节…

locust压测

目录locust1.依赖2. 实例2.1 压测方式2.2 locust服务端2.3 待压测接口服务3. 参考文档 locust 1.依赖 pip install locust2. 实例 2.1 压测方式 1. 压测方式 1.1 前台自编辑方式修改文件名为locustfile.py 并在控制台使用locust启动前台服务 用户自定义压测参数并开启压测1.2 …