歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux進程間通信之消息隊列

本文依據以下思路展開,首先從宏觀上闡述消息隊列的機制,然後以具體代碼為例進一步闡述該機制,最後試著暢想一下該通信機制潛在的應用。

消息隊列是在兩個不相關進程間傳遞數據的一種簡單、高效方式,她獨立於發送進程、接受進程而存在。

 

圖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程序設計 第四版》

《Linux程序設計》第四版中文版,高清PDF,很早時就發過,具體下載見http://www.linuxidc.com/Linux/2011-04/34148.htm

Copyright © Linux教程網 All Rights Reserved