溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux進程間通信中如何使用消息隊列

發(fā)布時間:2021-09-27 16:01:36 來源:億速云 閱讀:149 作者:柒染 欄目:系統(tǒng)運維

本篇文章給大家分享的是有關Linux進程間通信中如何使用消息隊列,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

一、什么是消息隊列

消息隊列提供了一種從一個進程向另一個進程發(fā)送一個數(shù)據(jù)塊的方法。  每個數(shù)據(jù)塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數(shù)據(jù)結(jié)構(gòu)。我們可以通過發(fā)送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數(shù)據(jù)塊都有一個***長度的限制。

Linux用宏MSGMAX和MSGMNB來限制一條消息的***長度和一個隊列的***長度。

二、在Linux中使用消息隊列

Linux提供了一系列消息隊列的函數(shù)接口來讓我們方便地使用它來實現(xiàn)進程間的通信。它的用法與其他兩個System V  PIC機制,即信號量和共享內(nèi)存相似。

1、msgget函數(shù)

該函數(shù)用來創(chuàng)建和訪問一個消息隊列。它的原型為:

int msgget(key_t, key, int msgflg);

與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個權(quán)限標志,表示消息隊列的訪問權(quán)限,它與文件的訪問權(quán)限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創(chuàng)建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,而只返回一個標識符。

它返回一個以key命名的消息隊列的標識符(非零整數(shù)),失敗時返回-1.

2、msgsnd函數(shù)

該函數(shù)用來把消息添加到消息隊列中。它的原型為:

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

msgid是由msgget函數(shù)返回的消息隊列標識符。

msg_ptr是一個指向準備發(fā)送消息的指針,但是消息的數(shù)據(jù)結(jié)構(gòu)卻有一定的要求,指針msg_ptr所指向的消息結(jié)構(gòu)一定要是以一個長整型成員變量開始的結(jié)構(gòu)體,接收函數(shù)將用這個成員來確定消息的類型。所以消息結(jié)構(gòu)要定義成這樣:

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

msg_sz是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結(jié)構(gòu)體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度。

msgflg用于控制當前消息隊列滿或隊列消息到達系統(tǒng)范圍的限制時將要發(fā)生的事情。

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

3、msgrcv函數(shù)

該函數(shù)用來從一個消息隊列獲取消息,它的原型為

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可以實現(xiàn)一種簡單的接收優(yōu)先級。如果msgtype為0,就獲取隊列中的***個消息。如果它的值大于零,將獲取具有相同消息類型的***個信息。如果它小于零,就獲取類型等于或小于msgtype的絕對值的***個消息。

msgflg用于控制當隊列中沒有相應類型的消息可以接收時將發(fā)生的事情。

調(diào)用成功時,該函數(shù)返回放到接收緩存區(qū)中的字節(jié)數(shù),消息被復制到由msg_ptr指向的用戶分配的緩存區(qū)中,然后刪除消息隊列中的對應消息。失敗時返回-1.

4、msgctl函數(shù)

該函數(shù)用來控制消息隊列,它與共享內(nèi)存的shmctl函數(shù)相似,它的原型為:

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

command是將要采取的動作,它可以取3個值,

IPC_STAT:把msgid_ds結(jié)構(gòu)中的數(shù)據(jù)設置為消息隊列的當前關聯(lián)值,即用消息隊列的當前關聯(lián)值覆蓋msgid_ds的值。

IPC_SET:如果進程有足夠的權(quán)限,就把消息列隊的當前關聯(lián)值設置為msgid_ds結(jié)構(gòu)中給出的值

IPC_RMID:刪除消息隊列

buf是指向msgid_ds結(jié)構(gòu)的指針,它指向消息隊列模式和訪問權(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; };

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

三、使用消息隊列進行進程間通信

馬不停蹄,介紹完消息隊列的定義和可使用的接口之后,我們來看看它是怎么讓進程進行通信的。由于可以讓不相關的進程進行行通信,所以我們在這里將會編寫兩個程序,msgreceive和msgsned來表示接收和發(fā)送信息。根據(jù)正常的情況,我們允許兩個程序都可以創(chuàng)建消息,但只有接收者在接收完***一個消息之后,它才把它刪除。

接收信息的程序源文件為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            //建立消息隊列        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);        if(msgid == -1)        {            fprintf(stderr, "msgget failed with error: %d\n", errno);            exit(EXIT_FAILURE);        }        //從隊列中獲取消息,直到遇到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;        }        //刪除消息隊列        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;            //建立消息隊列        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);        if(msgid == -1)        {            fprintf(stderr, "msgget failed with error: %d\n", errno);            exit(EXIT_FAILURE);        }            //向消息隊列中寫消息,直到寫入end        while(running)        {            //輸入數(shù)據(jù)            printf("Enter some text: ");            fgets(buffer, BUFSIZ, stdin);            data.msg_type = 1;    //注意2            strcpy(data.text, buffer);            //向隊列發(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);    }

運行結(jié)果如下:

Linux進程間通信中如何使用消息隊列

四、例子分析&mdash;&mdash;消息類型

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

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

Linux進程間通信中如何使用消息隊列

我們可以看到,msgreceive并沒有接收到信息和輸出,而且當msgsend輸入end結(jié)束后,msgreceive也沒有結(jié)束,通過jobs命令我們可以看到它還在后臺運行著。

五、消息隊列與命名管道的比較

消息隊列跟命名管道有不少的相同之處,通過與命名管道一樣,消息隊列進行通信的進程可以是不相關的進程,同時它們都是通過發(fā)送和接收的方式來傳遞數(shù)據(jù)的。在命名管道中,發(fā)送數(shù)據(jù)用write,接收數(shù)據(jù)用read,則在消息隊列中,發(fā)送數(shù)據(jù)用msgsnd,接收數(shù)據(jù)用msgrcv。而且它們對每個數(shù)據(jù)都有一個***長度的限制。

與命名管道相比,消息隊列的優(yōu)勢在于,1、消息隊列也可以獨立于發(fā)送和接收進程而存在,從而消除了在同步命名管道的打開和關閉時可能產(chǎn)生的困難。2、同時通過發(fā)送消息還可以避免命名管道的同步和阻塞問題,不需要由進程自己來提供同步方法。3、接收程序可以通過消息類型有選擇地接收數(shù)據(jù),而不是像命名管道中那樣,只能默認地接收。

以上就是Linux進程間通信中如何使用消息隊列,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI