消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。
每個數據塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。
我們可以通過發送消息來避免命名管道的同步和阻塞問題。
Linux用宏MSGMAX和MSGMNB來限制一條消息的最大長度和一個隊列的最大長度。
◆影響消息隊列的系統限制
MSGMAX 可發送是最長消息的長度 2048
MSGMNB 特定隊列的最大字節長度(亦即隊列中所有消息之和) 4096
MSGMNI 系統中最大消息列數 50
MSGTOL 系統中最大消息數 50
二 消息隊列的實現主要從如下幾個函數實現(就四個動作,創建、發送、接收、刪除)
◆創建和訪問消息隊列原型:
intmsgget(key_t,key,intmsgflg);
◇程序必須提供一個鍵來命名這個消息隊列。msgflg是一個權限標志,表示消息隊列的訪問權限,它與文件的訪問權限一樣。
◇msgflg可以與ipC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,而只返回一個標識符。
◇函數返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1.
◆把消息添加到消息隊列原型:
intmsgsend(intmsgid,constvoid*msg_ptr,size_tmsg_sz,intmsgflg);
◇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.
◆從一個消息隊列獲取消息.原型:
intmsgrcv(intmsgid,void*msg_ptr,size_tmsg_st,longintmsgtype,intmsgflg);
◇msgid,msg_ptr,msg_st的作用也函數msgsnd函數的一樣。
◇msgtype可以實現一種簡單的接收優先級。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大于零,將獲取具有相同消息類型的第一個信息。如果它小于零,就獲取類型等于或小于msgtype的絕對值的第一個消息。
◇msgflg用于控制當隊列中沒有相應類型的消息可以接收時將發生的事情。
◇調用成功時,該函數返回放到接收緩存區中的字節數,消息被復制到由msg_ptr指向的用戶分配的緩存區中,然后刪除消息隊列中的對應消息。失敗時返回-1。
◆控制消息隊列。原型:
intmsgctl(intmsgid,intcommand,structmsgid_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;};三 例子#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/msg.h> struct msg_st{ long int msg_type; char text[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 failed with error: %dn", errno); exit(EXIT_FAILURE); } //從隊列中獲取消息,直到遇到end消息為止 while(running) { if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1) { fprintf(stderr, "msgrcv failed with errno: %dn", errno); exit(EXIT_FAILURE); } printf("You wrote: %sn",data.text); //遇到end結束 if(strncmp(data.text, "end", 3) == 0) running = 0; } //刪除消息隊列 if(msgctl(msgid, IPC_RMID, 0) == -1) { fprintf(stderr, "msgctl(IPC_RMID) failedn"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS);} 發送信息的程序的源文件msgsend.c的源代碼為:
#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/msg.h>#include <errno.h> #define MAX_TEXT 512struct msg_st{ long int msg_type; char text[MAX_TEXT];}; int main(){ int running = 1; struct msg_st data; char buffer[BUFSIZ]; int msgid = -1; //建立消息隊列 msgid = msgget((key_t)1234, 0666 | IPC_CREAT); if(msgid == -1) { fprintf(stderr, "msgget failed with error: %dn", errno); exit(EXIT_FAILURE); } //向消息隊列中寫消息,直到寫入end while(running) { //輸入數據 printf("Enter some text: "); fgets(buffer, BUFSIZ, stdin); data.msg_type = 1; //注意2 strcpy(data.text, buffer); //向隊列發送數據 if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1) { fprintf(stderr, "msgsnd failedn"); exit(EXIT_FAILURE); } //輸入end結束輸入 if(strncmp(buffer, "end", 3) == 0) running = 0; sleep(1); } exit(EXIT_SUCCESS);}四 消息類型這里主要說明一下消息類型是怎么一回事,注意msgreceive.c文件main函數中定義的變量msgtype(注釋為注意1),它作為msgrcv函數的接收信息類型參數的值,其值為0,表示獲取隊列中第一個可用的消息。再來看看msgsend.c文件中while循環中的語句data.msg_type=1(注釋為注意2),它用來設置發送的信息的信息類型,即其發送的信息的類型為1。所以程序msgreceive能夠接收到程序msgsend發送的信息。
如果把注意1,即msgreceive.c文件main函數中的語句由longintmsgtype=0;改變為longintmsgtype=2;會發生什么情況,msgreceive將不能接收到程序msgsend發送的信息。因為在調用msgrcv函數時,如果msgtype(第四個參數)大于零,則將只獲取具有相同消息類型的第一個消息,修改后獲取的消息類型為2,而msgsend發送的消息類型為1,所以不能被msgreceive程序接收。重新編譯msgreceive.c文件并再次執行.
五 消息隊列與命名管道的比較消息隊列跟命名管道有不少的相同之處,通過與命名管道一樣,消息隊列進行通信的進程可以是不相關的進程,同時它們都是通過發送和接收的方式來傳遞數據的。在命名管道中,發送數據用write,接收數據用read,則在消息隊列中,發送數據用msgsnd,接收數據用msgrcv。而且它們對每個數據都有一個最大長度的限制。
與命名管道相比,消息隊列的優勢在于
1、消息隊列也可以獨立于發送和接收進程而存在,從而消除了在同步命名管道的打開和關閉時可能產生的困難。
2、同時通過發送消息還可以避免命名管道的同步和阻塞問題,不需要由進程自己來提供同步方法。
3、接收程序可以通過消息類型有選擇地接收數據,而不是像命名管道中那樣,只能默認地接收。
六 可能存在的隱患(1) 當64位應用向32位應用發送一消息時,如果它在8字節字段中設置的值大于32位應用中4字節類型字段可表示值,那么32位應用在其mtype字段中得到的是一個截短了的值,于是也就丟失了信息。新聞熱點
疑難解答