【Linux系统编程】进程间通信--消息队列
概述
消息隊列提供了一種在兩個不相關的進程之間傳遞數據的簡單高效的方法,其特點如下:
1)消息隊列可以實現消息的隨機查詢。消息不一定要以先進先出的次序讀取,編程時可以按消息的類型讀取。
2)消息隊列允許一個或多個進程向它寫入或者讀取消息。
3)與無名管道、命名管道一樣,從消息隊列中讀出消息,消息隊列中對應的數據都會被刪除。
4)每個消息隊列都有消息隊列標識符,消息隊列的標識符在整個系統中是唯一的。
5)消息隊列是消息的鏈表,存放在內存中,由內核維護。只有內核重啟或人工刪除消息隊列時,該消息隊列才會被刪除。若不人工刪除消息隊列,消息隊列會一直存在于系統中。
消息隊列常用操作函數如下:
#include <sys/msg.h> #include <sys/types.h> #include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id); int msgget(key_t key, int msgflg); int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg); int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg); int msgctl(int msqid, int cmd, struct msqid_ds *buf);對于消息隊列的操作,我們可以類比為這么一個過程:假如 A 有個東西要給 B,因為某些原因 A 不能當面直接給 B,這時候他們需要借助第三方托管(如銀行),A 找到某個具體地址的建設銀行,然后把東西放到某個保險柜里(如 1 號保險柜),對于 B 而言,要想成功取出 A 的東西,必須保證去同一地址的同一間銀行取東西,而且只有 1 號保險柜的東西才是 A 給自己的。
對于上面的例子,涉及到幾個比較重要的信息:地址、銀行、保險柜號碼。
只有同一個地址才能保證是同一個銀行,只有是同一個銀行雙方才能借助它來托管,只有同一個保險柜號碼才能保證是對方托管給自己的東西。
而在消息隊列操作中,鍵(key)值相當于地址,消息隊列標示符相當于具體的某個銀行,消息類型相當于保險柜號碼。
同一個鍵(key)值可以保證是同一個消息隊列,同一個消息隊列標示符才能保證不同的進程可以相互通信,同一個消息類型才能保證某個進程取出是對方的信息。
鍵(key)值
System V 提供的進程間通信機制需要一個 key 值,通過 key 值就可在系統內獲得一個唯一的消息隊列標識符。key 值可以是人為指定的,也可以通過 ftok() 函數獲得。
需要的頭文件:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
獲取鍵(key)值
參數:
pathname:?路徑名
proj_id:?項目ID,非 0 整數(只有低 8 位有效)
返回值:
成功:key 值
失敗:-1
消息隊列的創建
所需頭文件:
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:
創建一個新的或打開一個已經存在的消息隊列。不同的進程調用此函數,只要用相同的 key 值就能得到同一個消息隊列的標識符。
參數:
key: ftok() 返回的 key 值
msgflg:?標識函數的行為及消息隊列的權限,其取值如下:
IPC_CREAT:創建消息隊列。
IPC_EXCL: 檢測消息隊列是否存在。
位或權限位:消息隊列位或權限位后可以設置消息隊列的訪問權限,格式和open() 函數的 mode_t 一樣,但可執行權限未使用。
返回值:
成功:消息隊列的標識符
失敗:-1
示例代碼如下:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h>int main(int argc, char *argv[]) {key_t key;int msgqid;key = ftok(".", 2012); // key 值// 創建消息隊列msgqid = msgget(key, IPC_CREAT|0666);return 0; }
運行結果如下:
消息隊列的讀寫操作
對于消息隊列的讀寫,都是以消息類型為準。消息類型相當于保險柜號碼,A 往 1 號保險柜放東西,對方想取出 A 的東西必須也是從 1 號保險柜里取。同理,某一進程往消息隊列添加 a 類型的消息,別的進程要想取出這進程添加的信息也必須取 a 類型的消息。
在學習消息隊列讀寫操作前,我們先學習消息隊列的消息格式:
typedef struct _msg {long mtype; // 消息類型char mtext[100]; // 消息正文//…… …… // 消息的正文可以有多個成員 }MSG;消息類型必須是長整型的,而且必須是結構體類型的第一個成員,類型下面是消息正文,正文可以有多個成員(正文成員可以是任意數據類型的)。至于這個結構體類型叫什么名字,里面成員叫什么名字,自行定義,沒有明文規定。
添加信息
所需頭文件:
#include <sys/msg.h>
int msgsnd( ?int msqid,?const void *msgp,?size_t msgsz,?int msgflg);
功能:
將新消息添加到消息隊列。
參數:
msqid: 消息隊列的標識符。
msgp: ?待發送消息結構體的地址。
msgsz: 消息正文的字節數。
msgflg:函數的控制屬性,其取值如下:
0:msgsnd() 調用阻塞直到條件滿足為止。
IPC_NOWAIT: 若消息沒有立即發送則調用該函數的進程會立即返回。
返回值:
成功:0
失敗:-1
獲取信息
所需頭文件:
#include <sys/msg.h>
ssize_t msgrcv( int msqid,?void *msgp, ?size_t msgsz,?long msgtyp,?int msgflg );
功能:
從標識符為 msqid 的消息隊列中接收一個消息。一旦接收消息成功,則消息在消息隊列中被刪除。
參數:
msqid:消息隊列的標識符,代表要從哪個消息列中獲取消息。
msgp: 存放消息結構體的地址。
msgsz:消息正文的字節數。
msgtyp:消息的類型??梢杂幸韵聨追N類型:
msgtyp = 0:返回隊列中的第一個消息。
msgtyp > 0:返回隊列中消息類型為 msgtyp 的消息(常用)。
msgtyp < 0:返回隊列中消息類型值小于或等于 msgtyp 絕對值的消息,如果這種消息有若干個,則取類型值最小的消息。
注意:在獲取某類型消息的時候,若隊列中有多條此類型的消息,則獲取最先添加的消息,即先進先出原則。
0:msgrcv() 調用阻塞直到接收消息成功為止。
MSG_NOERROR: 若返回的消息字節數比 nbytes 字節數多,則消息就會截短到 nbytes 字節,且不通知消息發送進程。
IPC_NOWAIT: 調用進程會立即返回。若沒有收到消息則立即返回 -1。
返回值:
成功:讀取消息的長度
失敗:-1
消息隊列的控制
所需頭文件:
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:
對消息隊列進行各種控制,如修改消息隊列的屬性,或刪除消息消息隊列。
參數:
msqid:消息隊列的標識符。
cmd:函數功能的控制。其取值如下:
IPC_RMID:刪除由 msqid 指示的消息隊列,將它從系統中刪除并破壞相關數據結構。
IPC_STAT:將 msqid 相關的數據結構中各個元素的當前值存入到由 buf 指向的結構中。相對于,把消息隊列的屬性備份到 buf 里。
IPC_SET:將 msqid 相關的數據結構中的元素設置為由 buf 指向的結構中的對應值。相當于,消息隊列原來的屬性值清空,再由 buf 來替換。
buf:msqid_ds 數據類型的地址,用來存放或更改消息隊列的屬性。
返回值:
成功:0
失敗:-1
實踐示例
寫端示例代碼如下:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h>typedef struct _msg {long mtype;char mtext[50]; }MSG;int main(int argc, char *argv[]) {key_t key;int msgqid;MSG msg;key = ftok("./", 2015); // key 值// 創建消息隊列msgqid = msgget(key, IPC_CREAT|0666);if(msgqid == -1){perror("msgget");exit(-1);}msg.mtype = 10; // 消息類型strcpy(msg.mtext, "hello mike"); // 正文內容/* 添加消息msg_id:消息隊列標識符&msg:消息結構體地址sizeof(msg)-sizeof(long):消息正文大小0:習慣用0*/msgsnd(msgqid, &msg, sizeof(msg)-sizeof(long), 0);return 0; }
讀端示例代碼如下:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h>typedef struct _msg {long mtype;char mtext[50]; }MSG;int main(int argc, char *argv[]) {key_t key;int msgqid;key = ftok("./", 2015); // key 值// 創建消息隊列msgqid = msgget(key, IPC_CREAT|0666);if(msgqid == -1){perror("msgget");exit(-1);}MSG msg;memset(&msg, 0, sizeof(msg));/* 取出類型為 10 的消息msg_id:消息隊列標識符&msg:消息結構體地址sizeof(msg)-sizeof(long):消息正文大小(long)10:消息的類型0:習慣用0*/msgrcv(msgqid, &msg, sizeof(msg)-sizeof(long), (long)10, 0);printf("msg.mtext=%s\n", msg.mtext); // 把消息隊列刪除// IPC_RMID:刪除標志位msgctl(msgqid, IPC_RMID, NULL);return 0; }
運行結果如下:
總結
以上是生活随笔為你收集整理的【Linux系统编程】进程间通信--消息队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】进程间通信--有名
- 下一篇: 【Linux系统编程】进程间通信--共享