消息隊列(Message Queue)是一種進程間通信(IPC)機制,它允許進程通過在隊列中添加和讀取消息來交換數據。與管道(命名/匿名)相比,消息隊列具有結構化消息、異步通信和消息持久化等特點,更適合復雜的進程間數據交換場景。
核心特性
-
消息結構化
每個消息都有一個類型標識(通常是整數)和數據內容,接收進程可以根據類型選擇性讀取消息,而無需按順序處理所有數據。 -
異步通信
發送進程發送消息后無需等待接收進程立即處理,可繼續執行其他操作;接收進程可在需要時讀取消息,兩者無需同步運行。 -
消息持久化
消息存儲在內核空間,即使發送進程退出,消息也會保留在隊列中,直到被接收進程讀取或手動刪除。 -
多進程交互
多個進程可以向同一消息隊列發送消息,也可以從隊列中讀取消息(通過類型篩選實現一對一、一對多通信)。
消息隊列的使用(System V 消息隊列,Linux 為例)
System V 消息隊列是最常用的實現,通過以下系統調用操作:
msgget()
:創建或獲取消息隊列msgsnd()
:發送消息到隊列msgrcv()
:從隊列接收消息msgctl()
:控制消息隊列(如刪除、獲取狀態)
1. 消息結構定義
消息需要按固定格式定義,包含類型和數據:
#include <sys/msg.h>// 消息結構(必須以 long 類型的 mtype 開頭)
struct msgbuf {long mtype; // 消息類型(>0)char mtext[1024]; // 消息數據(可自定義大小和類型)
};
2. 創建/獲取消息隊列(msgget)
// 創建或獲取消息隊列,返回隊列 ID
int msgid = msgget(key_t key, int flags);
key
:用于標識消息隊列的鍵值(可通過ftok()
生成唯一鍵)flags
:創建權限和操作標志(如IPC_CREAT | 0666
表示創建隊列,權限為 666)
3. 發送消息(msgsnd)
// 向隊列發送消息,成功返回 0,失敗返回 -1
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
msgid
:消息隊列 IDmsgp
:指向消息結構的指針msgsz
:消息數據部分(mtext
)的長度msgflg
:發送標志(0
表示阻塞,IPC_NOWAIT
表示非阻塞)
4. 接收消息(msgrcv)
// 從隊列接收消息,成功返回接收的字節數,失敗返回 -1
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgtyp
:指定接收的消息類型(0
接收任意類型,>0
接收指定類型,<0
接收小于等于其絕對值的類型)- 其他參數同
msgsnd
5. 控制消息隊列(msgctl)
// 控制消息隊列(如刪除),成功返回 0,失敗返回 -1
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
cmd
:操作命令(IPC_RMID
表示刪除隊列)
完整示例
發送進程(sender.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>struct msgbuf {long mtype;char mtext[1024];
};int main() {// 生成唯一鍵(文件路徑和項目ID需與接收進程一致)key_t key = ftok("/tmp", 'A');if (key == -1) {perror("ftok failed");exit(1);}// 創建或獲取消息隊列int msgid = msgget(key, IPC_CREAT | 0666);if (msgid == -1) {perror("msgget failed");exit(1);}// 準備消息(類型為 1,數據為 "Hello, receiver!")struct msgbuf msg;msg.mtype = 1;strcpy(msg.mtext, "Hello, receiver!");// 發送消息if (msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0) == -1) {perror("msgsnd failed");exit(1);}printf("發送消息: %s\n", msg.mtext);return 0;
}
接收進程(receiver.c)
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>struct msgbuf {long mtype;char mtext[1024];
};int main() {// 生成與發送進程相同的鍵key_t key = ftok("/tmp", 'A');if (key == -1) {perror("ftok failed");exit(1);}// 獲取消息隊列(不創建,只連接已存在的)int msgid = msgget(key, 0666);if (msgid == -1) {perror("msgget failed");exit(1);}// 接收消息(只接收類型為 1 的消息)struct msgbuf msg;ssize_t n = msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);if (n == -1) {perror("msgrcv failed");exit(1);}printf("接收消息: %s\n", msg.mtext);// 接收完成后刪除消息隊列(可選)if (msgctl(msgid, IPC_RMID, NULL) == -1) {perror("msgctl failed");exit(1);}return 0;
}
運行方式:
- 先編譯并運行接收進程(會阻塞等待消息)。
- 再編譯并運行發送進程(發送消息后,接收進程會立即輸出并刪除隊列)。
關鍵注意事項
-
消息類型的作用
接收進程可通過msgtyp
篩選消息,例如:- 按優先級處理(高類型消息優先)
- 實現多進程定向通信(不同進程使用不同類型)
-
消息大小限制
系統對單條消息的大小有限制(可通過msgmax
配置),超過限制會導致發送失敗。 -
隊列容量限制
消息隊列的總字節數也有限制(msgmnb
),滿隊列時發送操作會阻塞(非阻塞模式下返回錯誤)。 -
資源釋放
消息隊列不會自動銷毀,需通過msgctl(..., IPC_RMID, ...)
手動刪除,否則會殘留內核中占用資源。 -
與其他 IPC 的對比
機制 特點 適用場景 消息隊列 結構化消息、異步、按類型讀取 復雜數據交換、多進程通信 管道 流式數據、簡單、順序讀取 簡單命令交互、父子進程通信 共享內存 速度最快、直接訪問內存 高頻數據交換、大數據量傳輸 信號量 用于同步和互斥,不傳遞數據 控制進程對共享資源的訪問
應用場景
- 分布式系統中的進程協作(如服務端與多個客戶端的消息交互)。
- 日志收集系統(不同進程按類型發送日志,收集進程分類處理)。
- 任務調度(調度進程發送任務消息,工作進程按類型接收并執行)。
消息隊列通過結構化和異步特性,簡化了復雜進程間通信的設計,是中大型系統中常用的 IPC 方案。