Linux進程間通信二 System V 消息隊列簡介與示例

1. SystemV消息隊列簡介

消息隊列,顧名思義即是存放消息的隊列,內核爲每個SystemV 維護了一個msg_queue的結構體,裏面記錄了每個消息隊列的信息。

struct msg_queue {
    structkern_ipc_perm q_perm;   // 權限相關
    time_tq_stime;         /* 上一次 msgsnd時間 */
    time_tq_rtime;         /* 上一次 msgrcv時間 */
    time_tq_ctime;         /* 屬性變化時間 */
    unsignedlong q_cbytes;     /* 隊列當前字節總數 */
    unsignedlong q_qnum;       /* 隊列當前消息總數 */
    unsignedlong q_qbytes;     /* 一個消息隊列允許最大字節數 */
    pid_tq_lspid;          /* 上一個調用msgsnd的進程ID */
    pid_tq_lrpid;          /* 上一個調用msgrcv的進程ID */
    structlist_head q_messages;
    structlist_head q_receivers;
    structlist_head q_senders;
};                                                                                                                                                         

消息隊列存儲消息的結構體需要用戶自定義,且第一個字段必須是long類型。雖然誕生已久,但是systemV支持優先級隊列和FIFO這一點確實是挺強大的,缺點也很明顯,不支持IO多路複用。一般大型的MQ開發很少用到SystemV mq,因爲存在很多限制,比如系統能夠創建的消息隊列個數有限,單條消息大小受限,單個消息隊列裏面消息個數受限等等。即便如此還是需要學習一下systemV消息隊列,以後自己做相關的開發可以借鑑相關的設計思路。

 

2. API接口

2. 1 創建或獲取消息隊列ID

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

/**
* @brief 創建或獲取消息隊列ID
*
* @params key 標識符,整形變量,三種方式,固定值,IPC_PRIVATE,ftok生成
* @params msgflg,標誌位,IPC_CREAT and IPC_EXCL
*
* @returns 成功返回消息隊列ID,失敗返回-1
*/

int msgget(key_t key, int msgflg);

2.2 寫消息

/**
* @brief 發送消息
*
* @params msqid 消息隊列ID
* @params msgp 待發送消息的地址,類型需要自定義且第一個元素爲long類型
* @params msgsz 消息大小
* @params msgflg 標誌位,IPC_NOWAIT,未指定情況下,如果消息隊列空間滿,默認阻塞,設置此標誌位則立即返回。

* @returns 成功返回消息隊列ID,失敗返回-1
*/

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

2.3 讀消息

/**
* @brief 發送消息
*
* @params msqid 消息隊列ID
* @params msgp 接收消息的緩存,需要和發送方約定好結構體
* @params msgsz 接收消息最大長度,不包括消息類型字段
* @params msgtyp 要接收的消息類型。
>0分爲兩種情況,如果設置了MSG_EXCEPT,從消息隊列中取出第一條消息類型相同的消息。
否則,取出第一條消息類型不同的消息。
=0相當於先入先出,最早進入消息隊列的消息被取出。
<0相當於優先級隊列,取出值小於或等於msgtyp絕對值的第一條消息

* @params msgflg 標誌位,IPC_NOWAIT,MSG_EXCEPT,MSG_NOERROR(消息過長時是否截斷)

* @returns 成功返回接收到的消息內容長度,失敗返回-1
*/

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

2.4 消息隊列其它控制操作

/**
* @brief 控制操作
*
* @params msqid 消息隊列ID
* @params cmd 控制命令類型
IPC_STAT 獲取或設置消息隊列的屬性
IPC_SET  設置權限相關屬性
IPC_RMID 刪除消息隊列,釋放消息隊列中的所有消息

* @returns 成功返回0,失敗返回-1
*/


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

3. 示例

下面是一個簡單的消息隊列例子,分爲讀消息和寫消息兩個部分。

3.1讀取


// 讀取消息隊列

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

// 消息結構體,第一個元素必須是消息類型
struct msg{
    long mtype;         // 消息類型
    char data[1024];    // 數據
};

int main()
{
    int mq_id = 0;
    key_t key = 1357;       // 標識符,三種方式,固定值,IPC_PRIVATE,ftok生成
    ssize_t msg_size;
    struct msg message={0};

    // 創建或獲取消息隊列
    mq_id = msgget(key, IPC_CREAT);
    if (mq_id == -1)
    {
        printf("create mq error");
        return mq_id;
    }

    // 循環監聽消息隊列
    while (1)
    {
        // 阻塞監聽所有消息類型的消息
        msg_size = msgrcv(mq_id, (void *)&message, sizeof(message), 0, 0);    
        if (msg_size != -1)
        {
            printf("read msg type:%ld, data:%s\n", message.mtype, message.data);
        }

        memset(&message,0,sizeof(message));
    }

    return 0;
}

3.2 寫入

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

// 消息結構體,第一個元素必須是消息類型
struct msg{
    long mtype;         // 消息類型
    char data[1024];    // 數據
};

int main(int argc, char** argv)
{
    if (argc < 3)
    {
        printf("invalid param, please input msg type and data\n");
        printf("Usage: ./mq_write type data\n");
        return -1;
    }

    int mq_id = 0;
    key_t key = 1357;       // 標識符,三種方式,固定值,IPC_PRIVATE,ftok生成

    // 創建或獲取消息隊列
    mq_id = msgget(key, IPC_CREAT);
    if (mq_id == -1)
    {
        printf("create mq error\n");
        return mq_id;
    }

    struct msg message;
    message.mtype = strtol(argv[1],NULL,0);
    strcpy(message.data, argv[2]);
    
    // 發送消息
    if (msgsnd(mq_id, (const void*)&message, sizeof(message), 0) == 0)
    {
        printf("send message succ\n");
    }
    else
    {
        printf("send message error\n");
    }
    return 0;
}

3.3 編譯&運行

gcc -o mq_read mq_read.c
gcc -o mq_wirte mq_write.c

 

4. 注意事項

4.1  消息隊列限制

MSGMAX 單條消息大小有限
cat /proc/sys/kernel/msgmax

MSGMNI 消息隊列總個數有限
cat /proc/sys/kernel/msgmni

MSGMNB 單個消息隊列最大大小
cat /proc/sys/kernel/msgmnb

5. 參考資料

1. https://man7.org/linux/man-pages/man2/msgsnd.2.html

2. 《Linux環境編程 從應用到內核》

3. 《Unix網絡編程 卷二 進程間通信》

================================================================================================

Linux應用程序、內核、驅動、後臺開發交流討論羣(745510310),感興趣的同學可以加羣討論、交流、資料查找等,前進的道路上,你不是一個人奧^_^。...

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