1. 程式人生 > >程序間通訊(IPC)之訊息佇列

程序間通訊(IPC)之訊息佇列

★IPC方法包括管道(PIPE)、訊息佇列(Message_Queue)、旗語、共用記憶體(ShareMemory)以及套接字(Socket)。進

間通訊主要包括了管道、系統IPC(包括了訊息佇列、訊號以及共享儲存)、套接字(SOCKET)。此文將詳細敘述訊息佇列的相

關內容。

★產生原因:

所謂訊息佇列,其實就是訊息(資料)傳輸過程中儲存的容器。既然有了管道通訊的方式,何必又有訊息佇列呢?

因為根據管道的特性,我們知道其在一定程度上存在或多或少的侷限性,首先匿名管道以及命名管道是隨程序的,進

程的退出,意味著管道生命週期的結束;其次,管道傳送資料時以無格式位元組流的形式傳送,這有時會給程式的開發

帶來不便;再者,擔當資料傳送媒介的管道,其緩衝區的大小也有較大的限制。所以作為IPC方式下的另一種應用於

程序間通訊的訊息佇列方式便應運而生了。

★什麼是訊息佇列?

  訊息佇列就是一個訊息的連結串列。可以把訊息看作一個記錄,具有特定的格式以及特定的優先順序。對訊息佇列有寫

限的程序可以向訊息佇列中按照一定的規則新增新訊息;對訊息佇列有讀許可權的程序則可以從訊息佇列中讀走訊息

訊息佇列是隨核心持續的。也就是說程序的退出,如果不自主去釋放資源,訊息佇列是會悄無聲息的存在的。所以

管道來說,訊息佇列的生命週期更加持久。訊息佇列提供了一種從一個程序向另一個程序傳送一個數據塊的方法。 

每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值。我們可以通過傳送訊息 來避免

名管道的同步和阻塞問題。訊息佇列與管道不同的是,訊息佇列是基於訊息的, 而管道是基於位元組流的,且訊息隊

的讀取不一定是先入先出。訊息佇列與命名管道有一 樣的不足,就是每個訊息的最大長度是有上限的

(MSGMAX),每個訊息佇列的總的位元組數是有上限的(MSGMNB),系統上訊息佇列的總數也有一個上限

(MSGMNI)。見下圖:


★訊息佇列的型別:

     目前有POSIX訊息佇列和系統V訊息佇列,系統V訊息佇列目前被大量使用。考慮到程式的可移植性,新開發的

應用程式應儘量使用POSIX訊息佇列。
系統V訊息佇列是隨核心持續的,只有在核心重起或者顯示刪除一個訊息佇列時,該訊息佇列才會真正被刪除

(對比管道,管道的生命週期是隨程序的)。因此係統中記錄訊息佇列的資料結構(struct ipc_ids msg_ids)位於

核心中,系統中的所有訊息佇列都可以在結構msg_ids中找到訪問入口。 訊息佇列就是一個訊息的連結串列。每個訊息隊

列都有一個佇列頭,用結構struct msg_queue來描述。佇列頭中包含了該訊息佇列的大量資訊,包括訊息佇列鍵

值、使用者ID、組ID、訊息佇列中訊息數目等等,甚至記錄了最近對訊息佇列讀寫程序的ID。讀者可以訪問這些資訊,

也可以設定其中的某些資訊。 

既然訊息佇列是IPC(Inter Process Communication)中的一種,所以我們先來看看核心為每一個程序間通訊物件所維護的資料

結構。

         命令:vim /usr/include/linux/ipc.h

注:可以看到,該結構體中包含了許多關於id方面的資訊,key類似於埠號,為目標獲取(get)時的標誌;

uid為擁有者的id;gid為組使用者id;cuid中的c為建立者(creator);cgid同理;mode為模式,也就是許可權;seq

為順序值。

再來看看關於訊息佇列中的結構體:

   命令:vim /usr/include/linux/msg.h

注:我們可以看到紅色框中的內容正是上面IPC的結構體。而後訊息佇列私有的資料成員為私有部分,其內容對應的都有註釋,就不

一一敘述了。

★相關函式:

1.msgget



注:msgget函式包含了兩個引數key和msgflg。引數key類似於埠號,也可以由fork函式生成。引數msgflg中存在兩個IPC標

志,即IPC_CREAT和IPC_EXEL。

其中,①IPC_CREAT標誌:如果IPC不存在,則建立一個IPC資源,否則開啟操作。                                       

  ②IPC_EXCL:只有在共享記憶體不存在的時候,新的共享記憶體才建立,否則就產生錯誤。如果單獨使用IPC_CREAT,XXXget()

函式要麼返回一個已經存在的共享記憶體的操作符,要 麼返回一個新建的共享記憶體的識別符號。         

  ③如果將IPC_CREAT和IPC_EXCL標誌一起使用,XXXget()將返回一個新建的IPC識別符號 ;如果該IPC資源已存在,或者返

回-1。IPC_EXEL標誌本身並沒有太大的意義,但是和IPC_CREAT標誌一起使用可以用來保證 所得的物件是新建的,而不是開啟已有

的物件。

2.msgrcv和msgsnd:



注:msgrcv從佇列中取出訊息;msgsnd將資料放到訊息佇列中;

函式引數:msqid------訊息佇列的標識碼;msgp-------指向訊息緩衝區的指標,此位置用來暫時儲存傳送和接收的訊息,是一個

使用者可 定義的通用結構,形態如下:struct msgstru{     long mtype; //大於0          char mtext[使用者指定大⼩小]; };msgsz------

---訊息的大小;msgtyp---------從訊息佇列內讀取的訊息形態。如果值為零,則表示訊息佇列中的所有訊息都會被讀取;msgflg--

------用來指明核心程式在佇列沒有資料的情況下所應採取的行動。如果msgflg和常數IPC_NOWAIT合用,則在msgsnd()執行時若

是訊息佇列已滿,則msgsnd()將不會阻塞,而會立即返回-1,如果執行的是msgrcv(),則在訊息佇列呈空時,不做等待馬上返

回-1,並設定錯誤碼為ENOMSG。當msgflg為0時,msgsnd()及msgrcv()在佇列呈滿或呈空的情形時,採取阻塞等待的處理模式。

3.msgctl:

 

注:該函式設定訊息佇列的屬性。函式引數:msgctl系統呼叫對msgqid標識的訊息佇列執行cmd操作,系統定義了3種cmd操作:

IPC_STAT , IPC_SET , IPC_RMID
。

IPC_STAT : 該命令⽤用來獲取訊息佇列對應的 msqid_ds 資料結構,並將其儲存到 buf 指 定的地址空間。
       

IPC_SET : 該命令⽤用來設定訊息佇列的屬性,要設定的屬性儲存在buf中。            

IPC_RMID : 從核心中刪除 msqid 標識的訊息佇列。 

★用訊息佇列對客戶端和服務端間通訊的模擬:

1.comm.h

#pragma once  
  
#include<stdio.h>  
#include<errno.h>  
#include<string.h>  
#include<sys/types.h>  
#include<sys/ipc.h>  
#include<sys/msg.h>  
  
#define _PATH_NAME_ "/tmp"  
#define _PROJ_ID_ 0x6666  
#define _SIZE_ 1024  
  
extern int server_type;  
extern int client_type;  
  
  
struct msgbuf  
{  
    long mtype;  
    char mtext[_SIZE_];  
};  
  
int creat_msg_queue();  
int get_msg_queue();  
//int creat_msg_queue(int msg_id);  
int send_msg(int msg_id,int send_type,const char* msg);  
int recv_msg(int msg_id,int recv_type,char* msg_out);  
int destroy_queue(int msg_id);  


2.comm.c

#include"comm.h"  
  
int server_type = 1;  
int client_type = 2;  
  
  
static int comm_msg_queue(int flags)  
{  
    key_t _key = ftok(_PATH_NAME_,_PROJ_ID_);  
    if(_key < 0)  
    {  
        printf("%d : %s",errno,strerror(errno));  
        return -1;  
    }  
  
    int msg_id = msgget(_key,flags);  
    //int msg_id = msgget(_key,IPC_CREAT | IPC_EXCL | 0666);  
    return msg_id;  
}  
  
int creat_msg_queue()  
{  
    int flags = IPC_CREAT | IPC_EXCL | 0666;  
    return comm_msg_queue(flags);  
}  
  
int get_msg_queue()  
{  
    int flags = IPC_CREAT;  
    return comm_msg_queue(flags);  
}  
  
int destroy_queue(int msg_id)  
{  
    if(msgctl(msg_id,IPC_RMID,NULL) != 0)  
    {  
        printf("%d : %s",errno,strerror(errno));  
        return -1;  
    }  
  
    return 0;  
}  
  
int send_msg(int msg_id,int send_type,const char* msg)  
{  
    struct msgbuf _buf;  
    _buf.mtype = send_type;  
    strncpy(_buf.mtext,msg,strlen(msg)+1);  
  
    if(msgsnd(msg_id,&_buf,sizeof(_buf.mtext),0) < 0)  
    {  
        printf("%d : %s",errno,strerror(errno));  
        return -1;  
    }  
    return 0;  
}  
  
int recv_msg(int msg_id,int recv_type,char* msg_out)  
{  
    struct msgbuf _buf;  
    _buf.mtype = 0;  
    memset(_buf.mtext,'\0',sizeof(_buf.mtext));  
  
    if(msgrcv(msg_id,&_buf,sizeof(_buf.mtext),recv_type,0) < 0)  
    {  
        printf("%d : %s",errno,strerror(errno));  
        return -1;  
    }  
  
    strcpy(msg_out,_buf.mtext);  
    return 0;  
}  


3.server.c

#include"comm.h"  
  
int main()  
{  
    int msg_id = creat_msg_queue();  
    if(msg_id <0)  
    {  
        printf("%d : %s\n",errno,strerror(errno));  
        return 1;  
    }  
  
    char buf[_SIZE_];  
    while(1)  
    {  
        memset(buf,'\0',sizeof(buf));  
        recv_msg(msg_id,client_type,buf);  
        printf("client:%s\n",buf);  
        if(strcasecmp(buf,"quit") == 0)  
        {  
            break;  
        }  
  
        printf("client say done,Please Enter# ");  
        fflush(stdout);  
        ssize_t _s = read(0,buf,sizeof(buf)-1);  
        if(_s > 0)  
        {  
            buf[_s - 1] = '\0';  
        }  
  
        send_msg(msg_id,server_type,buf);  
  
    }  
  
    destroy_queue(msg_id);  
    return 0;  
}  


4.client.c

#include"comm.h"  
  
int main()  
{  
    int msg_id = get_msg_queue();  
  
    char buf[_SIZE_];  
    while(1)  
    {  
        printf("Please Enter:");  
        fflush(stdout);  
        ssize_t _s = read(0,buf,sizeof(buf)-1);  
        if(_s >0 )  
        {  
            buf[_s - 1] = '\0';  
        }  
        send_msg(msg_id,client_type,buf);  
        if(strcasecmp(buf,"quit") == 0)  
        {  
            break;  
        }  
  
        memset(buf,'\0',sizeof(buf));  
        recv_msg(msg_id,server_type,buf);  
        printf("server# %s\n",buf);  
    }  
  
    return 0;  
}  

★Makefile檔案的編寫:

.PHONY:all
all:client server

client:client.c comm.c       //gcc -o [email protected](目標檔案) $^(依賴關係:後的所有內容)
gcc -o [email protected] $^
server:server.c comm.c
gcc -o [email protected] $^

.PHONY:clean
clean:
rm -f server client

★執行結果:


★注:這樣就完成了client端和server端的通訊。需要注意的是,執行程式時如果先打開了client端的可執行程式,此

時的第二個視窗執行server端時,可能會彈出17,FileExists的提示資訊致使程式無法執行,因為此時開啟client端

時已創立了一個訊息佇列,並申請了IPC資源。所以此時需要清除它(注意切換至root許可權),並重新按照先server

後client的順序執行即可。


命令:ipcs -q ----------------顯示訊息佇列相關資訊;

   ipcrm -q [引數]--------------這裡的引數輸入顯示的key值或者msqid的值都可


▲溫馨提示:最後make clean刪除生成的可執行程式時,千萬別忘了用ipcrm指令刪除產生的訊息佇列,因為它不

具有管道那樣程序退出就釋放所有資源的特性,不自主手動釋放,它也是會偷偷的一直存在的。

注:comm.h中的五個介面函式:

①int creat_msg_queue();  //建立一個訊息佇列
②int get_msg_queue();   //獲得訊息佇列的msg_id

③int send_msg(int msg_id,int send_type,const char* msg);  //傳送訊息
④int recv_msg(int msg_id,int recv_type,char* msg_out);  //接收訊息

⑤int destroy_queue(int msg_id);  //銷燬佇列

相關推薦

程序通訊(IPC)訊息佇列

★IPC方法包括管道(PIPE)、訊息佇列(Message_Queue)、旗語、共用記憶體(ShareMemory)以及套接字(Socket)。進 程間通訊主要包括了管道、系統IPC(包括了訊息佇列、

Linux---程序通訊IPC訊息佇列

**程序間通訊(IPC):**是指在不同程序之間傳播或交換資訊。 **IPC的方式:**通常有管道(無名管道、命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等(Socket和Streams支援不同主機上的兩個程序IPC) 程序間通訊的目

程序程式設計程序通訊-管道和訊息佇列

1.程序間通訊 Linux作為一種新興的作業系統,幾乎支援所有的Unix下常用的程序間通訊方法:管道、訊息佇列、共享記憶體、訊號量、套介面等等。 2.管道 管道是程序間通訊中最古老的方式,它包括無名管道(或者匿名管道)和有名管道兩種,前者用於父程序和

程序通訊方式總結——訊息佇列

        Linux/Unix系統IPC是各種程序間通訊方式的統稱,但是其中極少能在所有Linux/Unix系統實現中進行移植。隨著POSIX和Open Group(X/Open)標準化的推進

Android程序通訊(IPC)Socket

Socket也被稱為“套接字”程式設計,它分為流式套接字和使用者資料套接字兩種,分別對應於網路傳輸控制中層中TCP和UDP協議。TCP協議是面向連線的協議,提供穩定的雙向通訊功能,TCP連線的建立需要經過”三次握手”才能實現,為了實現穩定的資料傳輸功能,其本身提

程序通訊——管道,訊息佇列,共享記憶體

程序間通訊的本質是讓兩個不相干的程序看到同一份資源。這個資源是由作業系統提供的一個檔案。程序間通訊的目的:1.資料傳輸:一個程序需要將它 的資料傳送給另一個程序。2.資源共享:多個程序之間共享同樣的資源。3.通知事件:一個程序需要向另一個(組)程序傳送訊息,通知它們發生了

Linux---程序通訊IPC管道

**程序間通訊(IPC):**是指在不同程序之間傳播或交換資訊。 **IPC的方式:**通常有管道(無名管道、命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等(Socket和Streams支援不同主機上的兩個程序IPC) 程序間通訊的目的:

程序通訊(IPC)訊號量

★IPC方法包括管道(PIPE)、訊息佇列(Message_Queue)、訊號量(semaphore)、共用記憶體 (ShareMemory)以及套接字(Socket)。程序間通訊主要包括了管道、系統IPC(包括了訊息佇列、訊號以 及共享儲存)、套接字(SOCKET)。此文

程序通訊---管道和訊息佇列

程序間通訊的目的:資料傳輸:一個程序需要將它的資料傳送給另一個程序資源共享:對個程序之間共享同樣的資源通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它們發生了什麼事件程序控制:有些程序希望完全控制另一個程序的執行(如:Debug程序)程序間通訊的發展:管道:Syste

Linux---程序通訊IPC共享記憶體

程序間通訊(IPC):是指在不同程序之間傳播或交換資訊。 IPC的方式:通常有管道(無名管道、命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等(Socket和Streams支援不同主機上的兩個程序IPC) 程序間通訊的目的: 1

Linux程序通訊(IPC)程式設計實踐(十二)Posix訊息佇列--基本API的使用

posix訊息佇列與system v訊息佇列的差別: (1)對posix訊息佇列的讀總是返回最高優先順序的最早訊息,對system v訊息佇列的讀則可以返回任意指定優先順序的訊息。 (2)當往一個空佇列放置一個訊息時,posix訊息佇列允許產生一個訊號或啟動一個執行緒,

Linux程序通訊(IPC)程式設計實踐(三) 詳解System V訊息佇列(1)

訊息佇列簡介 訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法(本機);每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值。訊息佇列也有管道一樣的不足:  (1)每

Linux下程序通訊方式管道、訊號、共享記憶體、訊息佇列、訊號量、套接字

/* 1,程序間通訊 (IPC ) Inter-Process Communication   比較好理解概念的就是程序間通訊就是在不同程序之間傳播或交換資訊。 2,linux下IPC機制的分類:管道、訊號、共享記憶體、訊息佇列、訊號量、套接字 3,這篇主要說說管

【Qt】Qt程序通訊(Windows訊息)【轉】

簡述 通過上一節的瞭解,我們可以看出程序通訊的方式很多,今天分享下如何利用Windows訊息機制來進行不同程序間的通訊。 效果 傳送訊息 自定義型別與接收窗體 包含所需庫,定義傳送的自定義型別、接收訊息的窗體標題。自定義型別可以處理訊息過多情況下,對訊息的區分,如果不需要也可以去掉。

Qt 程序通訊(Windows 訊息

簡述 通過上一節的瞭解,我們可以看出程序通訊的方式很多,今天分享下如何利用Windows訊息機制來進行不同程序間的通訊。 | 效果 傳送訊息 自定義型別與接收窗體 包含所需庫,定義傳送的自定義型別、接收訊息的窗體標題。自定義型別可以處理

Linux程序通訊IPC的幾種方式簡介

Linux程序通訊的源頭       linux下的程序通訊手段基本上是從Unix平臺上的程序通訊手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力AT&T(原為American Telephone & Tele

Android系統程序通訊 IPC 機制Binder中的Server啟動過程原始碼分析

                        在前面一篇文章中,介紹了在Android系統中Binder程序間通訊機制中的Server角色是如何獲得Service Manager遠端介面的,即defaultServiceManager函式的實現。Server獲得了Service Manager遠端介面之後,

linux程序通訊(IPC)機制總結

在linux下的多個程序間的通訊機制叫做IPC(Inter-Process Communication),它是多個程序之間相互溝通的一種方法。在linux下有多種程序間通訊的方法:半雙工管道、命名管道、訊息佇列、訊號、

3. 程序通訊IPC

一、概念 IPC:     1)在linux環境中的每個程序各自有不同的使用者地址空間。任何一個程序的全域性變數在另一個程序中都看不到,所以程序和程序之間是不能相互訪問。     2)如果程序間要交換資料必須通過核心,在核心中開闢一塊緩衝區,程

Linux程序通訊(IPC)程式設計實踐(十)System V訊號量---PV操作經典題目

//P原語     //P(semaphore *S)     wait(semaphore *S)       {           -- S->value;           if (S->value < 0)           {