一、什么是消息队列?
✅ 概念:
消息队列是一种进程间通信(IPC)机制,允许不同进程之间以消息的形式交换数据。
消息队列是基于内核实现的,具有以下特点:
-
以消息为单位传递数据(结构化)
-
可以异步通信,发送方和接收方不需要同时运行
-
支持无亲缘关系的进程通信
-
消息带有类型,可按类型读取
二、消息队列的组成与结构
+------------------+ +------------------+ +------------------+
| Sender | -> | Message Queue | -> | Receiver |
+------------------+ +------------------+ +------------------+┌───────────────┐│ 内核中的消息队列 │└───────────────┘
每条消息:
+---------+--------------------+
| msgtype | msgtext(数据内容)|
+---------+--------------------+
-
msgtype:长整型,用于标记消息类型(例如 1、2、3…) -
msgtext:实际传输的文本或数据(字符数组)
三、常用的消息队列函数(System V IPC)
| 函数名 | 用途 |
|---|---|
msgget() | 创建或获取一个消息队列 |
msgsnd() | 向队列发送消息 |
msgrcv() | 从队列接收消息 |
msgctl() | 控制或删除消息队列 |
💡 引入头文件
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
四、消息队列通信流程图
进程A (Sender) 进程B (Receiver)──────────────── ────────────────1. msgget() 创建/打开队列 1. msgget() 创建/打开队列2. msgsnd() 发送消息 2. msgrcv() 接收消息3. msgctl() 删除(可选) 3. msgctl() 删除(可选)
五、完整代码示例(Sender 和 Receiver)
📤 sender.c(发送进程)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSGSIZE 128// 定义消息结构体
struct msgbuf {long mtype; // 消息类型,必须为正数char mtext[MSGSIZE]; // 消息正文
};int main() {key_t key = ftok("msgqueuefile", 65); // 生成唯一key(需保证msgqueuefile存在)int msgid = msgget(key, 0666 | IPC_CREAT); // 创建或获取消息队列struct msgbuf message;message.mtype = 1; // 自定义类型strcpy(message.mtext, "Hello from sender process!");msgsnd(msgid, &message, sizeof(message.mtext), 0); // 发送消息printf("Message sent: %s\n", message.mtext);return 0;
}
📥 receiver.c(接收进程)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSGSIZE 128// 同样的结构体定义
struct msgbuf {long mtype;char mtext[MSGSIZE];
};int main() {key_t key = ftok("msgqueuefile", 65);int msgid = msgget(key, 0666 | IPC_CREAT);struct msgbuf message;// 从队列中接收类型为1的消息msgrcv(msgid, &message, sizeof(message.mtext), 1, 0);printf("Message received: %s\n", message.mtext);// 删除消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}
🧪 编译与测试
1.创建一个空文件用于 ftok():
touch msgqueuefile
2.编译:
gcc sender.c -o sender
gcc receiver.c -o receiver
3.在两个终端分别运行:
./receiver # 先运行接收端(阻塞等待)
./sender # 再运行发送端
六、常见问题与注意事项
⚠️ 注意事项汇总:
| 问题 | 说明 |
|---|---|
msgtype 必须 > 0 | 否则消息无法识别或接收 |
| 队列未删除会残留在系统中 | 使用 msgctl(..., IPC_RMID) 删除 |
ftok() 文件必须存在 | 否则返回失败 |
| 消息大小限制 | 一般最大为 8192 字节左右 |
| 发送/接收数据需匹配结构体 | 否则会出错或乱码 |
🔍 查看当前系统消息队列
ipcs -q
🗑️ 手动删除消息队列
ipcrm -q <msgid>
七、总结:消息队列的优缺点
✅ 优点:
-
支持异步通信
-
可传输结构化数据
-
支持任意进程间通信(无须亲缘关系)
❌ 缺点:
-
队列容量有限
-
不适合大数据量传输
-
编程接口略繁琐(相比管道)