1. 程式人生 > >linux c之通過訊息佇列實現程序通訊

linux c之通過訊息佇列實現程序通訊

1、訊息佇列的介紹

訊息佇列提供了一種從一個程序向另一個程序傳送一個數據塊的方法。  每個資料塊都被認為含有一個型別,接收程序可以獨立地接收含有不同型別的資料結構。我們可以通過傳送訊息來避免命名管道的同步和阻塞問題。但是訊息佇列與命名管道一樣,每個資料塊都有一個最大長度的限制,Linux用巨集MSGMAX和MSGMNB來限制一條訊息的最大長度和一個佇列的最大長度。

2、訊息佇列需要使用的API介紹

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, int msgflg);  

msgid是由msgget函式返回的訊息佇列識別符號。

msg_ptr是一個指向準備傳送訊息的指標,但是訊息的資料結構卻有一定的要求,指標msg_ptr所指向的訊息結構一定要是以一個長整型成員變數開始的結構體,接收函式將用這個成員來確定訊息的型別。所以訊息結構要定義成這樣:
    struct my_message{  
        long int message_type;  
        /* The data you wish to transfer*/  
    };  

msg_sz是msg_ptr指向的訊息的長度,注意是訊息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型訊息型別成員變數的長度。

msgflg用於控制當前訊息佇列滿或佇列訊息到達系統範圍的限制時將要發生的事情。

如果呼叫成功,訊息資料的一分副本將被放到訊息佇列中,並返回0,失敗時返回-1.

3、msgrcv函式

該函式用來從一個訊息佇列獲取訊息,它的原型為

    int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
 

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結構至少包括以下成員:

    struct msgid_ds  
    {  
        uid_t shm_perm.uid;  
        uid_t shm_perm.gid;  
        mode_t shm_perm.mode;  
    };  

成功時返回0,失敗時返回-1.



3、使用訊息佇列進行程序間通訊

實現msgsend.c檔案向msgrcv.c通過訊息佇列進行寫檔案

1、msgsend.c檔案圖片如下

2、msgrcv.c檔案如下

4、結果分析

我們先執行msgsend.c檔案,然後再執行msgrcv.c檔案,結果如下


好了,那我就從msgsend這篇傳送資料,結果如下圖


msgreceive.c檔案main函式中的語句由long int msgtype = 0;改變為long int msgtype = 2;會發生什麼情況,msgreceive將不能接收到程式msgsend傳送的資訊。因為在呼叫msgrcv函式時,如果msgtype(第四個引數)大於零,則將只獲取具有相同訊息型別的第一個訊息,修改後獲取的訊息型別為2,而msgsend傳送的訊息型別為1,所以不能被msgreceive程式接收,如果把msgreceive.c檔案main函式中的語句由long int msgtype = 0改為long int msgtype = 2,把msgsend傳送的訊息型別改為2,這樣也是沒問題的,一直通訊

總結:傳送端型別為1,接收端可以為0,正常通訊
          傳送端型別為2,接收端可以為2,正常通訊

5、和命名管道對比分析


訊息佇列跟命名管道有不少的相同之處,通過與命名管道一樣,訊息佇列進行通訊的程序可以是不相關的程序,同時它們都是通過傳送和接收的方式來傳遞資料的。在命名管道中,傳送資料用write,接收資料用read,則在訊息佇列中,傳送資料用msgsnd,接收資料用msgrcv。而且它們對每個資料都有一個最大長度的限制。

與命名管道相比,訊息佇列的優勢在於,1、訊息佇列也可以獨立於傳送和接收程序而存在,從而消除了在同步命名管道的開啟和關閉時可能產生的困難。2、同時通過傳送訊息還可以避免命名管道的同步和阻塞問題,不需要由程序自己來提供同步方法。3、接收程式可以通過訊息型別有選擇地接收資料,而不是像命名管道中那樣,只能預設地接收。