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

上一篇博客我们讲了共享内存,接下来我们讲一下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);

    // 创建队列并获取队列ID
    int 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);
    // DEBUG
    std::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.mfbz.cn/a/595153.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript百炼成仙自学笔记——12

函数七重关之五&#xff08;自执行函数&#xff09; 什么时候用它&#xff1f; 很多时候&#xff0c;我们只想执行一个函数&#xff0c;却无所谓这个函数叫什么名字。那么这种情况下就可以考虑使用自执行函数。 {function(){console.log(123);} }(); 这就是一个简单的自执行的…

视频剪辑:视频文件元数据修改工具,批量操作提升效率和准确性

在视频剪辑和后期处理的过程中&#xff0c;除了对视频本身的编辑和修改&#xff0c;元数据的管理和修改同样重要。元数据&#xff0c;如标题、艺术家、专辑封面等&#xff0c;不仅提供了视频文件的基本信息&#xff0c;还有助于更好地组织、搜索和共享视频内容。而针对视频文件…

dumpsys meminfo 流程中细节

源码基于&#xff1a;Android U 参考&#xff1a; dumpsys meminfo 详解(R) dumpsys meminfo 详解(U) 1. 命令入口 MemBinder frameworks/base/services/core/java/com/android/server/am/AMS.javastatic class MemBinder extends Binder {ActivityManagerService mActivity…

原型模式和建造者模式

1、原型模式 1.1 概念 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 1.2 结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了具体原型对象必须实现的的 clone() 方法。 具体原型类&#xff1a;实现抽…

链表经典面试题01

目录 引言 面试题01:返回倒数第k个节点 题目描述: 思路分析: 代码展示: 面试题02:链表的回文结构 题目描述: 描述 思路分析: 代码展示: 面试题03:相交链表 题目描述: 思路分析: 代码展示: 小结: 引言 这次的题均来自力扣和牛客有关链表的经典面试题,代码只会展示…

二.Django--创建多个APP路由映射

目录 1-创建项目 2-创建多个APP 3-注册APP 4-创建"前端页面"并做路由映射 各个APP里面的views.py写视图函数等等 1-创建项目 django-admin startproject 项目名 django-admin startproject my_project 2-创建多个APP python manage.py startapp app名 pyth…

HttpCilent进行Post请求form-data接口,服务方接收不到参数

结论先行 生成分隔标识boundary在HttpPost中设置Header时带上boundary创建MultipartEntity时需要设置boundary 实现代码如下 /*** param url 调用接口的地址* param paramMap 调用接口传入的方法体参数*/ public static String postDataByFormData(String url, Map<Strin…

【参赛总结】第二届云原生编程挑战赛-冷热读写场景的RocketMQ存储系统设计 - Nico

关联比赛: 2021第二届云原生编程挑战赛1&#xff1a;针对冷热读写场景的RocketMQ存储系统设计 引子 在一个浑浑噩噩的下午&#xff0c;百无聊赖的我像往常一样点开了划水交流群&#xff0c;细细品味着老哥们关于量子力学的讨论。嬉戏间&#xff0c;平常水不拉几的群友张三忽…

【毕业设计】基于SSM的运动用品商城的设计与实现

1.项目介绍 在这个日益数字化和信息化的时代&#xff0c;随着人们购物习惯的转变&#xff0c;传统的实体商店已经无法满足人们日益增长的在线购物需求。因此&#xff0c;基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的运动用品商城项目应运而生&#xff0…

OpenGL 入门(二)—— 渲染摄像头采集的预览画面

本篇主要内容&#xff1a; 将摄像头采集到的图像通过 OpenGL 绘制到屏幕上FBO 离屏渲染 在开始上述流程前&#xff0c;我们有必要对 SurfaceTexture 做一个简单了解&#xff0c;因为 OpenGL 需要通过它获取要绘制的图像。 1、认识 SurfaceTexture SurfaceTexture 是 Androi…

【XR806开发板试用】XR806与鸿蒙,创建任务,串口转发TCPServer收到的数据

很荣幸获得评测开发板的机会&#xff0c;XR806的程序资料做的还是挺不错的。 目标&#xff1a; 1、学习用鸿蒙创建2个任务&#xff1b; 2、创建TCP Server收发数据。 任务ledThread&#xff1a;LED每秒亮灭一次&#xff0c;代表程序在运行。 任务MainThread&#xff1a;创建TCP…

Leetcode—377. 组合总和 Ⅳ【中等】

2024每日刷题&#xff08;124&#xff09; Leetcode—377. 组合总和 Ⅳ 算法思想 实现代码 class Solution { public:int combinationSum4(vector<int>& nums, int target) {vector<unsigned long long>dp(target 1);dp[0] 1;for(int i 1; i < target;…

echarts柱状图实现左右横向对比

实现效果如上图 其实是两组数据&#xff0c;其中一组数据改为负数&#xff0c;然后 在展示的时候&#xff0c;在将负数取反 第一处修改坐标轴 xAxis: [{type: value,axisLabel: {formatter: function (value) {if (value < 0) {return -value;}else{return value;}}}}], 第…

如何修改图片大小?调整图片大小的几个方法介绍

当我们在不同的应用场景中使用图片的时候&#xff0c;常常会需要去调整图片尺寸来适应不同的要求&#xff0c;还有图片体积大小也会有要求&#xff0c;这时候就需要用到我们今天分享的这款图片在线处理工具了&#xff0c;不管是图片改大小或者图片压缩它都能快速解决&#xff0…

LVGL移植到STM32F4

1、LVGL简介 LittlevGL是一个免费的开源图形库&#xff0c;提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用。 1.1、LVGL特点 强大的构建模组&#xff1a;按钮、图表、列表、滑块、图像等先进的图形&#xff1a;动画、反锯齿…

【热门话题】ElementUI 快速入门指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 ElementUI 快速入门指南环境准备安装 ElementUI创建 Vue 项目安装 ElementUI 基…

自然语言(NLP)

It’s time for us to learn how to analyse natural language documents, using Natural Language Processing (NLP). We’ll be focusing on the Hugging Face ecosystem, especially the Transformers library, and the vast collection of pretrained NLP models. Our proj…

STM32单片机实战开发笔记-独立看门狗IWDG

嵌入式单片机开发实战例程合集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11av8rV45dtHO0EHf8e_Q0Q?pwd28ab 提取码&#xff1a;28ab IWDG模块测试 1、功能描述 STM32F10X内置两个看门狗&#xff0c;提供了更高的安全性&#xff0c;时间的精确下性和使用的灵活性…

微信答题链接怎么做_新手也能快速上手制作

在数字营销日新月异的今天&#xff0c;如何有效吸引用户参与、提升品牌曝光度&#xff0c;成为了每一个营销人都在思考的问题。而微信答题链接&#xff0c;作为一种新兴的互动营销方式&#xff0c;正以其独特的魅力&#xff0c;在营销界掀起一股新的热潮。今天&#xff0c;就让…

第三节课,前端

一、参考链接&#xff1b; 总 知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具 分 2022-03-18 星球直播笔记-用户中心&#xff08;下&#xff09; 语雀 二、登录 2.1登录网址 2.2前端页面修改 2.1 页面修改 2.2 页脚的超链接 网址&am…
最新文章