POSIX訊息佇列

嚇人的猿發表於2018-02-27

3.2 POSIX訊息佇列

​ 訊息佇列可以認為是一個連結串列。程式(執行緒)可以往裡寫訊息,也可以從裡面取出訊息。一個程式可以往某個訊息佇列裡寫訊息,然後終止,另一個程式隨時可以從訊息佇列裡取走這些訊息。這裡也說明了,訊息佇列具有隨核心的持續性,也就是系統不重啟,訊息佇列永久存在。

3.2.1 建立(並開啟)、關閉、刪除一個訊息佇列

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode,
                     struct mq_attr *attr);

/*  
函式說明:函式建立或開啟一個訊息佇列,建立後,在/dev/mqueque下可以看到對應檔案
返回值:成功返回訊息佇列描述符,失敗返回-1,錯誤原因存於errno中 
name: 檔案路徑,必須以/開頭,而且有且只有一個/
oflag: 與open的開啟標誌一樣,加上O_CREAT標誌,能夠建立訊息佇列
mode: 與open的開啟mode一樣
attr: 建立訊息佇列的時候需要,指定訊息佇列大小訊息個數等,為NULL表示使用預設引數
*/ 
/*訊息佇列屬性結構體*/
struct mq_attr { 
   long mq_flags;       /* Flags: 0 or O_NONBLOCK */ 
   long mq_maxmsg;      /* Max. # of messages on queue */ 
   long mq_msgsize;     /* Max. message size (bytes) */ 
   long mq_curmsgs;     /* # of messages currently in queue */ 
};

mqd_t mq_close(mqd_t mqdes);
/*  
函式說明:關閉一個開啟的訊息佇列,表示本程式不再對該訊息佇列讀寫  
返回值:成功返回0,失敗返回-1,錯誤原因存於errno中  
*/  

int mq_unlink(const char *name);
/*  
函式說明:刪除一個訊息佇列,好比刪除一個檔案,其他程式再也無法訪問  
返回值:成功返回0,失敗返回-1,錯誤原因存於errno中  
*/

需要注意:

​ 訊息佇列的名字最好使用“/”打頭,並且只有一個“/”的名字。否則可能出現移植性問題;它必須符合已有的路徑名規則(最多由PATH_MAX個位元組構成,包括結尾的空位元組)。

3.2.2 Posix訊息佇列讀寫

訊息佇列的讀寫主要使用下面兩個函式:

#include <mqueue.h>  


int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); 
/*
功能:傳送一條訊息到佇列
mqdes:訊息佇列檔案描述符
msg_ptr: 訊息內容
msg_len: 內容長度
msg_prio: 訊息優先順序,它是一個小於MQ_PRIO_MAX的數, 數值越大,優先順序越高
返回:若成功則為訊息中位元組數,若出錯則為-1 
*/ 


ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);  
/*
功能:從佇列接收一條訊息
mqdes:訊息佇列檔案描述符
msg_ptr: 訊息內容
msg_len: 內容長度
msg_prio: 訊息優先順序
返回:若成功則為0, 若出錯則為-1
*/ 

原始碼sendmq.c

#include <stdio.h>  
#include <string.h>
#include <stdlib.h>  
#include <mqueue.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
   
void error_print(const char* msg)
{
     perror(msg);   
     exit(-1);  
}

/*向訊息佇列傳送訊息,訊息佇列名及傳送的資訊通過引數傳遞*/ 
int main(int argc, char *argv[])  
{  
    const char* mqname = "/mymq";
    char buf[] = "helloworld";
    mqd_t mqd;
    int ret;
        
    //只寫模式找開訊息佇列,不存在就建立  
    mqd = mq_open(mqname, O_WRONLY | O_CREAT, 0666, NULL);  
    if(mqd < 0)  
        error_print("mq_open");
   
    /*向訊息佇列寫入訊息,如訊息佇列滿則阻塞,直到訊息佇列有空閒時再寫入*/ 
    ret = mq_send(mqd, buf, strlen(buf) + 1, 10);  
    if(ret < 0)  
        error_print("mq_send");
  
    ret = mq_close(mqd);
    if(ret < 0)
        error_print("mq_close");
    return 0;  
} 

編譯:

gcc -o sendmq sendmq.c -lrt

執行後可以在/dev/mqueue下看到mymq檔案。

原始碼recvmq.c

#include <stdio.h>  
#include <stdlib.h>  
#include <mqueue.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
void error_print(const char* msg)
{
     perror(msg);   
     exit(-1);  
}
/*讀取某訊息佇列,訊息佇列名通過引數傳遞*/ 
int main(int argc, char *argv[])  
{  
    const char* mqname = "/mymq";
    mqd_t mqd;  
    struct mq_attr attr;  
    char *buf;  
    unsigned int prio;  
    int ret;  

    /*只讀模式開啟訊息佇列*/ 
    mqd = mq_open(mqname, O_RDONLY);  
    if(mqd < 0)  
         error_print("mq_open");   

    /*取得訊息佇列屬性,根據mq_msgsize動態申請記憶體*/
    ret = mq_getattr(mqd, &attr);  
    if(ret < 0)  
        error_print("mq_getattr");  

    /*動態申請保證能存放單條訊息的記憶體*/ 
    buf = (char*)malloc(attr.mq_msgsize);  
    if(NULL == buf)  
         error_print("malloc");  

    /*接收一條訊息*/ 
    ret = mq_receive(mqd, buf, attr.mq_msgsize, &prio);  
    if(ret < 0)  
        error_print("receive"); 
    
    printf("read data %s  priority %u\n", buf, prio);  
  
    ret = mq_close(mqd);
    if(ret < 0)
        error_print("mq_close");
  
 /*訊息佇列使用完後就可以刪除
    ret = mq_unlink(mqname);
    if(ret < 0)
        error_print("mq_unlink");
  */
    free(buf);
    return 0;  
} 

編譯並執行:

gcc -o recvmq recvmq.c -lrt

  

需要注意的是:當訊息不斷髮送,達到訊息佇列容量最大值的時候,mq_send將阻塞,知道訊息佇列被接收走,如果訊息還未接收,就把訊息佇列檔案刪除,則訊息丟失。

3.2.3 訊息佇列的屬性

Posix訊息佇列的屬性使用如下結構存放:
struct mq_attr  
{  
    long mq_flags; /*阻塞標誌位,0為非阻塞(O_NONBLOCK)*/ 
    long mq_maxmsg; /*佇列所允許的最大訊息條數*/ 
    long mq_msgsize; /*每條訊息的最大位元組數*/ 
    long mq_curmsgs; /*佇列當前的訊息條數*/ 
}; 
/*
佇列可以在建立時由mq_open()函式的第四個引數指定mq_maxmsg,mq_msgsize。 如建立時沒有指定則使用預設值,一旦建立,則不可再改變。
佇列可以在建立後由mq_setattr()函式設定mq_flags 
*/

#include <mqueue.h>  

/*取得訊息佇列屬性,放到mqstat中*/ 
int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);  

/*設定訊息佇列屬性,設定值由mqstat提供,原先值寫入omqstat中,只是用來改變O_NONBLOCK標誌*/ 
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat);  

均返回:若成功則為0,若出錯為-1 

獲取訊息佇列的屬性:

attrmq.c

#include <stdio.h>  
#include <stdlib.h>  
#include <mqueue.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
   
void error_print(const char* msg)
{
     perror(msg);   
     exit(-1);  
}
int main()  
{  
    mqd_t mqd;  
    int ret;  
    struct mq_attr mqattr; 
    const char* mqname = "/mymq";
    /*只讀模式開啟訊息佇列*/ 
    mqd = mq_open(mqname, O_CREAT | O_RDONLY, 0666, NULL);  
    if(mqd < 0)  
         error_print("mq_open");   

    /*取得訊息佇列屬性*/
    ret = mq_getattr(mqd, &attr);  
    if(ret < 0)  
        error_print("mq_getattr");  

    printf("nonblock flag:%ld\n", attr.mq_flags);  
    printf("max msgs:%ld\n", attr.mq_maxmsg);  
    printf("max msg size:%ld\n", attr.mq_msgsize);  
    printf("current msg count:%ld\n", attr.mq_curmsgs);  
   
    ret = mq_close(mqd);
    if(ret < 0)
        error_print("mq_close");
 
    ret = mq_unlink(mqname);
    if(ret < 0)
        error_print("mq_unlink");

    return 0;  
} 

編譯並執行:

gcc -o attrmq attrmq.c -lrt

設定訊息佇列的屬性:

原始碼setarrtmq.c

#include <stdio.h>  
#include <stdlib.h>  
#include <mqueue.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
       
void error_print(const char* msg)
{
     perror(msg);   
     exit(-1);  
}
int main()  
{  
    mqd_t mqd;  
    int ret;  
    const char * mqname = "/mymq";
    struct mq_attr mqattr;  
    mqattr.mq_maxmsg = 10; 
    mqattr.mq_msgsize = 8192;  

    mqd = mq_open(mqname, O_RDWR | O_CREAT, 0666, &mqattr);  

    if(mqd < 0)  
    {  
        perror("mq_open");  
        exit(1);  
    }  
    
    mqattr.mq_flags = O_NONBLOCK;  
    mq_setattr(mqd, &mqattr, NULL);// mq_setattr()只關注mq_flags  
       
    /*取得訊息佇列屬性*/
    ret = mq_getattr(mqd, &mqattr);  
    if(ret < 0)  
        error_print("mq_getattr");  
  
    printf("nonblock flag:%ld\n", mqattr.mq_flags);  
    printf("max msgs:%ld\n", mqattr.mq_maxmsg);  
    printf("max msg size:%ld\n", mqattr.mq_msgsize);  
    printf("current msg count:%ld\n", mqattr.mq_curmsgs);   
   
    ret = mq_close(mqd);
    if(ret < 0)
        error_print("mq_close");
         
    return 0;  
} 

編譯執行:

gcc -o setattrmq setattrmq.c -lrt

訊息佇列系統限制檢視:

cat /proc/sys/fs/mqueue/msg_max #檢視訊息佇列的訊息最大長度
cat /proc/sys/fs/mqueue/msgsize_max #檢視訊息佇列的訊息最大個數

相關文章