linux消息隊列總結

http://blog.csdn.net/stonecao/article/details/10364287


1.消息隊列簡介

實現linux進程通信的方式有5種:

--信號(Singal)

--管道(Pipe)

--消息隊列(Message)

--信號量(Semaphore)

每種進程通信方式實現方式和功能不一樣,帶來適用的場景也有所不同,消息隊列是鏈表隊列,它通過內核提供一個struct msqid_ds *msgque[MSGMNI]向量維護內核的一個消息隊列列表,因此linux系統支持的最大消息隊列數由msgque數組大小來決定,每一個msqid_ds表示一個消息隊列,並通過msqid_ds.msg_first、msg_last維護一個先進先出的msg鏈表隊列,當發送一個消息到該消息隊列時,把發送的消息構造成一個msg結構對象,並添加到msqid_ds.msg_first、msg_last維護的鏈表隊列,同樣,接收消息的時候也是從msg鏈表隊列尾部查找到一個msg_type匹配的msg節點,從鏈表隊列中刪除該msg節點,並修改msqid_ds結構對象的數據。


2.消息隊列的數據結構

--1.

struct msqid_ds *msgque[MSGMNI]

向量:

       msgque[MSGMNI]是一個msqid_ds結構的指針數組,每個msqid_ds結構指針代表一個系統消息隊列,msgque[MSGMNI]的大小爲MSGMNI=128,也就是說系統最多有MSGMNI=128個消息隊列

--2.

struct msqid_ds

        一個消息隊列結構

struct msqid_ds 中主要數據成員介紹如下:

struct msqid_ds
{
        struct ipc_perm msg_perm;
        struct msg *msg_first;         /*消息隊列頭指針*/
        struct msg *msg_last;          /*消息隊列尾指針*/
        __kernel_time_t msg_stime;     /*最後一次插入消息隊列消息的時間*/
        __kernel_time_t msg_rtime;     /*最後一次接收消息即刪除隊列中一個消息的時間*/
        __kernel_time_t msg_ctime; 
        struct wait_queue *wwait;      /*發送消息等待進程隊列*/
        struct wait_queue *rwait;  
        unsigned short msg_cbytes;
        unsigned short msg_qnum;       /*消息隊列中的消息個數*/
        unsigned short msg_qbytes;
        __kernel_ipc_pid_t msg_lspid;  /*最後一次消息發送進程的pid*/
        __kernel_ipc_pid_t msg_lrpid;  /*最後一次消息發送進程的pid*/
};

--3.

struct msg

消息節點結構:

        msqid_ds.msg_first,msg_last維護的鏈表隊列中的一個鏈表節點

struct msg
{
       msg *msg_next;              /*下一個msg*/
       long msg_type;              /*消息類型*/
       *msg_spot;                  /*消息體開始位置指針*/
        msg_ts;                    /*消息體長度*/
        message;                  /*消息體*/
}

--4.msgbuf消息內容結構:

       msg 消息節點中的消息體,也是消息隊列使用進程(消息隊列發送接收進程)發送或者接收的消息

struct msgbuf
{
        long mtype;        --消息類型
        char mtext[n];     --消息內容
}



3.消息隊列的使用


--1.消息隊列Key的獲取:

        在程序中若要使用消息隊列,必須要能知道消息隊列key,因爲應用進程無法直接訪問內核消息隊列中的數據結構,因此需要一個消息隊列的標識,讓應用進程知道當前操作的是哪個消息隊列,同時也要保證每個消息隊列key值的唯一性

    ----a.通過ftok函數獲取

    key_t key;    
    key=ftok(".","a")

           該函數通過一個路徑名稱映射出一個消息隊列key(我的理解是使用路徑映射的方式比較容易獲取一個唯一的消息隊列key)

    ----b.直接定義key:

    #define MSG_KEY      123456

            自定義key的方式要注意避免消息隊列的重複。


--2.獲取或者打開一個消息隊列

    ----a.使用說明

     qid=msgget(key_t key, int msgflag)

        --key:  消息隊列key

        --msgflag:

            ·IPC_PRIVATE:創建一個該進程獨佔的消息隊列,其它進程不能訪問該消息隊列

            ·IPC_CREAT:若消息隊列不存在,創建一個新的消息隊列,若消息隊列存在,返回存在的消息隊列

            ·IPC_CREAT | IPC_EXCL: IPC_EXCL標誌本身沒有多大意義,與IPC_CREAT一起使用,保證只創建新的消息隊列,若對應key的消息隊列已經存在,則返回錯誤

            ·IPC_NOWAIT:小隊列以非阻塞的方式獲取(若不能獲取,立即返回錯誤)

    ----b.函數原理

        ------1)如果key==IPC_PRIVATE,則申請一塊內存,創建一個新的消息隊列(數據結構msqid_ds),將其初始化後加入到msgque向量表中的某個空位置處,返回標示符。

        ------2)在msgque向量表中找鍵值爲key的消息隊列,如果沒有找到,結果有二:

                ·msgflag表示不創建新的隊列,則錯誤返回。

                ·msgflag表示要創建新的隊列(IPC_CREAT),則創建新消息隊列,創建過程如1)。

        -------3)如果在msgque向量表中找到了鍵值爲key的消息隊列,則有以下情況:

                ·如果msgflg表示一定要創建新的消息隊列而且不允許有相同鍵值的隊列存在,則錯誤返回。

                ·如果找到的隊列是不能用的或已經損壞的隊列,則錯誤返回。

                ·認證和存取權限檢查,如果該隊列不允許msgflg要求的存取,則錯誤返回。

                ·正常,返回隊列的標識符。


--3.發送一個消息到消息隊列

    ----a.使用說明:

    int msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)


        ------msqid爲消息隊列的qid 
        ------msgp對應消息內容結構體指針
        ------msgsz消息的大小即msgp指針指向的消息結構體的大小
        ------msgflg消息標誌

            ·0:忽略該標誌位,以阻塞的方式發送消息到消息隊列

            ·IPC_NOWAIT:以非阻塞的方式發送消息,若消息隊列滿,函數立即返回。

        ------返回:

            ·0: 成功

            ·-1:非阻塞方式訪問滿消息隊列返回

            ·EACCES:沒有該消息隊列寫權限

            ·EFAULT:消息隊列地址無法獲取

            ·EIDRM:消息隊列已經被刪除

            ·EINTR:消息隊列等待寫入的時候被中斷

            ·ENOMEM:內存不夠

    ----b.函數原理:

            1)計算id = (unsigned int) msqid % MSGMNI,然後根據id在linux系統消息隊列向量msgque[MSGMNI]中查找對應的消息隊列,並進行認證檢查,合法性檢查

            2)如果隊列已滿,以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在wwait等待隊列(發送消息等待隊列)上(msgflag==0)。

            3)否則根據msgbuf的大小申請一塊空間,並在其上創建一個消息數據結構struct msg(內核空間),將消息緩衝區中的消息內容拷貝到該內存塊中消息頭的後面(從用戶空間拷貝到內核空間)。

            4)將消息數據結構加入到消息隊列的隊尾,修改隊列msqid_ds的相應參數。

            5)喚醒在該消息隊列的rwait進程隊列(讀等待進程隊列)上等待讀的進程,並返回。


--4.從消息隊列接收一個消息到msgbuf*

    ----a.使用說明

     int msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msgtyp, int msgflg)

            ----msqid爲消息隊列的qid 
            ----msgp是接收到的消息將要存放的緩衝區
            ----msgsz是消息的大小
            ----msgtyp是期望接收的消息類型
            ----msgflg是標誌

                ·0:表示忽略

                ·IPC_NOWAIT:如果消息隊列爲空,不阻塞等待,返回一個ENOMSG

            ----返回

                ·0:成功

                ·-1:消息長度大於msgsz

                ·EACCES:沒有該消息隊列讀權限

                ·EFAULT:消息隊列地址無法獲取

                ·EIDRM:消息隊列已經被刪除

                ·EINTR:消息隊列等待寫入的時候被中斷

                ·ENOMEM:內存不夠

    ----b.函數原理:

        1)計算id = (unsigned int) msqid % MSGMNI,然後根據id在msgque[MSGMNI]中查找對應的消息隊列,並進行認證檢查,合法性檢查
        2)根據msgtyp搜索消息隊列,情況有二:
            ----如果找不到所要的消息,則以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在rwait等待隊列
            ----如果找到所要的消息,則將消息從隊列中摘下,調整隊列msqid_ds參數,喚醒該消息隊列的wwait進程隊列上等待寫的進程,將消息內容拷貝到用戶空間的消息緩衝區msgp中,釋放內核中該消息所佔用的空間,返回


--5.消息的控制

    ----a.使用說明:

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

        ------msqid:爲消息隊列的qid
        ------cmd:爲該函數要對消息隊列執行的操作

            ·IPC_STAT:取出消息隊列的msqid_ds結構體並將參數存入buf所指向的msqid_ds結構對象中

            ·IPC_SET:設定消息隊列的msqid_ds 數據中的msg_perm 成員。設定的值由buf 指向的msqid_ds
結構給出。

            ·IPC_EMID:將隊列從系統內核中刪除。
        ----buf:消息隊列msqid_ds結構體指針

    ----b.函數作用

        對消息隊列進行設置以及相關操作,具體操作由cmd指定。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章