下面來說說如何用不用訊息佇列來進行進程間的通信,訊息佇列與命名管道有許多相似之處。有關命名管道的更多內容可以參閱我的另一篇文章:Linux進程間通訊-使用命名管道
一、什麼是訊息佇列
訊息隊列提供了一種從一個進程向另一個進程發送一個資料塊的方法。 每個資料塊都被認為含有一個類型,接收程序可以獨立地接收含有不同類型的資料結構。我們可以透過發送訊息來避免命名管道的同步和阻塞問題。但是訊息佇列與命名管道一樣,每個資料塊都有一個最大長度的限制。
Linux用巨集MSGMAX和MSGMNB來限制一則訊息的最大長度和一個佇列的最大長度。
二、在Linux中使用訊息佇列
Linux提供了一系列訊息佇列的函數介面來讓我們方便地使用它來實現進程間的通訊。它的用法與其他兩個System V PIC機制,即信號量和共享記憶體相似。
1、msgget函數
此函數用來建立和存取一個訊息佇列。它的原型是:
#int msgget(key_t, key, int msgflg);
#與其他的IPC機制一樣,程式必須提供一個鍵來命名某個特定的訊息佇列。 msgflg是一個權限標誌,表示訊息佇列的存取權限,它與檔案的存取權限一樣。 msgflg可以與IPC_CREAT做或操作,表示當key所命名的訊息佇列不存在時建立一個訊息佇列,如果key所命名的訊息佇列存在時,IPC_CREAT標誌會被忽略,而只傳回一個識別碼。
它傳回以key命名的訊息佇列的識別碼(非零整數),失敗時傳回-1.
2、 msgsnd函數
該函數用來把訊息加入到訊息佇列中。它的原型是:
#int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, # msgfN);#
msgid是由msgget函數傳回的訊息佇列標識符。
msg_ptr是一個指向準備發送訊息的指針,但是訊息的資料結構卻有一定的要求,指針msg_ptr所指向的訊息結構一定要是以一個長整數成員變數開始的結構體,接收函數將用這個成員來決定訊息的類型。所以訊息結構要定義成這樣:
#[cpp]
struct my_message{ -
long - #int message_type;
l
##n#ne之一(
#
/* The data you wish to transfer*/
};
######如果呼叫成功,訊息資料的一分副本將被放到訊息佇列中,並傳回0,失敗時回傳-1.###### #### ##3、msgrcv函數######此函數用來從一個訊息佇列取得訊息,它的原型為#################[cpp]## # view plain copy### ### print?######
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, #msg_st, long ##》 ;
msgid, msg_ptr, msg_st的作用也函數msgsnd函數的一樣。
msgtype可以實作一個簡單的接收優先權。如果msgtype為0,就取得佇列中的第一個訊息。如果它的值大於零,將取得具有相同訊息類型的第一個資訊。如果它小於零,就取得類型等於或小於msgtype的絕對值的第一個訊息。
msgflg用於控制當佇列中沒有對應類型的訊息可以接收時將發生的事情。
呼叫成功時,函數會傳回放到接收快取區中的位元組數,訊息被複製到由msg_ptr指向的使用者指派的快取區中,然後刪除訊息佇列中的對應訊息。失敗時回傳-1.
4、msgctl函數
此函數用來控制訊息佇列,它與共享記憶體的shmctl函數相似,它的原型為:
##int msgctl(
int msgid,
int command,
struct msgid_ds *buf);
###############################################' #######command是將要採取的動作,它可以取3個值,###### IPC_STAT:把msgid_ds結構中的資料設定為訊息佇列的目前關聯值,也就是用訊息佇列的目前關聯值覆寫msgid_ds的值。 ###### IPC_SET:如果行程有足夠的權限,就把訊息列隊的目前關聯值設為msgid_ds結構中給的值###### IPC_RMID:刪除訊息佇列####### # #####buf是指向msgid_ds結構的指針,它指向訊息佇列模式和存取權限的結構。 msgid_ds結構至少包含以下成員:######### ################[cpp]### view plain copy### ### print?## ################struct msgid_ds ###############{ ############ uid_t shm_perm.uid#### uid_t shm_perm.uid##### uid_t shm_perm.uid ; ############ uid_t shm_perm.gid; ############ mode_t # #################成功時回傳0,失敗時回傳-1.###### ######三、使用訊息佇列進行進程間通訊# #####馬不停蹄,介紹完訊息佇列的定義和可使用的介面之後,我們來看看它是怎麼讓進程進行通訊的。由於可以讓不相關的進程進行行通信,所以我們在這裡將會編寫兩個程序,msgreceive和msgsned來表示接收和發送訊息。根據正常的情況,我們允許兩個程式都可以建立訊息,但只有接收者在接收最後一個訊息之後,它才會刪除它。 ###### ######接收訊息的程式原始檔為msgreceive.c的原始碼為:######### ############## #[cpp]### view plain copy### ### print?######
#include
include
include
include ;
include ;
include
struct msg_st
- struct msg_st
- # {
long - #int msg_type;
#char 文本[BUFSIZ];
};
-
##int main()
#{ ## int running = 1;
-
##
int msgid = -1;
struct msg_st data;
-
##
long int msgtype = 0; - //注意1
# #//建立訊息佇列
-
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
-
if(msgid == -1) -
# {
# {
- ##> # # fprintf(stderr,
"msgget 失敗,錯誤:%d\n", errno);
-
退出(EXIT_FAILURE);
}
-
#
//從佇列中擷取訊息,直到遇到結束訊息狀況 ##//從佇列中取得訊息,直到遇到結束訊息狀況
- ##
while(運行)
-
# {
of ) , (void*)&data, BUFSIZ, msgtype, 0) == -1)
#
# # fprintf(stderr, "msgrcv 失敗,錯誤編號:%d\n", errno);
- 退出(EXIT_FAILURE);
-
- printf(
"您寫的是:%s\n",data.text);
## - //遇到結束
# ##"結束", 3) == 0)
運轉 = 0;
} # - //刪除訊息佇列
#
-
if(msgctl(msgid, IPC_RMID, 0) == -1)
# {
stderr,
"msgctl(IPC_RMID) 失敗\n"); ############### 退出(EXIT_FAILURE); ############ } ############# 退出(EXIT_SUCCESS); ############} ###################傳送訊息的程式的原始檔msgsend.c的原始碼為:#### #### ####
### 睡覺(1); ############ } ############# 退出(EXIT_SUCCESS); ############} ################### 執行結果如下:###
四、範例分析-訊息類型
#這裡主要說明一下訊息類型是怎麼一回事,注意msgreceive.c檔案main函數中定義的變數msgtype(註解為注意1),它作為msgrcv函數的接收訊息類型參數的值,其值為0 ,表示取得佇列中第一個可用的訊息。再來看看msgsend.c檔案中while循環中的語句data.msg_type = 1(註解為注意2),它用來設定發送的訊息的訊息類型,即其發送的訊息的類型為1。所以程式msgreceive能夠接收到程式msgsend發送的訊息。
如果把注意1,即msgreceive.c檔案main函數中的語句由long int msgtype = 0;改為long int msgtype = 2;會發生什麼情況,msgreceive將不能接收到程式msgsend發送的訊息。因為在呼叫msgrcv函數時,如果msgtype(第四個參數)大於零,則將只取得具有相同訊息類型的第一個訊息,修改後取得的訊息類型為2,而msgsend發送的訊息類型為1,所以不能被msgreceive程式接收。重新編譯msgreceive.c檔案並再次執行,其結果如下:
我們可以看到,msgreceive並沒有接收到訊息和輸出,而且當msgsend輸入end結束後,msgreceive也沒有結束,透過jobs命令我們可以看到它還在後台運行著。
五、訊息佇列與命名管道的比較
訊息佇列跟命名管道有不少的相同之處,透過與命名管道一樣,訊息佇列進行通訊的進程可以是不相關的進程,同時它們都是透過發送和接收的方式來傳遞資料的。在命名管道中,傳送資料用write,接收資料用read,則在訊息佇列中,傳送資料用msgsnd,接收資料用msgrcv。而且它們對每個資料都有一個最大長度的限制。
與命名管道相比,訊息佇列的優點在於,1、訊息佇列也可以獨立於傳送和接收進程而存在,從而消除了在同步命名管道的開啟和關閉時可能產生的困難。 2.同時透過傳送訊息還可以避免命名管道的同步和阻塞問題,不需要由進程本身來提供同步方法。 3.接收程序可以透過訊息類型選擇性地接收數據,而不是像命名管道中那樣,只能預設接收。
以上是什麼是訊息隊列?在Linux中使用訊息佇列的詳細內容。更多資訊請關注PHP中文網其他相關文章!