Linux進程通信:消息隊列

對消息隊列的理解
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法,傳輸的數據塊類型可以由消息接收者自行解析,消息隊列不僅可以用在不同進程間通信,也可以在不同線程間通信,具體看實際應用
linux系統提供了幾個系統調用方便應用層調用,以下爲消息隊列用到的幾個系統調用

msgget()函數

原型 意義
int msgget(key_t, key, int msgflg); 創建和訪問一個消息隊列
參數 意義
key 鍵來命名某個特定的消息隊列
msgflg 權限標誌,表示消息隊列的訪問權限, 和IPC_CREAT做或操作,當隊列不存在時創建,否則打開

msgsnd()函數

原型 意義
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg); 把消息添加到消息隊列中
參數 意義
msgid 由msgget函數返回的消息隊列標識符
msg_ptr 指向準備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體,接收函數將用這個成員來確定消息的類型
msg_sz msg_ptr指向的消息的長度, 注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度
msgflg 用於控制當前消息隊列滿或隊列消息到達系統範圍的限制時將要發生的事情,0爲阻塞

msgrcv()函數

原型 意義
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg); 從消息隊列獲取消息

如果msgtype爲0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息

參數 意義
msgid 由msgget函數返回的消息隊列標識符
msg_ptr 和msgsnd一樣,但指向準備接收消息的指針
msg_st 和msgsnd一樣,msg_ptr指向的消息的長度, 注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度
msgtype 可以實現一種簡單的接收優先級。如果msgtype爲0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息
msgflg 用於控制當前消息隊列滿或隊列消息到達系統範圍的限制時將要發生的事情,0爲阻塞

msgctl()函數

原型 意義
int msgctl(int msgid, int command, struct msgid_ds *buf); 該函數用來控制消息隊列
參數 意義
msgid 由msgget函數返回的消息隊列標識符
command command是將要採取的動作,它可以取3個值, IPC_STAT,IPC_SET,IPC_RMID
buf 指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構
command 意義
IPC_STAT 把msgid_ds結構中的數據設置爲消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值
IPC_SET 如果進程有足夠的權限,就把消息列隊的當前關聯值設置爲msgid_ds結構中給出的值
IPC_RMID 刪除消息隊列

消息隊列實現:
github源碼

#include "msgqueue.h"

/**
 * @brief 創建一個消息隊列
 * @param name 消息隊列名稱
 * @return 返回一個消息隊列標識符MESSAGEQ_ID
 */
MESSAGEQ_ID create_msgqueue(char * name)
{
    char syscmd[32] = {0x00};
    char filename[32] = {0x00};

    snprintf(filename, sizeof(filename), "/tmp/%s.msgq", name);

    snprintf(syscmd, sizeof(syscmd), "rm -f %s", filename);
    system(syscmd);

    snprintf(syscmd, sizeof(syscmd), "touch %s", filename);
    system(syscmd);

    key_t key = ftok(filename, 0x01);

    MESSAGEQ_ID id = msgget(key,IPC_CREAT | 0755);

    return id;
}

/**
 * @brief 推送一條消息到消息隊列
 * @param msgqid, 消息隊列標識
 * @param msg, 推送的消息指針
 * @param msglen, 推送消息長度
 * @return bool, success or not
 */
bool msgqueue_post(MESSAGEQ_ID msgqid, uint8_t * msg, uint8_t msglen)
{
     MsgQueue_t Msgs =
    {
        .msgtype = 1,
        .msgbuff = {0},
    };

    if(msg == NULL || msglen == 0 || msglen > sizeof(Msgs.msgbuff))
        return false;

    memcpy(Msgs.msgbuff, msg, msglen);
    msgsnd(msgqid, &Msgs, sizeof(Msgs.msgbuff), 0);

    return true;
}

/**
 * @brief 獲取消息
 * @param msgqid, 消息隊列標識
 * @param msg, 獲取的消息存儲緩衝區指針
 * @param msglen, 能夠存的最大消息長度
 * @return bool, success or not
 */
bool msgqueue_wait(MESSAGEQ_ID msgqid, uint8_t * msg, uint8_t msglen)
{
    MsgQueue_t Msgs;

    if(msg == NULL || msglen == 0 || msglen > sizeof(Msgs.msgbuff))
        return false;

    msgrcv(msgqid, &Msgs, sizeof(Msgs.msgbuff), 0, 0);

    memcpy(msg, Msgs.msgbuff, msglen);

    return true;
}

/**
 * @brief 獲取消息隊列中的緩衝的消息數量
 * @param msgqid, 消息隊列標識
 * @return uint32_t, the result
 */
uint32_t msgqueue_getSize(MESSAGEQ_ID msgqid)
{
    struct msqid_ds buffer;

    msgctl(msgqid, IPC_STAT, &buffer);

    return buffer.msg_qnum;
}

/**
 * @brief 刪除消息隊列
 * @param msgqid消息隊列標識
 */
void msgqueue_delete(MESSAGEQ_ID msgqid)
{
    msgctl(msgqid, IPC_RMID, NULL);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章