進程間通信--消息隊列

Linux的一個重要特色就是允許兩個進程間通過數據交換進行通信,這樣簡單的程序就可以組合起來實現複雜的任務。現在我們主要談談基於系統V IPC機制的通信方式–消息隊列。

一.什麼是消息隊列?

消息隊列是消息的鏈表,每個消息都有固定的最大長度。消息可以加到隊列的尾部,消息的發送次序和接收次序是一致的,消息是可以有類型的,這樣使用一個隊列就可以處理多個消息流。

二 . 在使用消息隊列之前,和IPC另外兩種通信方式一樣調用get函數獲取該隊列的標識符,在消息隊列中使用megget函數,源碼如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

參數key值指定了消息隊列的鍵,這裏可以調用ftok函數獲取他的key值,參數msgflg如果是自己要創建設置爲IPC_CREAT和IPC_EXCL,因爲兩個一起設置表示,表示存在則打開,不存在則創建,如果別人要獲取msgid,只需要使用IPC_CREAT,直接打開,成功返回標識符,失敗返回-1,並將失敗原因保存在errno中。

創建消息隊列之後必然要刪除它,這裏調用的是msgctl函數,代碼如下:

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>

 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

在消息隊列創建好以後我們就要進行通信了,既然要通信,那麼必須涉及的兩個函數,一個是發送數據,一個是接收數據。發送數據使用msgsnd,接收數據使用msgrcv,調用如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

msgp參數是一個指向caller-defined結構的一般形式如下:

struct msgbuf {
      long mtype;       /* 消息類型,必須大於0 */
      char mtext[1];    /* 發送的數據,大小自己制定 */
 };

msgsz參數表示隊列容量
在消息隊列的關聯數據結構中由msgbytes字段定義。在隊列創建這個字段的初始化爲MSGMNB字節,但是這個限制可以使用msgctl(2)進行修改。如果隊列中沒有足夠的空間,那麼msgsnd()的默認行爲就是阻塞直到空間可用。如果ipcnowait在msgflg中指定,則調用將失敗。

消息隊列的創建

static int commMsgQuene(int flags)
{
    key_t _key = ftok(PATHNAME, PROJ_ID);
    if(_key < 0){
        perror("ftok");
        exit(-1);
    }
    int msggid = msgget(_key,flags);
    if(msggid<0){
        perror("msgget");
        exit(-2);
    }
    return msggid;
}


int creatMsgQuene()
{
    return commMsgQuene(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQuene()
{ 
    return commMsgQuene(IPC_CREAT);
}

消息隊列的銷燬

int DestoryMsgQuene(int msgid)
{
    if(msgctl(msgid, IPC_RMID,NULL)<0){
        perror("msgctl");
        return -1;
    }
    return 0;
}

發送消息

int SendMsg(int msgid, int who, char *msg)
{
    struct msgbuf buf;
    buf.mtype= who;
    strcpy(buf.mtext, msg);
    if(msgsnd(msgid, (void*)&buf, sizeof(buf.mtext), 0)<0){
        perror("msgid");
        exit(-1);
    }
    return 0;
}

接收消息

int RecMsg(int msgid, int recType, char out[])
{
    struct msgbuf buf;
    if(msgrcv(msgid, (void*)&buf, sizeof(buf.mtext),recType,0)<0){
        perror("megrcv");
        exit(-1);
    }

    strcpy(out, buf.mtext);

這裏說一下要使用shrcpy,因爲是一個函數,我們都知道函數裏面的數據只有在函數內有意義,出了這個函數體以後,函數就被銷燬了,有可能導致數據被篡改了,所以設計一個返回型參數存放他的數據。
這裏就是所有的函數設計,只需要另外編寫server和client接收和發送數據即可。

三 . 爲什麼兩個進程之間不能直接進行通信?

由於內存管理的一些機制,導致兩個進程間並不能直接的進行通信(在獨立的用戶空間),因此我們需要利用一些介質來完成兩個進程之間的通信。以下是常用的進程間通信方式。
管道( pipe ):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關係的進程間使用。進程的親緣關係通常是指父子進程關係。
有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關係進程間的通信。
信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作爲一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作爲進程間以及同一進程內不同線程之間的同步手段。
消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。
信號 ( sinal ) : 信號是一種比較複雜的通信方式,用於通知接收進程某個事件已經發生。
共享內存( shared memory ) :共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號兩,配合使用,來實現進程間的同步和通信。
套接字( socket ) : 套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同及其間的進程通信。

四 . 查看和刪除消息隊列的控制指令

ipcs -m
ipcrm -m +msgid   //很重要的兩條指令
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章