Linux进程通信——消息队列

POSIX 消息队列是 Linux 下高性能的进程间通信(IPC)机制,支持结构化数据传输、消息优先级、阻塞 / 非阻塞模式,以下是涵盖核心函数、属性、使用规则、避坑要点的完整总结,重点对每个核心函数做精细化讲解。

一、核心概念

  1. 本质 :内核维护的消息存储队列,跨进程可见,每个队列有唯一名称(必须以 / 开头,如 /test_mq)。
  2. 核心特性
    • 消息带优先级(0~31,数值越大优先级越高),高优先级消息优先被读取;
    • 支持阻塞 / 非阻塞模式,适配不同业务场景;
    • 队列属性(最大消息数、单消息长度)创建后不可修改;
    • 每个进程需通过 mq_open() 获取独立的队列描述符(进程私有)。
    • 仅传输 "实际数据",不支持跨进程传输指针 / 动态分配内存(核心避坑点)。
  3. 编译要求 :编译时必须链接实时库 rt,命令为 gcc 代码文件.c -o 可执行文件 -lrt

二、核心函数详解(必掌握)

1. mq_open () ------ 创建 / 打开消息队列(唯一入口)

函数原型
复制代码
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, mode_t mode, const struct mq_attr *attr);
核心作用
  • 队列不存在时:带 O_CREAT 标志则创建 + 打开队列;
  • 队列已存在时:不带 O_CREAT 标志则仅打开队列。
参数详解
参数 含义与取值
name 队列名称,必须以 / 开头(如 /test_mq),长度不超过 NAME_MAX(通常 255)
oflag 打开标志(可组合):- O_RDONLY:只读打开- O_WRONLY:只写打开- O_RDWR:读写打开- O_CREAT:队列不存在则创建- O_EXCL:与 O_CREAT 配合,队列已存在则报错(避免重复创建)- O_NONBLOCK:非阻塞模式打开(仅影响当前进程的操作模式)
mode 队列权限(如 0664),仅 O_CREAT 时有效,规则同文件权限(所有者 / 组 / 其他)
attr 队列属性结构体指针,仅 O_CREAT 时有效:- 传 NULL:使用系统默认属性- 传结构体:设置 mq_maxmsg/mq_msgsize(其他属性无效)
返回值
  • 成功:返回 mqd_t 类型的队列描述符(非负整数);
  • 失败:返回 (mqd_t)-1,并设置 errno(如 EEXIST 队列已存在、EINVAL 名称格式错误)。

2. mq_send () ------ 发送消息到队列

函数原型
复制代码
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
核心作用

将字节流(字符串、结构体等)作为一条消息发送到队列,消息长度需 ≤ 队列的 mq_msgsize

参数详解
参数 含义与注意事项
mqdes mq_open() 返回的队列描述符(唯一合法值,不可填随机数)
msg_ptr 消息缓冲区指针:- 字符串:直接传字符串地址(如 const char *msg = "test")- 结构体:强制转换为 char*(如 (const char *)&stu) ❌ 禁止传指向动态内存(malloc)的指针(仅传地址,不传数据)
msg_len 消息字节长度:- 字符串:strlen(msg) + 1(包含 \0 结束符)- 结构体:sizeof(结构体)- 必须 ≤ 队列的 mq_msgsize
msg_prio 消息优先级(0~31,0 最低):- 同优先级按 FIFO 顺序读取- 不同优先级:高优先级先读取
返回值
  • 成功:返回 0;
  • 失败:返回 -1,设置 errno(如 EAGAIN 非阻塞模式下队列满、EMSGSIZE 消息长度超过 mq_msgsize)。

3. mq_receive () ------ 从队列接收消息

函数原型
复制代码
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
核心作用

从队列读取一条消息(优先高优先级),存入缓冲区,返回实际读取的字节数。

参数详解
参数 含义与注意事项
mqdes mq_open() 返回的队列描述符
msg_ptr 接收缓冲区指针:- 字符串:char buf[256] = {0}(初始化避免乱码)- 结构体:强制转换为 char*(如 (char *)&stu) - ❌ 接收含动态指针的结构体时,指针地址无效(段错误 / 乱码
msg_len 缓冲区字节长度:- 必须 ≥ 队列的 mq_msgsize(否则返回 -1errno=EMSGSIZE)- 推荐设为 mq_msgsize 或结构体大小
msg_prio 输出参数,存储接收到的消息优先级:- 不需要则传 NULL- 需要则传 unsigned int 变量地址
返回值
  • 成功:返回实际读取的字节数;
  • 失败:返回 -1,设置 errno(如 EAGAIN 非阻塞模式下队列空、EMSGSIZE 缓冲区长度不足)。

4. mq_close () ------ 关闭队列描述符

函数原型
复制代码
int mq_close(mqd_t mqdes);
核心作用

释放当前进程的队列描述符(类似文件的 close()),不删除队列本身。

参数详解
参数 含义与注意事项
mqdes mq_open() 返回的队列描述符(唯一合法值,已关闭的描述符再次传入会报错)
返回值
  • 成功:返回 0;
  • 失败:返回 -1,设置 errno(如 EBADF 无效的描述符)。
使用规则
  • 进程退出时内核会自动关闭未显式关闭的描述符,但建议显式调用(良好编程习惯);
  • 关闭后描述符失效,不可再用于 mq_send()/mq_receive()
函数原型
复制代码
int mq_unlink(const char *name);
核心作用

删除队列名称,内核会等待所有进程关闭该队列的描述符后,释放队列的内核资源(彻底删除队列)。

参数详解
参数 含义与注意事项
name 队列名称(与 mq_open()name 一致,如 /test_mq
返回值
  • 成功:返回 0;
  • 失败:返回 -1,设置 errno(如 ENOENT 队列不存在)。
使用规则
  • 仅需一个进程调用(通常是最后退出的进程,如父进程);
  • 调用后,新进程无法打开该队列,但已打开的进程仍可操作,直到所有进程关闭描述符。

6. mq_getattr () ------ 获取队列属性

函数原型
复制代码
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
核心作用

读取队列的当前属性(如当前消息数、阻塞模式、最大消息数等)。

参数详解
参数 含义与注意事项
mqdes 队列描述符
attr 输出参数,存储队列属性(需提前分配内存)
返回值
  • 成功:返回 0;
  • 失败:返回 -1,设置 errno
7. mq_setattr () ------ 设置队列属性
函数原型
复制代码
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
核心作用

仅能修改队列的 mq_flags(阻塞 / 非阻塞模式),其他属性(mq_maxmsg/mq_msgsize)创建后不可改。

参数详解
参数 含义与注意事项
mqdes 队列描述符
newattr 新属性结构体:仅 mq_flags 字段有效(0 = 阻塞,O_NONBLOCK = 非阻塞)
oldattr 输出参数,存储修改前的属性:不需要则传 NULL
返回值
  • 成功:返回 0;
  • 失败:返回 -1,设置 errno

三、队列属性(struct mq_attr)详解

函数原型
复制代码
struct mq_attr {
    long mq_flags;    // 阻塞/非阻塞模式(0 / O_NONBLOCK)
    long mq_maxmsg;   // 队列最大消息数(创建时设置,不可改)
    long mq_msgsize;  // 单消息最大字节数(创建时设置,不可改)
    long mq_curmsgs;  // 当前消息数(只读,内核维护)
};
参数详解
成员名 赋值 / 修改规则 注意事项
mq_flags 仅能通过 mq_setattr() 修改,mq_open() 时设置无效 作用于 mq_send()mq_receive():- 阻塞:队列满 / 空时等待- 非阻塞:队列满 / 空时返回 -1(EAGAIN)
mq_maxmsg mq_open(O_CREAT) 时设置,≤ 系统限制(/proc/sys/fs/mqueue/msg_max 创建后不可修改,超出限制会导致 mq_open() 失败
mq_msgsize mq_open(O_CREAT) 时设置,≤ 系统限制(/proc/sys/fs/mqueue/msgsize_max 接收缓冲区长度必须 ≥ 该值,否则 mq_receive() 失败
mq_curmsgs 只读,手动赋值无效,通过 mq_getattr() 读取 反映队列当前的消息数量,可用于监控队列状态

四、常见问题与避坑要点

问题现象 根本原因 解决方案
栈溢出(stack smashing) 接收缓冲区长度 < 消息长度 /mq_msgsize 缓冲区大小设为 ≥ mq_msgsize,初始化缓冲区为 0(如 char buf[256] = {0}
结构体解析乱码 内存对齐 / 字节序不一致 __attribute__((packed)) 取消内存对齐;跨平台转换字节序(htons/ntohs
接收顺序错乱 消息优先级不同 统一消息优先级(如都设为 0),保证 FIFO 顺序
mq_open 返回 -1 队列已存在且带 O_EXCL 去掉 O_EXCL,或先调用 mq_unlink() 删除旧队列
mq_receive 返回 -1 非阻塞模式下队列空 改用阻塞模式,或在代码中处理 EAGAIN(循环重试 + 休眠)
多进程操作队列失败 未重新调用 mq_open() 获取描述符 每个进程需独立调用 mq_open(),描述符是进程私有

五、核心规则总结

  1. 唯一性 :队列名称以 / 开头,系统内唯一;
  2. 权限 :创建队列时的 mode 参数决定其他进程能否打开;
  3. 资源清理mq_close() 仅关描述符,mq_unlink() 才彻底删除队列;
  4. 数据传输:支持任意字节流(字符串 / 结构体),仅关注长度,不解析内容;禁止传输指针 / 动态内存(仅传地址,不传数据),必须用固定数组嵌入结构体;
  5. 阻塞 / 非阻塞mq_flags 作用于所有操作(发送 + 接收),需按需设置。
相关推荐
零基础的修炼2 小时前
Linux网络---数据链路层
linux·服务器·网络
楼田莉子3 小时前
Linux学习:线程的同步与互斥
linux·运维·c++·学习
小草儿7993 小时前
PG18备份恢复
linux·运维·服务器
笑口常开xpr3 小时前
Linux 命 令 界 的 “王 炸 组 合”!Gitee 提 交 + 权 限 控 制 + 热 键 神 操,学 会 直 接 霸 屏 终 端!
linux·gitee·权限
Starry_hello world4 小时前
Linux http代码
linux·运维·http
开开心心_Every6 小时前
全屏程序切换工具,激活选中窗口快速切换
linux·运维·服务器·pdf·ocr·测试用例·模块测试
未来之窗软件服务7 小时前
AI人工智能(四)本地部署vosk-ASR环境命令—东方仙盟练气期
linux·运维·人工智能·本地模型·仙盟创梦ide·东方仙盟
~央千澈~7 小时前
抖音弹幕游戏开发之第17集:添加日志系统·优雅草云桧·卓伊凡
linux·服务器·前端
vortex58 小时前
Zellij 复制提示成功却粘贴不了?一文解决剪贴板不同步问题
linux