进程间通信——消息队列

一、什么是消息队列?

✅ 概念:

消息队列是一种进程间通信(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>

七、总结:消息队列的优缺点

✅ 优点:

  • 支持异步通信

  • 可传输结构化数据

  • 支持任意进程间通信(无须亲缘关系)

❌ 缺点:

  • 队列容量有限

  • 不适合大数据量传输

  • 编程接口略繁琐(相比管道)