Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux System V 訊息佇列程式設計完整範例:msgget/msgsnd/msgrcv/msgctl 實作詳解

編輯:Linux 教學

前言

System V 訊息佇列是核心持久化的進程間通訊(IPC)機制,適合多進程非同步傳遞結構化資料。本文提供完整的發送端與接收端 C 程式碼,詳細註解四大核心 API(msgget、msgsnd、msgrcv、msgctl)的參數用法、旗標意義與錯誤處理模式。

一、核心資料結構


struct msgbuf {
    long mtype;       // 必須 > 0,作為訊息類型/優先級
    char mtext[2048]; // 訊息資料載荷,大小自定
};

二、API 快速查閱表

函數功能關鍵參數
msgget(key, flags)建立或取得佇列 IDIPC_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,接收端可依類型篩選,實現優先級或多通道。
  • 阻塞 vs 非阻塞:發送端用 IPC_NOWAIT 避免佇列滿時卡死;接收端通常用阻塞模式等待資料。
  • 佇列容量限制:系統參數 kernel.msgmnb(單佇列最大位元組)、kernel.msgmax(單則最大位元組),需依負載調整 /etc/sysctl.conf。
  • 清理機制:佇列為核心持久化,進程結束不會自動刪除,必須顯式呼叫 msgctl(IPC_RMID) 或重開機清除。
  • 替代方案:現代 Linux 建議優先使用 POSIX 訊息佇列(mq_open 等),支援通知、優先級、超時等更豐富語意。
Copyright © Windows教程網 All Rights Reserved