目錄
1. Posix 消息隊列簡介
Posix 消息隊列是基於文件描述符的,因此可以使用諸如select、poll和epoll等IO多路複用方式,這應該是最大的優點,此外,它還是一個優先級隊列,優先級最高的消息會先出隊列。從我的開發經驗來看,實際項目中並不推薦使用系統提供的消息隊列,主要原因是限制太多,比如說Posix 消息隊列,系統範圍內能夠創建的消息隊列個數是優先的,此外,消息的大小和消息隊列的容量也受限,還有一點是僅支持優先級,不支持FIFO,這一點SystemV消息隊列是支持的。雖然不怎麼使用,但是還是需要學習一下它的基本用法,這樣後續需要自己設計的時候可以參考下。
Posix mq可以使用mq_open來創建或者獲取,該函數返回消息隊列描述符mqd_t,每個消息隊列都有一個名字,以斜槓'/'開頭,長度不超過NAME_MAX,且後面不能再有斜槓。不同的進程可以通過同一個名字來操作同一個消息隊列。消息通過mq_send和mq_receive來接收和發送,當不需要消息隊列的時候,使用mq_close關閉它,關閉不代表刪除,消息隊列具有系統生存期,可以使用mq_unlink來刪除它。對了,Posix mq還提供一個異步接收消息的特性,使用mq_notify函數,該接口向消息隊列註冊或註銷一個通知,希望有消息的時候能夠通知它,通知方式有兩種,一種是通過信號,一種是通過一個線程。這裏限制比較多,首先,消息隊列只能註冊一個通知,其次,通知是一次性的,通知完如果還想繼續通知需要重新註冊,還有一點是如果消息被其它進程通過mq_receive取到了,通知不會發生。我感覺這是一個非常雞肋的功能,O(∩_∩)O哈哈~
下面介紹下相關的接口和使用例子
2. API接口
2.1 創建或打開消息隊列
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
/**
* @brief 創建或獲取消息隊列
*
* @params name 消息隊列關聯的名字,斜槓開頭,長度不超過NAME_MAX(e.g. 255)
* @params oflag 標誌位,可以O_RDONLY| O_WRONLY| O_RDWR| O_CLOEXEC| O_CREAT| O_EXCL| O_NONBLOCK
* 這裏如果指定了O_CREAT標誌位,還要填寫額外兩個參數,mode和attr
*
* @params mode,參考open函數,通常填0即可
* @params attr,設置一些屬性
* struct mq_attr {
* long mq_flags; /* 不用填 */
* long mq_maxmsg; /* Max. # 消息隊列中消息最大個數 */
* long mq_msgsize; /* Max. message size (bytes) 最大容量 */
* long mq_curmsgs; /* 不用填寫 */
* };
* @returns 成功返回描述符,失敗返回-1
*/
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);
# 需要加上編譯選項-lrt
* Link with -lrt.
2.2 發送消息
#include <mqueue.h>
/**
* @brief 發送消息,如果隊列滿了並且沒有設置NONBLOCK標誌位會阻塞
*
* @params mqdes 消息隊列描述符
* @params msg_ptr 要發送的消息buffer
* @params msg_len 消息大小
* @params msg_prio 消息優先級
*
* @returns 成功返回0,失敗返回-1
**/
int mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio);
#include <time.h>
#include <mqueue.h>
/**
* @brief 同上,多了一個時間參數,用於控制在隊列滿了的情況下,等待多長時間
*
* @params abs_timeout 時間
* @returns 成功返回0,失敗返回-1
**/
int mq_timedsend(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio,
const struct timespec *abs_timeout);
# 編輯要加上 -lrt選項
Link with -lrt.
2.3 接收消息
#include <mqueue.h>
/**
* @brief 接收消息,無消息時默認阻塞,可以設置不阻塞標誌位更改
*
* @params mqdes 消息隊列描述符
* @params msg_ptr 消息緩存buffer
* @params msg_len 消息緩存大小,必須大於mq_msgsize,這個值可以通過mq_getattr接口獲得
* @params msg_prio 如果設置了則返回消息的優先級
* @returns 成功返回消息大小,失敗返回-1
**/
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
#include <time.h>
#include <mqueue.h>
/**
* @brief 功能同上,只不過多了一個阻塞時間
**/
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio,
const struct timespec *abs_timeout);
2.4 獲取、設置消息隊列屬性
#include <mqueue.h>
/**
* @brief 獲取消息隊列屬性
*
* @params mqdes 消息隊列描述符
* @params attr 屬性buffer
struct mq_attr {
long mq_flags; /* Flags: 0 or O_NONBLOCK */
long mq_maxmsg; /* Max. 最大消息個數 */
long mq_msgsize; /* Max. 消息隊列最大容量 */
long mq_curmsgs; /* 當前隊列消息數 */
};
* @returns 成功返回0,失敗返回-1
**/
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
/**
* @brief 設置消息隊列屬性
*
* @params mqdes 消息隊列描述符
* @params attr 屬性buffer, 這裏實際上只能設置attr裏面的mq_flags標誌位
* @returns 成功返回0,失敗返回-1
**/
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr,
struct mq_attr *oldattr);
# 編譯鏈接選項 -lrt
Link with -lrt.
2.5 關閉消息隊列
#include <mqueue.h>
/**
* @brief 關閉消息隊列,並不實際刪除消息隊列
*
* @params mqdes 消息隊列描述符
* @returns 成功返回0,失敗返回-1
**/
int mq_close(mqd_t mqdes);
2.6 刪除消息隊列
#include <mqueue.h>
/**
* @brief 將消息隊列從系統中刪除,實際的銷燬要等到所有進程關閉消息隊列描述符才發生
*
* @params name 消息隊列名字
* @returns 成功返回0,失敗返回-1
**/
int mq_unlink(const char *name);
2.7 註冊消息通知
#include <mqueue.h>
/**
* @brief 註冊或註銷一個消息通知,當收到消息的時候根據指定的方式進行回調,可以是信號也可以執行一個線程。僅執行一次,需要反覆通知則每次都要重新註冊,最多允許一個進程註冊。只有消息隊列從空變爲非空並且沒有其它進程等待消息的時候,才觸發通知。
*
* @params mqdes 消息隊列名字
* @params sevp, 爲空表示註銷,非空指定回調方式,或是信號或者是線程
* @returns 成功返回0,失敗返回-1
**/
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
3. 示例
3.1 消息生產者
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define MQ_NAME "/mq_0"
struct smessage{
char name[128];
char content[1024];
};
int main(int argc, char** argv)
{
int ret = 0;
if (argc < 4)
{
printf("Usage: ./mq_write name message prio\n");
return 0;
}
mqd_t mq_fd;
// 打開或創建消息隊列,如果指定C_CREAT標誌位則需要提供額外兩個參數
mq_fd = mq_open(MQ_NAME, O_RDWR|O_CREAT, 0, NULL);
if (mq_fd == (mqd_t)-1)
{
perror(__FUNCTION__);
return -1;
}
struct smessage msg;
memset(&msg, 0, sizeof(msg));
strncpy(msg.name, argv[1], strlen(argv[1]));
strncpy(msg.content, argv[2], strlen(argv[2]));
unsigned int prio = (unsigned int)atoi(argv[3]);
// 發送消息
ret = mq_send(mq_fd, (const char *)&msg, sizeof(msg), prio);
if (ret == -1)
{
perror("mq_send error");
return ret;
}
printf("mq_send succ\n");
return 0;
}
3.2 消息消費者
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
// 消息隊列名字
#define MQ_NAME "/mq_0"
struct smessage{
char name[128];
char content[1024];
};
int main()
{
mqd_t mq_fd;
// 打開或創建消息隊列,如果指定O_CREAT,則需要填充額外兩個參數
mq_fd = mq_open(MQ_NAME, O_RDWR|O_CREAT, 0, NULL);
if (mq_fd == (mqd_t)-1)
{
perror(__FUNCTION__);
return -1;
}
// 獲取消息隊列屬性,讀取消息的時候要用到
struct mq_attr attr;
if (mq_getattr(mq_fd, &attr) == -1)
{
perror("mq_getattr");
return -1;
}
printf("mq_msgsize:%ld\n", attr.mq_msgsize);
// 分配消息緩存
char *buffer = malloc(attr.mq_msgsize);
if (buffer == NULL)
{
printf("malloc error\n");
return -1;
}
memset(buffer, 0, attr.mq_msgsize);
struct smessage *msg;
unsigned int prio = 0;
// 讀取消息
if (mq_receive(mq_fd, buffer,attr.mq_msgsize, &prio) != -1)
{
msg = (struct smessage*)buffer;
printf("recv msg, name:%s, content:%s, prio:%lu\n", msg->name, msg->content, prio);
}
else
{
perror("mq_receive error");
}
// 釋放buffer
free(buffer);
// 關閉消息隊列
mq_close(mq_fd);
return 0;
}
3.3 編譯運行
# Makefile
default:
gcc -o mq_read mq_read.c -lrt
gcc -o mq_write mq_write.c -lrt
clean:
rm -rf mq_read mq_write
生產三條優先級不同的消息,可以看到讀取的時候是按照優先級來讀取的。
4. 通過文件系統操作消息隊列
# mkdir /dev/mqueue
# mount -t mqueue none /dev/mqueue
5. 參考文檔
5.1. https://www.man7.org/linux/man-pages/man7/mq_overview.7.html
================================================================================================
Linux應用程序、內核、驅動、後臺開發交流討論羣(745510310),感興趣的同學可以加羣討論、交流、資料查找等,前進的道路上,你不是一個人奧^_^。...