消息隊(duì)列介紹

消息隊(duì)列

消息隊(duì)列提供了一種從一個(gè)進(jìn)程向另一個(gè)進(jìn)程發(fā)送一個(gè)數(shù)據(jù)塊的方法觉啊。 每個(gè)數(shù)據(jù)塊都被認(rèn)為含有一個(gè)類型折联,接收進(jìn)程可以獨(dú)立地接收含有不同類型的數(shù)據(jù)結(jié)構(gòu)急灭。我們可以通過(guò)發(fā)送消息來(lái)避免命名管道的同步和阻塞問(wèn)題。但是消息隊(duì)列與命名管道一樣,每個(gè)數(shù)據(jù)塊都有一個(gè)最大長(zhǎng)度的限制钠糊。
Linux用宏MSGMAX和MSGMNB來(lái)限制一條消息的最大長(zhǎng)度和一個(gè)隊(duì)列的最大長(zhǎng)度。

在Linux中使用消息隊(duì)列

1壹哺、msgget函數(shù)
該函數(shù)用來(lái)創(chuàng)建和訪問(wèn)一個(gè)消息隊(duì)列抄伍。它的原型為:

int msgget(key_t, key, int msgflg);  

與其他的IPC機(jī)制一樣,程序必須提供一個(gè)鍵來(lái)命名某個(gè)特定的消息隊(duì)列斗躏。msgflg是一個(gè)權(quán)限標(biāo)志逝慧,表示消息隊(duì)列的訪問(wèn)權(quán)限,它與文件的訪問(wèn)權(quán)限一樣啄糙。msgflg可以與IPC_CREAT做或操作笛臣,表示當(dāng)key所命名的消息隊(duì)列不存在時(shí)創(chuàng)建一個(gè)消息隊(duì)列,如果key所命名的消息隊(duì)列存在時(shí)隧饼,IPC_CREAT標(biāo)志會(huì)被忽略沈堡,而只返回一個(gè)標(biāo)識(shí)符。
它返回一個(gè)以key命名的消息隊(duì)列的標(biāo)識(shí)符(非零整數(shù))燕雁,失敗時(shí)返回-1.

2诞丽、msgsnd函數(shù)
該函數(shù)用來(lái)把消息添加到消息隊(duì)列中鲸拥。它的原型為:

int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);  

msgid是由msgget函數(shù)返回的消息隊(duì)列標(biāo)識(shí)符。
msg_ptr是一個(gè)指向準(zhǔn)備發(fā)送消息的指針僧免,但是消息的數(shù)據(jù)結(jié)構(gòu)卻有一定的要求刑赶,指針msg_ptr所指向的消息結(jié)構(gòu)一定要是以一個(gè)長(zhǎng)整型成員變量開(kāi)始的結(jié)構(gòu)體,接收函數(shù)將用這個(gè)成員來(lái)確定消息的類型懂衩。所以消息結(jié)構(gòu)要定義成這樣:

struct my_message{  
    long int message_type;  
    /* The data you wish to transfer*/  
};  

msg_sz是msg_ptr指向的消息的長(zhǎng)度撞叨,注意是消息的長(zhǎng)度,而不是整個(gè)結(jié)構(gòu)體的長(zhǎng)度浊洞,也就是說(shuō)msg_sz是不包括長(zhǎng)整型消息類型成員變量的長(zhǎng)度牵敷。
msgflg用于控制當(dāng)前消息隊(duì)列滿或隊(duì)列消息到達(dá)系統(tǒng)范圍的限制時(shí)將要發(fā)生的事情

如果調(diào)用成功法希,消息數(shù)據(jù)的一分副本將被放到消息隊(duì)列中枷餐,并返回0,失敗時(shí)返回-1.

3苫亦、msgrcv函數(shù)
該函數(shù)用來(lái)從一個(gè)消息隊(duì)列獲取消息毛肋,它的原型為

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

msgid, msg_ptr, msg_st的作用也函數(shù)msgsnd函數(shù)的一樣。
msgtype可以實(shí)現(xiàn)一種簡(jiǎn)單的接收優(yōu)先級(jí)著觉。如果msgtype為0村生,就獲取隊(duì)列中的第一個(gè)消息。如果它的值大于零饼丘,將獲取具有相同消息類型的第一個(gè)信息。如果它小于零辽话,就獲取類型等于或小于msgtype的絕對(duì)值的第一個(gè)消息肄鸽。
msgflg用于控制當(dāng)隊(duì)列中沒(méi)有相應(yīng)類型的消息可以接收時(shí)將發(fā)生的事情
調(diào)用成功時(shí)油啤,該函數(shù)返回放到接收緩存區(qū)中的字節(jié)數(shù)典徘,消息被復(fù)制到由msg_ptr指向的用戶分配的緩存區(qū)中,然后刪除消息隊(duì)列中的對(duì)應(yīng)消息益咬。失敗時(shí)返回-1.

4逮诲、msgctl函數(shù)
該函數(shù)用來(lái)控制消息隊(duì)列,它與共享內(nèi)存的shmctl函數(shù)相似幽告,它的原型為:

int msgctl(int msgid, int command, struct msgid_ds *buf);  

command是將要采取的動(dòng)作梅鹦,它可以取3個(gè)值,
IPC_STAT:把msgid_ds結(jié)構(gòu)中的數(shù)據(jù)設(shè)置為消息隊(duì)列的當(dāng)前關(guān)聯(lián)值冗锁,即用消息隊(duì)列的當(dāng)前關(guān)聯(lián)值覆蓋msgid_ds的值齐唆。
IPC_SET:如果進(jìn)程有足夠的權(quán)限,就把消息列隊(duì)的當(dāng)前關(guān)聯(lián)值設(shè)置為msgid_ds結(jié)構(gòu)中給出的值
IPC_RMID:刪除消息隊(duì)列

buf是指向msgid_ds結(jié)構(gòu)的指針冻河,它指向消息隊(duì)列模式和訪問(wèn)權(quán)限的結(jié)構(gòu)箍邮。msgid_ds結(jié)構(gòu)至少包括以下成員:

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

成功時(shí)返回0茉帅,失敗時(shí)返回-1.

三、使用消息隊(duì)列進(jìn)行進(jìn)程間通信
馬不停蹄锭弊,介紹完消息隊(duì)列的定義和可使用的接口之后堪澎,我們來(lái)看看它是怎么讓進(jìn)程進(jìn)行通信的。由于可以讓不相關(guān)的進(jìn)程進(jìn)行行通信味滞,所以我們?cè)谶@里將會(huì)編寫(xiě)兩個(gè)程序全封,msgreceive和msgsned來(lái)表示接收和發(fā)送信息。根據(jù)正常的情況桃犬,我們?cè)试S兩個(gè)程序都可以創(chuàng)建消息刹悴,但只有接收者在接收完最后一個(gè)消息之后,它才把它刪除攒暇。

接收信息的程序源文件為msgreceive.c的源代碼為:

#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  
  
    //建立消息隊(duì)列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  
    {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
    //從隊(duì)列中獲取消息土匀,直到遇到end消息為止  
    while(running)  
    {  
        if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)  
        {  
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);  
            exit(EXIT_FAILURE);  
        }  
        printf("You wrote: %s\n",data.text);  
        //遇到end結(jié)束  
        if(strncmp(data.text, "end", 3) == 0)  
            running = 0;  
    }  
    //刪除消息隊(duì)列  
    if(msgctl(msgid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}  

發(fā)送信息的程序的源文件msgsend.c的源代碼為:

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/msg.h>  
#include <errno.h>  
  
#define MAX_TEXT 512  
struct 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;  
  
    //建立消息隊(duì)列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  
    {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
  
    //向消息隊(duì)列中寫(xiě)消息,直到寫(xiě)入end  
    while(running)  
    {  
        //輸入數(shù)據(jù)  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        data.msg_type = 1;    //注意2  
        strcpy(data.text, buffer);  
        //向隊(duì)列發(fā)送數(shù)據(jù)  
        if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)  
        {  
            fprintf(stderr, "msgsnd failed\n");  
            exit(EXIT_FAILURE);  
        }  
        //輸入end結(jié)束輸入  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
        sleep(1);  
    }  
    exit(EXIT_SUCCESS);  
}  

運(yùn)行結(jié)果如下:

例子分析——消息類型

這里主要說(shuō)明一下消息類型是怎么一回事形用,注意msgreceive.c文件main函數(shù)中定義的變量msgtype(注釋為注意1)就轧,它作為msgrcv函數(shù)的接收信息類型參數(shù)的值,其值為0田度,表示獲取隊(duì)列中第一個(gè)可用的消息妒御。再來(lái)看看msgsend.c文件中while循環(huán)中的語(yǔ)句data.msg_type = 1(注釋為注意2),它用來(lái)設(shè)置發(fā)送的信息的信息類型镇饺,即其發(fā)送的信息的類型為1乎莉。所以程序msgreceive能夠接收到程序msgsend發(fā)送的信息。

如果把注意1奸笤,即msgreceive.c文件main函數(shù)中的語(yǔ)句由long int msgtype = 0;改變?yōu)閘ong int msgtype = 2;會(huì)發(fā)生什么情況惋啃,msgreceive將不能接收到程序msgsend發(fā)送的信息。因?yàn)樵谡{(diào)用msgrcv函數(shù)時(shí)监右,如果msgtype(第四個(gè)參數(shù))大于零边灭,則將只獲取具有相同消息類型的第一個(gè)消息,修改后獲取的消息類型為2健盒,而msgsend發(fā)送的消息類型為1绒瘦,所以不能被msgreceive程序接收。重新編譯msgreceive.c文件并再次執(zhí)行扣癣,其結(jié)果如下:


![](file:///C:/Users/Administrator/AppData/Local/youdao/ynote/images/A1A9EF1F521A463D80835BF457097564/QQ%E6%88%AA%E5%9B%BE20130824162817.jpg)

我們可以看到惰帽,msgreceive并沒(méi)有接收到信息和輸出,而且當(dāng)msgsend輸入end結(jié)束后搏色,msgreceive也沒(méi)有結(jié)束善茎,通過(guò)jobs命令我們可以看到它還在后臺(tái)運(yùn)行著。

五频轿、消息隊(duì)列與命名管道的比較

消息隊(duì)列跟命名管道有不少的相同之處垂涯,通過(guò)與命名管道一樣烁焙,消息隊(duì)列進(jìn)行通信的進(jìn)程可以是不相關(guān)的進(jìn)程,同時(shí)它們都是通過(guò)發(fā)送和接收的方式來(lái)傳遞數(shù)據(jù)的耕赘。在命名管道中骄蝇,發(fā)送數(shù)據(jù)用write,接收數(shù)據(jù)用read操骡,則在消息隊(duì)列中九火,發(fā)送數(shù)據(jù)用msgsnd,接收數(shù)據(jù)用msgrcv册招。而且它們對(duì)每個(gè)數(shù)據(jù)都有一個(gè)最大長(zhǎng)度的限制岔激。

與命名管道相比,消息隊(duì)列的優(yōu)勢(shì)在于是掰,
1虑鼎、消息隊(duì)列也可以獨(dú)立于發(fā)送和接收進(jìn)程而存在,從而消除了在同步命名管道的打開(kāi)和關(guān)閉時(shí)可能產(chǎn)生的困難键痛。
2炫彩、同時(shí)通過(guò)發(fā)送消息還可以避免命名管道的同步和阻塞問(wèn)題,不需要由進(jìn)程自己來(lái)提供同步方法絮短。
3江兢、接收程序可以通過(guò)消息類型有選擇地接收數(shù)據(jù),而不是像命名管道中那樣丁频,只能默認(rèn)地接收杉允。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市限府,隨后出現(xiàn)的幾起案子夺颤,更是在濱河造成了極大的恐慌,老刑警劉巖胁勺,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異独旷,居然都是意外死亡署穗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)嵌洼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)案疲,“玉大人,你說(shuō)我怎么就攤上這事麻养『址龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵鳖昌,是天一觀的道長(zhǎng)备畦。 經(jīng)常有香客問(wèn)我低飒,道長(zhǎng),這世上最難降的妖魔是什么懂盐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任褥赊,我火速辦了婚禮,結(jié)果婚禮上莉恼,老公的妹妹穿的比我還像新娘拌喉。我一直安慰自己,他們只是感情好俐银,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布尿背。 她就那樣靜靜地躺著,像睡著了一般捶惜。 火紅的嫁衣襯著肌膚如雪田藐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天售躁,我揣著相機(jī)與錄音坞淮,去河邊找鬼。 笑死陪捷,一個(gè)胖子當(dāng)著我的面吹牛回窘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播市袖,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼啡直,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了苍碟?” 一聲冷哼從身側(cè)響起酒觅,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎微峰,沒(méi)想到半個(gè)月后舷丹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜓肆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年颜凯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仗扬。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡症概,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出早芭,到底是詐尸還是另有隱情彼城,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站募壕,受9級(jí)特大地震影響调炬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜司抱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一筐眷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧习柠,春花似錦匀谣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至溶锭,卻和暖如春宝恶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趴捅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工垫毙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拱绑。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓综芥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親猎拨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子膀藐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容