System V 訊息佇列是核心持久化的進程間通訊(IPC)機制,適合多進程非同步傳遞結構化資料。本文提供完整的發送端與接收端 C 程式碼,詳細註解四大核心 API(msgget、msgsnd、msgrcv、msgctl)的參數用法、旗標意義與錯誤處理模式。
struct msgbuf {
long mtype; // 必須 > 0,作為訊息類型/優先級
char mtext[2048]; // 訊息資料載荷,大小自定
};
| 函數 | 功能 | 關鍵參數 |
|---|---|---|
msgget(key, flags) | 建立或取得佇列 ID | IPC_CREAT 建立、IPC_EXCL 獨佔檢查、權限 0666 |
msgsnd(msqid, &msg, size, flags) | 發送訊息 | IPC_NOWAIT 非阻塞、預設阻塞直到佇列有空間 |
msgrcv(msqid, &msg, size, msgtyp, flags) | 接收訊息 | msgtyp=0 取第一則、>0 取指定類型、IPC_NOWAIT 非阻塞 |
msgctl(msqid, cmd, &buf) | 控制佇列 | IPC_STAT 取狀態、IPC_SET 設屬性、IPC_RMID 刪除佇列 |
/* send.c */
#include
#include
#include
#include
#include
#include
#include
#define MSGKEY 1024
struct msgstru {
long msgtype;
char msgtext[2048];
};
int main() {
struct msgstru msgs;
int msg_type;
char str[256];
int ret, msqid;
/* 檢查佇列是否存在,不存在則建立 */
msqid = msgget(MSGKEY, IPC_EXCL);
if (msqid < 0) {
msqid = msgget(MSGKEY, IPC_CREAT | 0666);
if (msqid < 0) {
perror("msgget create failed");
exit(EXIT_FAILURE);
}
}
while (1) {
printf("Input message type (0 to quit): ");
if (scanf("%d", &msg_type) != 1) break;
if (msg_type == 0) break;
printf("Input message text: ");
scanf("%255s", str);
msgs.msgtype = msg_type;
strcpy(msgs.msgtext, str);
/* 非阻塞發送 */
ret = msgsnd(msqid, &msgs, sizeof(struct msgstru), IPC_NOWAIT);
if (ret < 0) {
perror("msgsnd failed");
exit(EXIT_FAILURE);
}
}
/* 刪除佇列(演示用,實際常由獨立清理程序執行) */
msgctl(msqid, IPC_RMID, NULL);
return 0;
}
/* receive.c */
#include
#include
#include
#include
#include
#include
#include
#define MSGKEY 1024
struct msgstru {
long msgtype;
char msgtext[2048];
};
void child_proc() {
struct msgstru msgs;
int msqid, ret;
while (1) {
msqid = msgget(MSGKEY, IPC_EXCL);
if (msqid < 0) {
fprintf(stderr, "Queue not exist, retry... (%s)\n", strerror(errno));
sleep(2);
continue;
}
/* 阻塞接收任意類型訊息 */
ret = msgrcv(msqid, &msgs, sizeof(struct msgstru), 0, 0);
if (ret < 0) {
perror("msgrcv failed");
continue;
}
printf("[PID %ld] Received type=%ld text=%s\n", (long)getpid(), msgs.msgtype, msgs.msgtext);
}
}
int main() {
for (int i = 0; i < 5; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
} else if (pid == 0) {
child_proc(); // 子進程永不返回
}
}
/* 父進程可在此加入 wait 或信號處理 */
while (1) sleep(100);
return 0;
}
gcc -o send send.c
gcc -o receive receive.c
# 終端機 1 執行接收端(會 fork 5 個子進程)
./receive &
# 終端機 2 執行發送端
./send
# 輸入類型 1、文字 hello → 5 個子進程其中之一會收到並印出
mtype > 0,接收端可依類型篩選,實現優先級或多通道。IPC_NOWAIT 避免佇列滿時卡死;接收端通常用阻塞模式等待資料。kernel.msgmnb(單佇列最大位元組)、kernel.msgmax(單則最大位元組),需依負載調整 /etc/sysctl.conf。msgctl(IPC_RMID) 或重開機清除。mq_open 等),支援通知、優先級、超時等更豐富語意。