0

0

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

看不見的法師

看不見的法師

发布时间:2025-06-19 15:36:33

|

315人浏览过

|

来源于php中文网

原创

本文讨论的是system v版本的消息队列。

  1. 前言

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

  1. 什么是消息队列

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

1.1 消息队列的特点

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

1.2 消息队列的核心概念

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

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

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

1.3 消息队列的数据结构

我们使用man手册来查看,输入man msgctl

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

消息队列的数据结构:

struct msqid_ds {
   struct ipc_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() */
   unsigned long   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_perm这个类型,该类型在共享内存的数据结构中也出现过。下面是它的具体内容:

struct ipc_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 */
   unsigned short mode;        /* Permissions */
   unsigned short __seq;       /* Sequence number */
};

1.4 消息队列的使用

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

1.4.1 创建消息队列

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

#include 
#include 
#include 
int msgget(key_t key, int msgflg);

使用方法和shmget高度相似。

参数说明:

  • key:一个唯一标识消息队列的值。常通过ftok函数生成,也可以直接使用整数值(不推荐)。
  • msgflg:用于指定消息队列的权限和行为。
    • 访问权限:权限掩码。
    • 行为控制:IPC_CREAT:如果队列不存在那么创建;IPC_EXCL:需要与IPC_CREAT结合使用,单独使用没有意义。表示如果队列已经存在,那么返回错误。

返回值:

  • 成功:返回消息队列的标识符msgid
  • 失败:返回-1。

是不是和shmget相似,简直一模一样啊!如果你没有看过我的共享内存文章,推荐一看,会对你理解消息队列也是有帮助的哦~ 【Linux】「共享内存揭秘」:高效进程通信的终极指南-CSDN博客。

还是老样子,我们先创建一个共享区域:common.hpp

#include 
#include 
#include 
#include 
#include 
#define PATHNAME "./"
#define PROJ_ID 0x3f
const mode_t mode = 0666;
key_t getKey(){
    /**
     * :return 返回key值
     * @return
     */
    key_t ret = ftok(PATHNAME,PROJ_ID);
    assert(ret!=-1);
    return ret;
}
int creatMsg(){
    /**:function 创建消息队列
     * :return 返回msgid
     * @return
     */
    int msgid = msgget(getKey(),IPC_CREAT|IPC_EXCL|mode);
    assert(msgid!=-1);
    return msgid;
}

然后创建一个消息队列。

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

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

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

由于现在我们并没有使用消息队列,其used-bytes和消息数messages都是0。消息队列的生命周期也是跟随操作系统的,并不会因为进程的结束而结束。

BgSub
BgSub

免费的AI图片背景去除工具

下载

1.4.2 释放消息队列

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

释放指令:ipcrm -q msqid

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

函数释放:msgctl。例行惯例,我们先在介绍msgctl

#include 
#include 
#include 
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数说明:

  • msqid:消息队列的标识符(由msgget返回)。
  • cmd:指定要执行的操作:
    • IPC_STAT:获取消息队列的当前状态,并将其存储到buf中。
    • IPC_SET:设置消息队列的属性(使用buf中的数据)。
    • IPC_RMID:删除消息队列。
  • buf:指向struct msqid_ds的指针,表示消息队列的状态和属性。对于IPC_STATIPC_SET,需要使用此参数;对IPC_RMID则可以为NULL

返回值:

  • 成功返回0,失败返回-1。

只是释放消息队列,函数的使用就是套路了。

msgctl(msqid,IPC_RMID,nullptr);

1.4.3 使用消息队列发送消息

为了将数据发送到消息队列,现在需要的函数为msgsndmsgsnd是用于向System V消息队列发送消息的系统调用。它将一条消息放入消息队列中,以实现进程间通信。

#include 
#include 
#include 
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数说明:

  • msqid:消息队列的标识符,由msgget返回。
  • msgp:指向消息数据的指针。这个指针指向的结构必须包含一个long类型的成员(用于存储消息类型),后面跟随实际的数据。
  • msgsz:消息正文部分的大小(以字节为单位,不包括消息类型字段)。
  • msgflg:标志选项,用于控制消息发送行为:
    • IPC_NOWAIT:如果消息队列已满,则不阻塞,立即返回错误。默认情况下,如果队列满,调用会阻塞直到有空间可用。

返回值:

  • 成功:0。
  • 失败:-1。

关于参数2,表示的是待发送的数据块,是一个结构体,我们需要自己定义。

struct msgbuf {
   long mtype;       /* message type, must be > 0 */
   char mtext[1];    /* message data */
};

1.4.4 使用消息队列接收消息

为了接收消息,同样我们还需要一个函数msgrcvmsgrcv是一个用于从System V消息队列中接收消息的系统调用。通过它,进程可以从指定的消息队列中读取一条符合条件的消息。

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

参数说明:

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

common.hpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PATHNAME "./"
#define PROJ_ID 0x3f
#define MSGSZ 128
const mode_t mode = 0666;
key_t getKey(){
    /**
     * :return 返回key值
     * @return
     */
    key_t ret = ftok(PATHNAME,PROJ_ID);
    assert(ret!=-1);
    return ret;
}
int creatMsg(){
    /**:function 创建消息队列
     * :return 返回msgid
     * @return
     */
    int msgid = msgget(getKey(),IPC_CREAT|IPC_EXCL|mode);
    assert(msgid!=-1);
    return msgid;
}
int getMsg(){
    /**:function 获取消息队列
     * :return 返回msgid
     * @return
     */
    int msgid = msgget(getKey(),IPC_CREAT);
    assert(msgid!=-1);
    return msgid;
}

client.cc用于向server.cc发送消息。

/**
 * 用户端发送消息
 */
#include "common.hpp"
struct my_msgbuf{
    long mtype;
    char mtext[MSGSZ];//MSGSZ为新定义的标识常量:128
};
int main(){
    int msgid = creatMsg();
    struct my_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 << "消息已发送:" << msg.mtext << std::endl;
    return 0;
}

server.cc用于接收client.cc消息。

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

执行效果:

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

  1. 总结

消息队列的大部分接口都与共享内存类似,如果你使用过更现代的POSIX IPC,可能会觉得System V已经落后,事实上也确实是如此,System V已经过于老旧,现在用的很少,对于此版本,不需要太过深入了解,在实际情况中遇到,查查文档就是了。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

235

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

437

2024.03.01

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

183

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

286

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

256

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

123

2025.08.07

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

220

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

190

2025.07.04

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

70

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

Swoft2.x速学之http api篇课程
Swoft2.x速学之http api篇课程

共16课时 | 0.9万人学习

PHP入门到实战消息队列RabbitMQ
PHP入门到实战消息队列RabbitMQ

共22课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号