本文首先從巨集觀的角度對進程間的通信方式之一,消息隊列進行闡述,然後以代碼實例對消息隊列進行更近一步的闡述,最後試著暢想消息隊列的潛在應用 ...
本文依據以下思路展開,首先從巨集觀上闡述消息隊列的機制,然後以具體代碼為例進一步闡述該機制,最後試著暢想一下該通信機制潛在的應用。
消息隊列是在兩個不相關進程間傳遞數據的一種簡單、高效方式,她獨立於發送進程、接受進程而存在。
圖1 消息隊列通信機制示意圖
首先從巨集觀的角度瞭解一下消息隊列的工作機制。因為消息隊列獨立於進程而存在,為了區別不同的消息隊列,需要以key值標記消息隊列,這樣兩個不相關進程可以通過事先約定的key值通過消息隊列進行消息收發。例如進程A向key消息隊列發送消息,進程B從Key消息隊列讀取消息。在這一過程中主要涉及到四個函數:
#include <sys/msg.h> # 消息隊列相關函數及數據結構頭文件 int msgctl(int msqid, int cmd, struct msqid_ds *buf);# 控制消息隊列函數 int msgget(key_t key, int msgflg); # 創建消息隊列,key值唯一標識該消息隊列 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);# 發送消息
下麵結合代碼實例,對上述過程進行分析(具體分析見代碼註釋)
# msg1.c 接收端
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> # 包含消息隊列相關函數及數據結構的頭文件 struct my_msg_st { long int my_msg_type; char some_text[BUFSIZ]; };# 消息格式 int main() { int running = 1; int msgid; struct my_msg_st some_data; long int msg_to_receive = 0; msgid = msgget((key_t)1234, 0666 | IPC_CREAT);# 創建標識符為key = 1234 的消息隊列,註意發送端與接收端該值的一致性 if (msgid == -1) { fprintf(stderr, “msgget failed with error: %d\n”, errno); exit(EXIT_FAILURE); }# 錯誤處理:msgget調用成功返回消息隊列標識符,調用失敗返回-1 while(running) { if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1) { # 從消息隊列接收消息,如果接收失敗執行if語句並退出 fprintf(stderr, “msgrcv failed with error: %d\n”, errno); exit(EXIT_FAILURE); } printf(“You wrote: %s”, some_data.some_text); if (strncmp(some_data.some_text, “end”, 3) == 0) { # 如果接收到文本含有“end”,將running設置為0,效果是:退出while迴圈 running = 0; } } if (msgctl(msgid, IPC_RMID, 0) == -1) { # 刪除消息隊列,如果刪除失敗執行if語句並退出 fprintf(stderr, “msgctl(IPC_RMID) failed\n”); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
# msg2.c 發送端
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> #define MAX_TEXT 512 struct my_msg_st { long int my_msg_type; char some_text[MAX_TEXT]; };# 消息格式,與接收端一致 int main() { int running = 1; struct my_msg_st some_data; int msgid; char buffer[BUFSIZ]; msgid = msgget((key_t)1234, 0666 | IPC_CREAT);# 創建消息標識符key = 1234的消息隊列。如果該隊列已經存在,則直接返回該隊列的標識符,以便向該消息隊列收發消息 if (msgid == -1) { fprintf(stderr, “msgget failed with error: %d\n”, errno); exit(EXIT_FAILURE); }# 錯誤處理,同接收者msg1 while(running) { printf(“Enter some text: “); fgets(buffer, BUFSIZ, stdin);# 由控制台輸入文本,並將其存放在buffer之中 some_data.my_msg_type = 1;# 類型填充,在本例中沒有特別含義 strcpy(some_data.some_text, buffer);# 將buffer數據複製到some_text之中 if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { # 向消息隊列發送消息,如果發送失敗執行if語句並退出 fprintf(stderr, “msgsnd failed\n”); exit(EXIT_FAILURE); } if (strncmp(buffer, “end”, 3) == 0) {# 如果發送的“end”,則在發送“end”之後,退出while,結束程式 running = 0; } } exit(EXIT_SUCCESS); }
以下是在控制台模擬的結果:
$ ./msg2
Enter some text: hello
Enter some text: How are you today?
Enter some text: end
$ ./msg1
You wrote: hello
You wrote: How are you today?
You wrote: end
$
消息隊列潛在應用
圖2 消息隊列在守護進程中的應用
如圖2所示,假如有三個圖形界面程式,他們分別對應進程1、進程2、進程3。這三個應用程式都需要滑鼠、鍵盤操作,如果在每個進程都加入捕獲滑鼠、鍵盤操作的代碼,那麼一共需要三份這樣的代碼,有點浪費資源(記憶體空間)。如果我們將捕獲滑鼠、鍵盤操作的代碼獨立出來做成一個單獨的進程,該進程向特定的消息隊列發送捕獲的滑鼠、鍵盤操作,當前激活圖像程式可以從該消息隊列中提取相應的滑鼠、鍵盤操作,然後據此執行後續的指令。以這種方式,能夠將不同應用程式中,共性的部分提取出來,從而簡化應用程式的設計和設計更加優化的共性處理程式。
消息隊列潛在應用之升華
處理程式共性部分的一些方法:
庫:通用的一些功能實現為庫函數,對外提供定義良好的藉口;應用程式在應用這些功能的時候,只需調用相應介面即可。
守護進程:將應用程式的共性部分提出出來實現為守護進程,通過某種通信機制實現守護進程與應用程式的信息交互。
參考資料:《Linux程式設計 第四版》