SystemV消息隊列揭秘:原理與實戰

目錄

一、消息隊列的基本原理

1、基本概念

2、基本原理

3、消息類型的關鍵作用

4、重要特性總結

5、生命周期管理

6、典型應用場景

二、System V 消息隊列的內核數據結構

1、消息隊列的管理結構?msqid_ds(消息隊列標識符結構)

關鍵字段解析

2、權限控制結構?ipc_perm

權限控制字段解析

3、消息結構

4、消息隊列與共享內存的對比

5、關鍵系統限制

6、總結

三、消息隊列的創建:msgget()?函數

1、函數原型

2、參數解析

key?的生成方式

msgflg?的組成

1. 權限位(低 9 位)

2. 選項標志(高位)

3、返回值

4、典型使用場景

(1) 創建新隊列

(2) 獲取已有隊列

(3) 創建獨占隊列(檢測是否已存在)

5、與共享內存 (shmget) 的對比

6、注意事項

7、完整示例

輸出示例

說明

驗證

8、總結

四、?消息隊列的釋放與管理:msgctl()?函數

1、函數原型

2、參數解析

cmd?的常用選項

3、返回值

4、典型使用場景

(1) 刪除消息隊列(釋放資源)

(2) 查詢隊列狀態

(3) 修改隊列屬性

5、與共享內存 (shmctl) 的對比

6、注意事項

7、完整示例

分析:?

8、總結

五、?向消息隊列發送數據:msgsnd()?函數

1、函數原型

2、參數解析

消息結構體?msgbuf

msgflg?的常用選項

3、返回值

4、使用示例

(1) 定義消息結構

(2) 發送消息(阻塞模式)

(3) 發送消息(非阻塞模式)

5、關鍵注意事項

6、完整代碼示例

代碼邏輯分析

7、總結

六、從消息隊列獲取數據:msgrcv()?函數詳解

1、函數原型

2、參數解析

消息結構體?msgbuf

msgtyp?的接收規則

msgflg?的常用選項

3、返回值

4、使用示例

(1) 定義消息結構

(2) 接收特定類型的消息(阻塞模式)

(3) 接收任意消息(非阻塞模式)

(4) 處理長消息(自動截斷)

5、關鍵注意事項

6、完整代碼示例

關鍵點說明:

注意:

7、總結


一、消息隊列的基本原理

????????System V消息隊列是Unix/Linux系統中一種進程間通信(IPC)機制,允許不相關的進程通過消息隊列交換數據。

1、基本概念

System V消息隊列是一種內核維護的消息鏈表,具有以下特點:

  • 消息隊列由唯一的標識符(隊列ID)標識

  • 消息是類型化的,每個消息都有一個長整型的類型字段

  • 消息可以按照類型順序讀取,而不一定是先進先出(后面會具體介紹)

  • 消息隊列是持久的,會持續到系統重啟或顯式刪除

2、基本原理

????????消息隊列是System V IPC機制中的一種進程間通信方式,其本質是在內核空間維護的一個鏈表結構。這個鏈表中的每個節點都是一個獨立的數據塊,每個數據塊包含兩個主要部分:

  • 消息類型:一個正整數,用于標識消息的類別

  • 消息內容:實際傳輸的數據

通信雙方通過以下機制實現交互:

  1. 隊列共享:不同進程通過唯一的key訪問同一個消息隊列

  2. 寫入機制:發送方總是在隊列尾部添加新的消息

  3. 讀取機制接收方可以按照特定規則從隊列中獲取消息(不一定先進先出)

3、消息類型的關鍵作用

消息類型決定了消息的歸屬和讀取方式:

  • 發送方標識:通過指定不同類型的值來區分消息的接收者

  • 選擇性接收:接收方可以指定只接收特定類型的消息

  • 優先級控制:類型值也可以用于實現消息優先級(數值越小優先級越高)

4、重要特性總結

  1. 消息隊列提供了一個從一個進程向另一個進程發送數據塊的方法。
  2. 結構化通信:支持類型化數據塊(每個數據塊都被認為是有一個類型的,接收者進程接收的數據塊可以有不同的類型值)的傳輸,比管道等字節流通信更結構化

  3. 異步通信:發送和接收進程不需要同時存在

  4. 多路復用:單個隊列可支持多個進程間的多種消息交換

  5. 內核持久性:消息會一直保留在隊列中直到被讀取(和共享內存一樣,消息隊列的資源也必須自行刪除,否則不會自動清除,因為system V IPC資源的生命周期是隨內核的)

  6. 容量限制:受系統配置參數限制(如MSGMAX, MSGMNB等)

5、生命周期管理

與System V IPC的其他資源相同:

  • 顯式刪除:必須通過msgctl(..., IPC_RMID,...)主動刪除

  • 查看命令:使用ipcs -q查看現有消息隊列

  • 清理命令:可通過ipcrm -q刪除指定的隊列

6、典型應用場景

  • 生產者-消費者模型

  • 多進程事件通知

  • 跨進程的任務分發系統

  • 需要保證消息邊界的通信場景

補充說明:

????????雖然POSIX也定義了消息隊列接口(mq_*系列函數),但System V消息隊列在現有系統中仍廣泛使用,兩者在實現和特性上有顯著差異。


二、System V 消息隊列的內核數據結構

1、消息隊列的管理結構?msqid_ds(消息隊列標識符結構

????????System V 消息隊列在內核中通過?msqid_ds?結構體進行管理(內核為每個消息隊列維護一個msqid_ds結構),該結構體存儲了消息隊列的所有關鍵信息,定義在?<linux/msg.h>?中:

struct msqid_ds {struct ipc_perm msg_perm;   // 權限控制結構struct msg *msg_first;      // 指向隊列中的第一個消息(內核內部使用)struct msg *msg_last;       // 指向隊列中的最后一個消息(內核內部使用)__kernel_time_t msg_stime;  // 最后一次調用 `msgsnd()` 的時間(發送時間)__kernel_time_t msg_rtime;  // 最后一次調用 `msgrcv()` 的時間(接收時間)__kernel_time_t msg_ctime;  // 最后一次修改隊列的時間(如權限變更)unsigned long msg_lcbytes;  // 保留字段(32位兼容)unsigned long msg_lqbytes;  // 保留字段(32位兼容)unsigned short msg_cbytes;  // 當前隊列中的總字節數unsigned short msg_qnum;    // 當前隊列中的消息數量unsigned short msg_qbytes;  // 隊列允許的最大字節數(`msg_qbytes ≤ MSGMNB`)__kernel_ipc_pid_t msg_lspid; // 最后一個執行 `msgsnd()` 的進程PID__kernel_ipc_pid_t msg_lrpid; // 最后一個執行 `msgrcv()` 的進程PID
};

關鍵字段解析

  • msg_perm:權限控制結構,決定哪些進程可以訪問該消息隊列。

  • msg_first?/?msg_last:內核內部使用,指向消息鏈表(用戶層不可見)。

  • 時間戳msg_stime?/?msg_rtime?/?msg_ctime):記錄消息隊列的關鍵操作時間,可用于監控和調試。

  • msg_qnum?和?msg_cbytes

    • msg_qnum:當前隊列中的消息數量。

    • msg_cbytes:當前隊列占用的總字節數(所有消息大小之和)。

  • msg_qbytes:隊列的最大容量(字節數),可通過?msgctl(IPC_SET)?調整,但不得超過系統限制(MSGMNB)。

  • msg_lspid?/?msg_lrpid:記錄最后一個發送(msgsnd)和接收(msgrcv)消息的進程PID,便于調試。

2、權限控制結構?ipc_perm

????????可以看到消息隊列數據結構的第一個成員是msg_perm,它和shm_perm是同一個類型的結構體變量。ipc_perm?是 System V IPC 機制的通用權限控制結構,定義在?<linux/ipc.h>?中,用于管理消息隊列、共享內存和信號量的訪問權限:

struct ipc_perm {__kernel_key_t key;    // 創建 IPC 資源時指定的 key(`ftok()` 生成)__kernel_uid_t uid;    // 所有者的用戶ID__kernel_gid_t gid;    // 所有者的組ID__kernel_uid_t cuid;   // 創建者的用戶ID__kernel_gid_t cgid;   // 創建者的組ID__kernel_mode_t mode;  // 訪問權限(如 0644)unsigned short seq;    // 序列號(內核用于管理 IPC 標識符)
};

權限控制字段解析

  • key:用于生成 IPC 標識符(msgget()?的第一個參數),通常由?ftok()?生成。

  • uid?/?gid:資源當前的所有者(可通過?msgctl(IPC_SET)?修改)。

  • cuid?/?cgid:資源的創建者(不可修改)。

  • mode

    • 訪問權限位,格式類似文件權限(如?0600?表示僅所有者可讀寫)。

    • 可通過?msgctl(IPC_SET)?修改。

3、消息結構

發送和接收消息時使用的結構:

struct msgbuf {long mtype;     // 消息類型,必須>0char mtext[1];  // 消息數據(實際長度可變)
};

4、消息隊列與共享內存的對比

特性消息隊列 (msqid_ds)共享內存 (shmid_ds)
數據結構鏈表結構,存儲消息塊線性內存區域
訪問方式類型化消息(msgrcv?按類型讀取)直接內存讀寫
同步需求自帶消息邊界,無需額外同步通常需要信號量同步
內核持久性消息保留直到被讀取內存段持久,直到顯式刪除
適用場景結構化通信、異步通知大數據量、高性能共享

5、關鍵系統限制

  • MSGMAX:單個消息的最大長度(字節),可通過?sysctl kernel.msgmax?查看。

  • MSGMNB:單個消息隊列的最大容量(字節),可通過?sysctl kernel.msgmnb?查看。

  • MSGMNI:系統范圍內最大消息隊列數量,可通過?sysctl kernel.msgmni?查看。

6、總結

  • System V 消息隊列通過?msqid_ds?結構管理,包含隊列狀態、時間戳、權限等信息。

  • ipc_perm?控制訪問權限,確保安全性。

  • 消息隊列適用于結構化、異步的進程間通信,但需注意手動釋放資源(msgctl(IPC_RMID))。

  • 與共享內存相比,消息隊列自帶消息邊界,但性能較低,適用于中小數據量通信。


三、消息隊列的創建:msgget()?函數

1、函數原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);

2、參數解析

參數類型說明
keykey_t消息隊列的唯一鍵值,通常由?ftok()?生成,或使用?IPC_PRIVATE?創建私有隊列。
msgflgint控制隊列創建的標志位,由權限位(如?0644)和選項標志(如?IPC_CREAT)按位或(|)組合。

key?的生成方式

  • ftok()?生成(推薦)
    通過文件路徑和項目ID生成唯一鍵值:

    key_t key = ftok("/path/to/file", 'A');  // 'A' 是項目ID(1~255)
  • IPC_PRIVATE

    創建一個僅當前進程可訪問的私有隊列(通常用于父子進程間通信):

    key_t key = IPC_PRIVATE;

msgflg?的組成

????????在?System V IPC(如消息隊列、共享內存、信號量)中,msgflg?參數是一個整型(int),它由?權限位(低 9 位)?和?選項標志(高位)?組合而成,通過?按位或(|?運算來設置。

1. 權限位(低 9 位)
  • 作用:控制消息隊列的訪問權限(類似 Linux 文件權限)。

  • 格式:一個?9 位?的八進制數,結構如下:

    位組含義示例
    0700所有者(User)權限rwx(讀/寫/執行)
    0070組(Group)權限rw-(讀/寫)
    0007其他用戶(Others)權限r--(只讀)

    示例

    • 0644?→ 所有者可讀可寫(6?=?110),組和其他用戶只讀(4?=?100)。

    • 0600?→ 僅所有者可讀可寫,其他用戶無權限。

2. 選項標志(高位)
標志作用示例
IPC_CREAT如果隊列不存在,則創建;否則直接返回現有隊列標識符。IPC_CREAT | 0644
IPC_EXCL與?IPC_CREAT?聯用,若隊列已存在則返回錯誤(EEXIST)。IPC_CREAT | IPC_EXCL | 0644
IPC_NOWAIT非阻塞模式,若隊列滿/空時立即返回錯誤(ENOMSG)。IPC_CREAT | IPC_NOWAIT | 0644

3、返回值

返回值說明
成功返回消息隊列的標識符(非負整數)(一個有效的消息隊列標識符(用戶層標識符),類似共享內存的shmid),用于后續操作(如?msgsnd/msgrcv)。
失敗返回?-1,并設置?errno(如?EACCES?權限不足、ENOENT?隊列不存在)。

4、典型使用場景

(1) 創建新隊列

key_t key = ftok("/tmp/msgqueue", 65);
int msqid = msgget(key, IPC_CREAT | 0666);
if (msqid == -1) 
{perror("msgget failed");exit(EXIT_FAILURE);
}

(2) 獲取已有隊列

int msqid = msgget(key, 0666);  // 不指定 IPC_CREAT,若隊列不存在則失敗

(3) 創建獨占隊列(檢測是否已存在)

int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msqid == -1 && errno == EEXIST) 
{printf("Message queue already exists.\n");
}

5、與共享內存 (shmget) 的對比

特性消息隊列 (msgget)共享內存 (shmget)
鍵值生成同樣使用?ftok()?或?IPC_PRIVATE相同
權限控制通過?msgflg?的低9位設置相同
創建標志IPC_CREAT/IPC_EXCL相同
資源類型消息鏈表結構線性內存段

6、注意事項

  1. 鍵值沖突:不同進程使用相同的?key?會訪問同一隊列,需確保?ftok()?的參數唯一。

  2. 權限管理:若權限不足(如?msgflg=0600),其他進程無法訪問隊列。

  3. 資源泄漏:消息隊列不會自動釋放,需通過?msgctl(msqid, IPC_RMID, NULL)?手動刪除。

  4. 系統限制:隊列數量受?MSGMNI?限制,可通過?sysctl kernel.msgmni?查看。

7、完整示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>int main() 
{key_t key = ftok("/tmp/msgqueue", 65);if (key == -1) {perror("ftok failed");exit(EXIT_FAILURE);}int msqid = msgget(key, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(EXIT_FAILURE);}printf("Message queue created with ID: %d\n", msqid);return 0;
}

輸出示例

Message queue created with ID: 0
(實際?msqid?由內核動態分配)

說明

  • ftok()?成功

    • 文件?/home/hmz/learn4/demo1.c?存在且可讀。

    • key?計算成功(基于文件的?inode?和?proj_id=65)。

  • msgget()?成功

    • 如果消息隊列?不存在,則創建新隊列,返回其標識符?msqid

    • 如果隊列?已存在,則直接返回其?msqid(不會報錯,因為未使用?IPC_EXCL)。

驗證

運行?ipcs -q?可查看新創建的消息隊列:(key?由?ftok()?生成,msqid?由內核分配)

ipcs -q

8、總結

  • msgget()?是創建/訪問消息隊列的入口函數,依賴?key?和權限標志。

  • 需合理管理隊列生命周期,避免資源泄漏。

  • 適用于需要結構化通信的場景(如生產者-消費者模型)。


四、?消息隊列的釋放與管理:msgctl()?函數

1、函數原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);

2、參數解析

參數類型說明
msqidint消息隊列標識符,由?msgget()?返回。
cmdint控制命令,決定該操作的類型(如刪除、查詢或設置隊列屬性)。
bufstruct msqid_ds*用于存儲或修改隊列狀態信息的緩沖區(部分命令需傳入,部分命令可設為?NULL)。

說明一下:
????????msgctl函數的參數與釋放共享內存時使用的shmctl函數的三個參數相同,只不過msgctl函數的第三個參數傳入的是消息隊列的相關數據結構。?

cmd?的常用選項

命令作用
IPC_RMID立即刪除消息隊列,并喚醒所有阻塞的讀寫進程(errno?設為?EIDRM)。buf?參數可設為?NULL
IPC_STAT獲取隊列的當前狀態信息(如權限、消息數量等),存儲到?buf?指向的?msqid_ds?結構體中。
IPC_SET修改隊列屬性(如權限?msg_perm.mode?或最大容量?msg_qbytes),需通過?buf?傳入新值。

3、返回值

返回值說明
成功返回?0
失敗返回?-1,并設置?errno(如?EINVAL?無效隊列ID、EPERM?權限不足)。

4、典型使用場景

(1) 刪除消息隊列(釋放資源)

if (msgctl(msqid, IPC_RMID, NULL) == -1) 
{perror("msgctl(IPC_RMID) failed");exit(EXIT_FAILURE);
}
printf("Message queue %d deleted.\n", msqid);

(2) 查詢隊列狀態

struct msqid_ds info;
if (msgctl(msqid, IPC_STAT, &info) == -1) 
{perror("msgctl(IPC_STAT) failed");exit(EXIT_FAILURE);
}
printf("Queue stats: %d messages, %d bytes max.\n", info.msg_qnum, info.msg_qbytes);

(3) 修改隊列屬性

struct msqid_ds info;
msgctl(msqid, IPC_STAT, &info);  // 先獲取當前狀態
info.msg_perm.mode = 0600;       // 修改權限為僅所有者可讀寫
info.msg_qbytes = 8192;          // 調整隊列最大容量為8KB
if (msgctl(msqid, IPC_SET, &info) == -1) 
{perror("msgctl(IPC_SET) failed");
}

5、與共享內存 (shmctl) 的對比

特性消息隊列 (msgctl)共享內存 (shmctl)
刪除命令IPC_RMID相同
查詢命令IPC_STAT(返回?msqid_ds?結構)IPC_STAT(返回?shmid_ds?結構)
設置命令IPC_SET(修改權限或容量)相同
關鍵差異管理消息鏈表結構管理內存段屬性

6、注意事項

  1. 權限要求

    • 刪除隊列(IPC_RMID)需具備所有者或超級用戶權限。

    • 修改屬性(IPC_SET)需與原權限匹配。

  2. 資源釋放時機:即使所有進程退出,消息隊列仍會保留,必須顯式調用?IPC_RMID?刪除。

  3. 阻塞進程的處理:刪除隊列會喚醒所有阻塞在?msgsnd/msgrcv?的進程,并使其返回錯誤(EIDRM)。

  4. 內核限制:隊列刪除后,其標識符可能被后續新建的隊列復用,需避免誤操作。

7、完整示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define PATHNAME "/home/hmz/learn4/demo2.c"int main() 
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(EXIT_FAILURE);}// 刪除隊列if (msgctl(msqid, IPC_RMID, NULL) == -1)     {perror("msgctl(IPC_RMID) failed");exit(EXIT_FAILURE);}printf("Message queue %d released.\n", msqid);return 0;
}

分析:?

  1. ftok()函數

    • 使用給定的路徑名/home/hmz/learn4/demo2.c和項目ID65生成一個唯一的鍵值(key)。

    • 如果文件存在且可訪問,ftok()會成功返回一個key_t類型的鍵;否則返回-1。

  2. msgget()函數

    • 使用生成的key創建一個新的消息隊列或訪問已存在的隊列。

    • IPC_CREAT | 0666表示如果隊列不存在則創建,并設置權限為0666(所有用戶可讀寫)。

    • 成功時返回消息隊列標識符(msqid),失敗時返回-1。

  3. msgctl()函數

    • 使用IPC_RMID命令刪除消息隊列。

    • 成功時返回0,失敗時返回-1。

  4. 可能的運行結果

    • 如果所有操作都成功,程序將輸出:Message queue [隊列ID] released.

      其中[隊列ID]是系統分配的消息隊列標識符。

    • 可能的錯誤情況:
      a) 如果ftok()失敗(如路徑不存在),會輸出錯誤并退出
      b) 如果msgget()失敗(如權限不足),會輸出"msgget failed"并退出
      c) 如果msgctl()失敗,會輸出"msgctl(IPC_RMID) failed"并退出

  5. 程序特點

    • 這是一個"創建后立即刪除"的示例程序,實際使用中通常不會這樣操作。

    • 程序沒有發送或接收任何實際消息,只是演示了消息隊列的創建和刪除。

    • 退出時使用了EXIT_FAILURE宏(值為1)表示失敗退出。

注意:每次運行程序時,系統可能會分配不同的消息隊列標識符,所以輸出的數字可能會變化。

8、總結

  • msgctl()?是管理消息隊列的核心函數,支持刪除、查詢和配置操作。

  • IPC_RMID?是釋放資源的唯一方式,需主動調用以避免內存泄漏。

  • 通過?IPC_STAT/IPC_SET?可實現精細化的隊列監控與權限控制。

  • 與共享內存的管理接口高度相似,但操作對象為消息鏈表而非內存段。


五、?向消息隊列發送數據:msgsnd()?函數

1、函數原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

2、參數解析

參數類型說明
msqidint消息隊列標識符,由?msgget()?返回。
msgpconst void*指向消息緩沖區的指針,必須符合?msgbuf?結構。
msgszsize_t消息數據的實際長度(字節數),不包括?mtype?字段
msgflgint發送標志位,控制發送行為(如非阻塞模式)。
  • 第一個參數msqid,表示消息隊列的用戶級標識符。
  • 第二個參數msgp,表示待發送的數據塊。
  • 第三個參數msgsz,表示所發送數據塊的大小
  • 第四個參數msgflg,表示發送數據塊的方式,一般默認為0即可。

消息結構體?msgbuf

struct msgbuf {long mtype;      // 消息類型(必須 > 0)char mtext[1];   // 消息數據(柔性數組,實際長度由用戶定義)
};
  • mtype:消息類型,接收方可通過該字段篩選消息(值必須為正整數)。

  • mtext:實際消息數據,定義時可擴展為任意長度(如?char mtext[100])。

msgflg?的常用選項

標志作用
0默認阻塞模式,若隊列已滿則阻塞,直到空間可用。
IPC_NOWAIT非阻塞模式,若隊列滿則立即返回?-1,并設置?errno?為?EAGAIN

3、返回值

返回值說明
成功返回?0,消息被添加到隊列尾部。
失敗返回?-1,并設置?errno(如?EINVAL?無效參數、EIDRM?隊列被刪除、EACCES?權限不足)。

4、使用示例

(1) 定義消息結構

#define MAX_MSG_SIZE 1024struct my_msg {long mtype;char mtext[MAX_MSG_SIZE];
};

(2) 發送消息(阻塞模式)

struct my_msg msg;
msg.mtype = 1;  // 設置消息類型
strcpy(msg.mtext, "Hello, Message Queue!");if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, 0) == -1) {perror("msgsnd failed");exit(EXIT_FAILURE);
}

(3) 發送消息(非阻塞模式)

if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, IPC_NOWAIT) == -1) {if (errno == EAGAIN) {printf("Queue is full. Try later.\n");} else {perror("msgsnd failed");}
}

5、關鍵注意事項

  1. 消息大小限制

    • 單條消息的長度(msgsz)不能超過系統限制?MSGMAX(可通過?cat /proc/sys/kernel/msgmax?查看)。

    • 隊列總容量受?MSGMNB?限制(所有消息的?msgsz?之和 ≤?msg_qbytes)。

  2. 內存對齊問題:msgbuf?結構中的?mtext?字段建議定義為柔性數組(C99)或足夠大的靜態數組,避免內存越界。

  3. 消息類型必須為正數:若?mtype ≤ 0msgsnd()?會返回?EINVAL?錯誤。

  4. 資源競爭處理:多進程同時發送消息時,需通過額外同步機制(如信號量)避免數據混亂。

6、完整代碼示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>#define MAX_MSG_SIZE 1024
#define PATHNAME "/home/hmz/learn4/demo4.c"struct my_msg
{long mtype;char mtext[MAX_MSG_SIZE];
};int main()
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, IPC_CREAT | 0666);  // 隊列不存在時自動創建if (msqid == -1){perror("msgget failed");exit(EXIT_FAILURE);}struct my_msg msg;msg.mtype = 1; // 消息類型snprintf(msg.mtext, MAX_MSG_SIZE, "PID %d: Hello, World!", getpid());if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, 0) == -1){perror("msgsnd failed");exit(EXIT_FAILURE);}printf("Message sent to queue %d.\n", msqid);return 0;
}

代碼邏輯分析

  1. 生成鍵值 (ftok)

    • 使用文件路徑?/home/hmz/learn4/demo4.c?和項目 ID?65?生成唯一的?key

    • 如果文件不存在或不可訪問,ftok?會失敗,返回?-1

  2. 創建/獲取消息隊列 (msgget)

    • IPC_CREAT | 0666?表示:

      • 如果隊列不存在,則創建新隊列;

      • 權限設置為?0666(所有用戶可讀寫)。

    • 如果失敗(如權限不足),返回?-1

  3. 構造并發送消息 (msgsnd)

    • 消息類型?mtype = 1(可用于接收端篩選消息)。

    • 消息內容為字符串?"PID [進程ID]: Hello, World!"(例如?"PID 1234: Hello, World!")。

    • strlen(msg.mtext) + 1?表示發送消息長度(包括終止符?\0)。

    • 如果隊列已滿或權限不足,msgsnd?會失敗。

7、總結

  • msgsnd()?用于向消息隊列發送結構化數據,需通過?msgbuf?指定類型和內容。

  • 消息長度需明確指定(不包括?mtype),且不能超過系統限制。

  • 支持阻塞和非阻塞模式,適用于不同實時性要求的場景。

  • 務必檢查返回值,確保消息成功入隊。


六、從消息隊列獲取數據:msgrcv()?函數詳解

1、函數原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

2、參數解析

參數類型說明
msqidint消息隊列標識符,由?msgget()?返回。
msgpvoid*輸出型參數,指向接收消息的緩沖區,需符合?msgbuf?結構(見下文)。
msgszsize_t緩沖區?mtext?的最大容量(字節數),不包括?mtype?字段
msgtyplong指定接收的消息類型,決定消息篩選規則(見下文)。
msgflgint接收標志位,控制接收行為(如非阻塞模式、截斷長消息等)。

消息結構體?msgbuf

struct msgbuf {long mtype;      // 消息類型(由發送方指定)char mtext[1];   // 消息數據(柔性數組,實際長度由用戶定義)
};

msgtyp?的接收規則

msgtyp?值接收行為
> 0讀取隊列中?第一條類型等于?msgtyp?的消息。
= 0讀取隊列中的?第一條消息(無論類型)。
< 0讀取隊列中?類型值 ≤ |msgtyp|?的最小類型的消息(如?-3?接收類型為?12?或?3?的消息)。

msgflg?的常用選項

標志作用
IPC_NOWAIT非阻塞模式,若隊列無匹配消息則立即返回?-1,并設置?errno?為?ENOMSG
MSG_NOERROR若消息實際長度?> msgsz,則截斷消息(不返回錯誤);未設置時,長消息會觸發?E2BIG?錯誤。
MSG_EXCEPT當?msgtyp > 0?時,接收?類型不等于?msgtyp?的第一條消息(Linux 特有擴展)。

3、返回值

返回值說明
成功返回實際接收到的?mtext?的字節數(不包括?mtype)。
失敗返回?-1,并設置?errno(如?EINVAL?無效參數、EIDRM?隊列被刪除、EACCES?權限不足)。

4、使用示例

(1) 定義消息結構

#define MAX_MSG_SIZE 1024struct my_msg {long mtype;char mtext[MAX_MSG_SIZE];
};

(2) 接收特定類型的消息(阻塞模式)

struct my_msg msg;
ssize_t bytes = msgrcv(msqid, &msg, MAX_MSG_SIZE, 1, 0);  // 接收類型為1的消息
if (bytes == -1) {perror("msgrcv failed");exit(EXIT_FAILURE);
}
printf("Received: %s\n", msg.mtext);

(3) 接收任意消息(非阻塞模式)

ssize_t bytes = msgrcv(msqid, &msg, MAX_MSG_SIZE, 0, IPC_NOWAIT);
if (bytes == -1) {if (errno == ENOMSG) {printf("No message available.\n");} else {perror("msgrcv failed");}
}

(4) 處理長消息(自動截斷)

ssize_t bytes = msgrcv(msqid, &msg, 100, 0, MSG_NOERROR);  // 最多接收100字節
if (bytes == -1) {perror("msgrcv failed");
} else if (bytes == 100) {printf("Message truncated to 100 bytes.\n");
}

5、關鍵注意事項

  1. 緩沖區大小

    • msgsz?必須 ≥ 消息的實際長度(除非使用?MSG_NOERROR)。

    • 建議將?mtext?定義為足夠大的數組(如?char mtext[4096])。

  2. 消息類型篩選

    • 使用?msgtyp?可實現優先級消息處理(如類型值越小優先級越高)。

    • msgtyp < 0?時,內核會遍歷隊列尋找匹配的最小類型消息。

  3. 阻塞與非阻塞

    • 默認阻塞模式下,若隊列無匹配消息,進程會休眠等待。

    • IPC_NOWAIT?適用于需要輪詢的場景。

  4. 隊列刪除處理:若隊列在阻塞期間被刪除(IPC_RMID),msgrcv()?會立即返回?-1,并設置?errno?為?EIDRM

6、完整代碼示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>#define MAX_MSG_SIZE 1024
#define PATHNAME "/home/hmz/learn4/demo4.c"struct my_msg
{long mtype;char mtext[MAX_MSG_SIZE];
};int main()
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, 0666);if (msqid == -1){perror("msgget failed");exit(EXIT_FAILURE);}struct my_msg msg;if (msgrcv(msqid, &msg, MAX_MSG_SIZE, 1, 0) == -1) // 接收類型為1的消息{perror("msgrcv failed");exit(EXIT_FAILURE);}printf("Received message: %s\n", msg.mtext);// 可選:刪除消息隊列// if (msgctl(msqid, IPC_RMID, NULL) == -1) {//     perror("msgctl failed");//     exit(EXIT_FAILURE);// }return 0;
}

????????上面第五點的發送代碼,消息隊列中已經有了,下面是一個對應的接收端代碼。這個代碼會從消息隊列中讀取發送的消息:

關鍵點說明:

  1. 使用相同的PATHNAME和項目ID(65)生成相同的key

  2. 使用msgget獲取相同的消息隊列ID(不需要IPC_CREAT標志)

  3. 使用msgrcv接收類型為1的消息(與發送端設置的mtype匹配)

  4. 接收的消息會存儲在msg結構體中,可以通過msg.mtext訪問消息內容

注意:

  • 接收端運行時需要確保消息隊列已存在(即發送端已運行過)

  • 如果要刪除消息隊列,可以取消注釋最后的msgctl代碼

  • 如果發送端和接收端在不同的終端運行,確保工作目錄相同或PATHNAME路徑正確

7、總結

  • msgrcv()?是消息隊列的核心接收函數,支持按類型篩選和多種接收模式。

  • 通過?msgtyp?和?msgflg?可靈活控制消息選擇策略(如優先級、非阻塞等)。

  • 務必檢查返回值和錯誤碼,確保消息完整性和程序健壯性。

  • 與?msgsnd()?配合使用,可實現進程間結構化通信(如任務分發、事件通知等)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916578.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916578.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916578.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

5 分鐘上手 Firecrawl

文章目錄Firecrawl 是什么&#xff1f;本地部署驗證mcp安裝palyground&#x1f525; 5 分鐘上手 FirecrawlFirecrawl 是什么&#xff1f; 一句話&#xff1a; 開源版的 “最強網頁爬蟲 清洗引擎” ? 自動把任意網頁 → 結構化 Markdown / JSON ? 支持遞歸整站抓取、JS 渲染…

算法訓練營day31 貪心算法⑤56. 合并區間、738.單調遞增的數字 、968.監控二叉樹

貪心算法的最后一篇博客&#xff01;前面兩道題都是比較簡單的思路&#xff0c;重點理解一下最后一道題即可。有一說一&#xff0c;進入到貪心算法這一章節之后&#xff0c;我的博客里和代碼注釋里的內容明顯少了很多&#xff0c;因為很多貪心的題目我覺得不需要很復雜的文字說…

Jenkins流水線部署+webhook2.0

文章目錄1. 環境2. 用到的插件3. 流水線部署腳本1. 環境 Centos7Jenkins2.5.0JDKopen17阿里云倉庫 注意&#xff1a;這個版本兼容需要特別注意&#xff0c;要不然會很麻煩 2. 用到的插件 Generic Webhook Trigger 3. 流水線部署腳本 兼容鉤子部署&#xff08;webhook&…

IDM下載失敗排查

網絡連接問題排查檢查網絡連接是否穩定&#xff0c;確保能夠正常訪問互聯網 測試其他下載工具或瀏覽器是否能夠正常下載 嘗試關閉防火墻或殺毒軟件&#xff0c;排除安全軟件攔截的可能性代理和VPN設置檢查確認IDM的代理設置是否正確&#xff0c;是否與系統代理一致 檢查是否使用…

Anaconda安裝時的幾個操作

一、安裝Anaconda 其實Anaconda的安裝比較簡單&#xff0c;點擊next就好了。在安裝中需要注意以下兩點&#xff1a; 1、選擇安裝路徑 在安裝時&#xff0c;路徑最好選擇非C盤&#xff0c;且路徑中不要出現中文&#xff0c;以免后期運行代碼時出現不必要的錯誤。 我安裝時&…

網易易盾、騰訊ACE等主流10款游戲反外掛系統對比

本文將深入對比10款游戲反外掛系統&#xff1a;1.網易易盾&#xff1b;2.Ricochet Anti?Cheat&#xff1b;3.BattlEye&#xff1b;4.幾維安全手游智能反外掛系統&#xff1b;5.伏魔AI反外掛&#xff1b;6.Riot Vanguard&#xff1b;7.Xigncode3&#xff1b;8.盛大GPK&#xff…

wpa_supplicant-2.10交叉編譯

參考文章:https://blog.csdn.net/weixin_45783574/article/details/145810790 1、Openssl交叉編譯 1.1 下載openssl-1.1.1t.tar.gz 下載網址: https://openssl-library.org/source/old/1.1.1/index.html1.2 編譯 sudo tar xvf openssl-1.1.1t.tar.gz cd openssl-1.1

源碼解讀SpringCloudAlibaba Nacos2.x

Nacos 服務注冊 Nacos 服務注冊時&#xff0c;客戶端會將自己的信息注冊到Nicosserver上&#xff0c;形成key-value組合&#xff0c;其中key通常是服務名稱&#xff0c;value是實例地址信息。在二點X版本中&#xff0c;客戶端通過Spring Boot的擴展機制(例如web_initialized事件…

Windows 11 下 Anaconda 命令修復指南及常見問題解決

Windows 11 下 Anaconda 命令修復指南及常見問題解決 在使用 Anaconda 過程中&#xff0c;可能會遇到環境損壞、更新失敗、包依賴沖突等問題。本文整理了一套通過命令行修復 Anaconda 的完整方案&#xff0c;適用于 Windows 11 系統&#xff0c;同時補充了權威參考鏈接供深入學…

安寶特案例丨全球連線!安寶特Vuzix與RodsCones共筑實時手術教育平臺

安寶特Vuzix與合作伙伴Rods&Cones協作&#xff0c;為Rocamed在布拉格UROSANIT診所舉辦的創新型實時手術直播研討會提供技術賦能。 本次直播通過合作伙伴Rods&Cones軟件平臺搭載安寶特Vuzix智能眼鏡&#xff0c;成功連接來自9國、3大洲、6個時區的27位醫生&#xff0c;…

【Spring Boot 快速開發】一、入門

目錄Spring Boot 簡介Web 入門Spring Boot 快速入門HTTP 協議概述請求協議響應協議解析協議TomcatSpring Boot 簡介 Spring Boot 是由 Pivotal 團隊&#xff08;后被 VMware 收購&#xff09;開發的基于 Spring 框架的開源項目&#xff0c;于 2014 年首次發布。其核心目標是簡…

laravel chunkById導出數據亂序問題

2025年7月28日17:47:29 這幾天在做數據導出優化&#xff0c;使用xlswriter作為導出組件&#xff0c;但是發現在 使用 $base->chunkById(2000, function ($list) use ($writer, $sheet1) { 發現導出的數據是亂的&#xff0c;偶爾有些重復&#xff0c;偶爾有些少了&#xff0c…

Spring IOC與DI

spring的兩大思想:IOC與AOP一、ioc的概念什么叫控制翻轉?之前:對象的使用方,創建對象,對象的控制權,在對象的使用方手中.spring:對象的控制權交給了spring.舉個例子:智能駕駛,之前車的使用權在人手中,而現在在ai手中,這就是控制反轉.什么叫ioc:之前車企生產車需要做整個車,費事…

【圖像處理基石】Segment Anything Model (SAM) 調研

Segment Anything Model (SAM) 是由 Meta AI 開發的革命性圖像分割模型,它能夠對圖像中的任何物體進行分割,無需針對特定類別進行訓練。SAM 具有以下特點: 通用性:可以分割任何視覺對象,無論是否見過該類別 靈活性:支持多種輸入提示(點、框、掩碼或文本) 實時性:在普通…

unisS5800XP-G交換機配置命令之端口篇

一、批量配置端口(1) 進入系統視圖。system-view(2) 指定接口范圍&#xff0c;并進入接口批量配置視圖。¡ 指定一個不帶別名的接口列表。interface range { interface-type interface-number [ to interface-type interface-number ] } &<1-24>¡…

MySQL中的 redolog

什么是redo log如果我們只在內存的 Bufer Pool中修改了頁面&#xff0c;假設在事務提交后突然發生了某個故障導致內存中的數據都失效了&#xff0c;那么這個已經提交的事務在數據庫中所做的更改也就跟著丟失了&#xff0c;這是我們所不能忍受的。那么&#xff0c;如何保證這個持…

數據結構之 【排序】(非遞歸實現快速排序)

目錄 1.引入 2.非遞歸實現快排的思想 3.非遞歸實現快排圖解 4.完整代碼 1.引入 遞歸不可避免的話題就是防止棧溢出 所以程序員需要具備遞歸改非遞歸的能力 &#xff0c;一般來說&#xff0c;抓住遞歸中變化的量是關鍵 void QuickSort(int* a, int left, int right){if (left…

CLAP文本-音頻基礎模型: LEARNING AUDIO CONCEPTS FROM NATURAL LANGUAGE SUPERVISION

一、TL&#xff1b;DR 現在的做法有什么問題&#xff1f;主流范式是 “一個類別標簽對應多個錄音”&#xff0c;需要提前標注預測預先定義的類別&#xff0c;只能做閉集理解&#xff0c;失去靈活性 我們怎么做&#xff1f;通過兩個編碼器和對比學習機制建立語言與音頻的關聯&a…

Flink2.0學習筆記:Stream API 常用轉換算子

EC0720/FLINKTASK-TEST-STREAM/demo at master stevensu1/EC0720 先看測試效果&#xff1a;控制臺 測試效果&#xff1a;監控服務端 主要的轉換算子包括&#xff1a; 轉換算子 filter:過濾包含“Flink”的輸入 轉換算子 map: 將每行數據前添加“Processed: ”并轉為大寫 轉…

一、Python環境、Jupyter與Pycharm

安裝Python由于RAG項目中所需要的Python版本必須高于3.8&#xff0c;經過篩選&#xff0c;最終選擇了3.10.11這個版本py --version Python 3.10.11安裝過程略過&#xff0c;但對于幾個基礎的命令作個筆記記錄where python找到python啟動器的位置D:\>where python C:\Users\x…