0


【Linux】消息传递的艺术:探索Linux消息队列机制

本文所讲的共享内存为

System V

版的消息队列

🏠 大家好,我是Yui_,一位努力学习C++/Linux的博主💬
🍑 如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🚀 如有不懂,可以随时向我提问,我会全力讲解

🔥 如果感觉博主的文章还不错的话,希望大家关注、点赞、收藏三连支持一下博主哦~!
🔥 你们的支持是我创作的动力!
🧸 我相信现在的努力的艰辛,都是为以后的美好最好的见证!
🧸 人的心态决定姿态!
💬 欢迎讨论:如有疑问或见解,欢迎在评论区留言互动。
👍 点赞、收藏与分享:如觉得这篇文章对您有帮助,请点赞、收藏并分享!
🚀 分享给更多人:欢迎分享给更多对 Linux 感兴趣的朋友,一起学习!

文章目录

0.前言

共享内存没有进行同步于互斥以及异步
System V 是一种经典的 UNIX 进程间通信(IPC)机制,提供了一套 API 来支持进程之间的高效数据交换和同步。消息队列信号量 是其中的两个关键部分,它们各自解决了不同的通信和同步问题,但都基于 System V 的 IPC 框架。
虽然 System V IPC 功能强大,但其接口较为复杂,现代系统中逐渐被 POSIX IPC 替代。

1.什么是消息队列

消息队列(Message Queue)是进程间通信(IPC)的一种方式,通过将消息存入内核维护的队列中,实现异步的进程数据传递。与管道不同,消息队列不仅允许不同大小的数据块传递,还支持消息的优先级排序,从而提供了更灵活的通信机制。

1.1 消息队列的特点

  1. 异步通信:发送方和接受方不需要同时进行,消息会存储在队列中,直到接收方读取。
  2. 持久性:消息队列由内核维护,即使发送方或者接收方意外退出,消息仍然保留在队列中。
  3. 消息分类:每个消息都有一个消息类型,可以根据类型有选择地读取特定地消息。
  4. 容量限制:消息队列地大小是有限制地,需要合理地管理和清空,避免队列满导致堵塞。

1.2 消息队列地核心概念

  1. 标识符:消息队列使用一个唯一地标识符(Queue ID)来区分。
  2. 消息类型:每条消息包含一个正整数的类型,用户可以根据类型选择性地读取消息。
  3. 结构:
  • 消息队列:实际传输地数据内容。
  • 消息长度:消息地字节数。
  • 消息类型地分类标识。消息队列

遍历消息时,存数据块还是取数据块,取决于数据块中的类型

type

1.3 消息队列的数据结构

我们使用

man

手册来查看,输入

man msgctl

消息队列的数据结构

structmsqid_ds{structipc_perm msg_perm;/* Ownership and permissions */time_t          msg_stime;/* Time of last msgsnd(2) */time_t          msg_rtime;/* Time of last msgrcv(2) */time_t          msg_ctime;/* Time of creation or last
                                  modification by msgctl() */unsignedlong   msg_cbytes;/* # of bytes in queue */msgqnum_t       msg_qnum;/* # number of messages in queue */msglen_t        msg_qbytes;/* Maximum # of bytes in queue */pid_t           msg_lspid;/* PID of last msgsnd(2) */pid_t           msg_lrpid;/* PID of last msgrcv(2) */};

我们要注意类型为

struct ipc_prem

这个类型,该类型在共享内存的数据结构也出现过。
下面是它的具体内容:

structipc_perm{key_t          __key;/* Key supplied to msgget(2) */uid_t          uid;/* Effective UID of owner */gid_t          gid;/* Effective GID of owner */uid_t          cuid;/* Effective UID of creator */gid_t          cgid;/* Effective GID of creator */unsignedshort mode;/* Permissions */unsignedshort __seq;/* Sequence number */};

1.4 消息队列的使用

如果你已经学习过共享内存,那么消息队列的使用也一定会得心应手的。

1.4.1 创建消息队列

使用

msgget

函数来创建消息队列。

msgget

是用于创建或访问 System V 消息队列 的系统调用。它根据指定的键值创建或获取一个消息队列标识符,用于后续的消息操作。

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

使用方法和

shmget

高度相似
参数说明:

  1. key
  • 一个唯一标识消息队列的值。
  • 常通过ftok函数生成,也可以直接使用整数值(不推荐)
  1. msgflg
  • 用于指定消息队列和权限和行为。 - 访问权限:权限掩码- 行为控制: - IPC_CREAT:如果队列不存在那么创建- IPC_RXCL:需要与IPC_CREAT结合使用,单独使用没有意义。表示如果队列已经存在,那么返回错误。 返回值
  • 成功:返回消息队列的标识符msgid
  • 失败:返回-1。 是不是和shmget相似,简直一模一样啊! 如果你没有看过我的共享内存文章,推荐一看,会对你理解消息队列也是有帮助的哦~ 【Linux】「共享内存揭秘」:高效进程通信的终极指南-CSDN博客 还是老样子,我们先创建一个共享区域:common.hpp
#include<iostream>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<assert.h>#definePATHNAME"./"#definePROJ_ID0x3fconst mode_t mode =0666;

key_t getKey(){/**
     * :return 返回key值
     * @return
     */
    key_t ret =ftok(PATHNAME,PROJ_ID);assert(ret!=-1);return ret;}intcreatMsg(){/**:function 创建消息队列
     * :return 返回msgid
     * @return
     */int msgid =msgget(getKey(),IPC_CREAT|IPC_EXCL|mode);assert(msgid!=-1);return msgid;}

然后创建一个消息队列

#include"common.hpp"intmain(){//创建消息队列int msgid =creatMsg();return0;}

运行后,我们在命令行输入指令

ipcs -q

指令查看

由于现在我们并没有使用消息队列,其

used-bytes

和消息数

messages

都是0
消息队列的生命周期也是跟随操作系统的,并不会因为进程的结束而结束

1.4.2 释放消息队列

同共享内存一样,你可以通过指令或者程序中的函数来将消息队列释放。
释放指令:

ipcrm -q msqid

指令释放

函数释放:

msgctl

例行惯例,我们先在介绍

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

参数说明

  1. msqid- 消息队列的标识符(由 msgget 返回)。
  2. cmd- 指定要执行的操作: - **IPC_STAT**:获取消息队列的当前状态,并将其存储到 buf 中。- **IPC_SET**:设置消息队列的属性(使用 buf 中的数据)。- **IPC_RMID**:删除消息队列。
  3. buf- 指向 struct msqid_ds 的指针,表示消息队列的状态和属性。- 对于 IPC_STATIPC_SET,需要使用此参数;对 IPC_RMID 则可以为 NULL返回值 成功返回0,失败返回-1 只是释放消息队列,函数的使用就是套路了。
msgctl(msqid,IPC_RMID,nullptr);

1.4.2 使用消息队列

发送消息
为了将数据发送到消息队列,现在需要的函数为

msgsnd
msgsnd

是用于向 System V 消息队列 发送消息的系统调用。它将一条消息放入消息队列中,以实现进程间通信。

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

参数说明:

  1. msqid- 消息队列的标识符,由 msgget 返回。
  2. msgp- 指向消息数据的指针。这个指针指向的结构必须包含一个 long 类型的成员(用于存储消息类型),后面跟随实际的数据。
  3. msgsz- 消息正文部分的大小(以字节为单位,不包括消息类型字段)。
  4. msgflg- 标志选项,用于控制消息发送行为: - **IPC_NOWAIT**:如果消息队列已满,则不阻塞,立即返回错误。- 默认情况下,如果队列满,调用会阻塞直到有空间可用。 返回值 成功:0。失败:-1关于参数2 表示的是待发送的数据块,是一个结构体剋们需要自己定义
structmsgbuf{long mtype;/* message type, must be > 0 */char mtext[1];/* message data */};

接受
为了接受消息,同样我们还需要一个函数

msgrcv
msgrcv

是一个用于从 System V 消息队列 中接收消息的系统调用。通过它,进程可以从指定的消息队列中读取一条符合条件的消息。

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

ssize_t msgrcv(int msqid,void*msgp, size_t msgsz,long msgtyp,int msgflg);

参数说明:

  • msqid- 消息队列的标识符,由 msgget 返回。用于标识从哪个队列中接收消息。
  • msgp- 指向一个用户定义的消息缓冲区。该缓冲区的第一个字段必须是 long 类型的消息类型 mtype,后续是消息正文。 示例消息缓冲区定义:structmsgbuf{long mtype;// 消息类型 char mtext[MSGSZ];// 消息正文 };
  • msgsz- 指定消息正文 mtext 的大小(以字节为单位)。如果接收到的消息大小超过该值,行为将根据 msgflg 参数决定。
  • msgtyp- 指定要接收的消息类型: - **>0**:接收队列中类型等于 msgtyp 的第一条消息。- **0**:接收队列中的第一条消息,不论类型。- **<0**:接收队列中类型绝对值小于等于 |msgtyp| 的第一条消息。
  • msgflg- 控制消息接收行为的标志,可选值包括: - **IPC_NOWAIT**:如果没有符合条件的消息,函数立即返回 -1,而不是阻塞。- **MSG_NOERROR**:如果消息正文大小超过 msgsz,多余部分将被截断,而不是导致错误。common.hpp
#include<iostream>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<cstring>#include<unistd.h>#include<assert.h>#definePATHNAME"./"#definePROJ_ID0x3f#defineMSGSZ128const mode_t mode =0666;

key_t getKey(){/**
     * :return 返回key值
     * @return
     */
    key_t ret =ftok(PATHNAME,PROJ_ID);assert(ret!=-1);return ret;}intcreatMsg(){/**:function 创建消息队列
     * :return 返回msgid
     * @return
     */int msgid =msgget(getKey(),IPC_CREAT|IPC_EXCL|mode);assert(msgid!=-1);return msgid;}intgetMsg(){/**:function 获取消息队列
     * :return 返回msgid
     * @return
     */int msgid =msgget(getKey(),IPC_CREAT);assert(msgid!=-1);return msgid;}
client.cc

用于向

server.cc

发送消息

/**
 * 用户端发送消息
 */#include"common.hpp"structmy_msgbuf{long mtype;char mtext[MSGSZ];//MSGSZ为新定义的标识常量:128};intmain(){int msgid =creatMsg();structmy_msgbuf msg;
    msg.mtype =1;//设置消息类型strcpy(msg.mtext,"hello,Message Queue!");//开始发送消息int n =msgsnd(msgid,&msg,strlen(msg.mtext)+1,0);assert(n!=-1);
    std::cout<<"消息已发送"<<std::endl;sleep(10);msgctl(msgid,IPC_RMID,nullptr);//释放return0;}
server.cc

用于接收

client.cc

消息

/**
 * 服务端接收消息
 */#include"common.hpp"structmy_msgbuf{long mtype;char mtext[MSGSZ];//MSGSZ为新定义的标识常量:128};intmain(){int msgid =getMsg();structmy_msgbuf msg;//开始接受信息
    ssize_t n =msgrcv(msgid,&msg,MSGSZ,1,0);assert(n!=-1);
    std::cout<<"已接收消息:"<<msg.mtext<<std::endl;return0;}

执行效果:
执行结果
感谢你的阅读,如果你有求职的需求可以看看这里

2. 总结

消息队列的大部分接口都与共享内存类似,如果你使用过更现代的

POSIX IPC

,可能会觉得

System V

已经落后,事实上也确实是如此,

System V

已经过于老旧,现在用的很少,对于此版本,不需要太过深入了解,在实际情况中遇到,查查文档就是了。
往期Linux文章:Linux专栏
在这里插入图片描述

标签: linux java 数据库

本文转载自: https://blog.csdn.net/2303_79015671/article/details/144089247
版权归原作者 Yui_ 所有, 如有侵权,请联系我们删除。

“【Linux】消息传递的艺术:探索Linux消息队列机制”的评论:

还没有评论