Posix消息隊列可以認為是一個消息鏈表. 有足夠寫權限的線程可以往隊列中放置消息, 有足夠讀權限的線程可以從隊列中取走消息
在某個進程往一個隊列寫入消息前, 並不需要另外某個進程在該隊列上等待消息的到達.
這跟管道和FIFO是相反的, 因為對於管道,FIFO來說, 除非讀出者已經存在, 光有寫入者是沒有意義的。一個進程在往某消息隊列寫入消息后, 終止進程. 另一個進程某時刻讀出該消息;然而對於管帶或FIFO而言, 當管道或FIFO的最后一次關閉發生時,仍在管道或FIFO中的數據將被拋棄
消息隊列的布局如下圖
下面介紹隊列用到函數:
1 mqd_t mq_open(const char* name, int oflag, mode_t mode, struct mq_attr* attr);
函數打開或者創建一個posix消息隊列。當我們的實際操作是創建一個新的隊列時(即所要創建的隊列不存在, 且oflag中已經指定O_CREAT), mode 和 attr參數是需要的.
函數的返回值稱為消息隊列描述符, 這個值用作其他消息隊列操作函數的第一個參數值。另外根據man 7 mq_overview里面的介紹。在鏈接的時候必須鏈接librt庫,采用-lrt的方法
如果沒有鏈接的話,會出現下面的錯誤
root@zhf-maple:/home/zhf/codeblocks_prj/unix_network# gcc -c main.c -o main_tmp
root@zhf-maple:/home/zhf/codeblocks_prj/unix_network# ./main_tmp test2
bash: ./main_tmp: 無法執行二進制文件: 可執行文件格式錯誤
name:
posix IPC名字。(必須以/開頭,且后面不能再含有/)
oflag:
標志。
標志——————————作用
O_CREAT———————沒有該對象則創建
O_EXCL————————如果O_CREAT指定,但name不存在,就返回錯誤
O_NONBLOCK—————以非阻塞方式打開消息隊列
O_RDONLY———————只讀
O_RDWR————————讀寫
O_WRONLY———————只寫
mode:
權限——————作用
S_IWUSR——用戶/屬主寫
S_IRUSR——用戶/屬主讀
S_IWGRP——組成員寫
S_IRGRP——組成員讀
S_IWOTH——其他用戶寫
S_IROTH——其他用戶讀
attr:結構體如下:
struct mq_attr {
long mq_flags; /* Flags: 0 or O_NONBLOCK */
————在mq_open創建時被初始化;
————在mq_setattr中設置;
————其值為0(阻塞)或者O_NONBLOCK(非阻塞)。
long mq_maxmsg; /* Max. # of messages on queue */
——隊列的消息個數最大值:
————只能在mq_open創建時被初始化。
long mq_msgsize; /* Max. message size (bytes) */
——隊列中每個消息的最大值:
————只能在mq_open創建時被初始化。
long mq_curmsgs; /* # of messages currently in queue */
——當前隊列的消息個數:
————在mq_getattr中獲得。
};
2 int mq_close(mqd_t mqdes):關閉消息隊列
關閉之后調用進程不在使用該描述符,但消息隊列不會從系統中刪除,進程終止時,會自動關閉已打開的消息隊列,和調用mq_close一樣。參數為mq_open()函數返回的值
3 int mq_unlink(const char *name): 從系統中刪除某個消息隊列
刪除會馬上發生,即使該隊列的描述符引用計數仍然大於0。參數為mq_open()函數第一個參數。
4設置和獲取隊列屬性
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
參數mqdes為mq_open()函數返回的消息隊列描述符。
參數attr、newattr、oldattr為消息隊列屬性結構體指針;
5向消息隊列放置和取走消息
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
參數msg_ptr為指向消息的指針。
msg_len為消息長度,該值不能大於屬性值中mq_msgsize的值。
msg_prio為優先級,消息在隊列中將按照優先級大小順序來排列消息。
如果消息隊列已滿,mq_send()函數將阻塞,直到隊列有可用空間再次允許放置消息或該調用被信號打斷;如果O_NONBLOCK被指定,mq_send()那么將不會阻塞,而是返回EAGAIN錯誤。如果隊列空,mq_receive()函數將阻塞,直到消息隊列中有新的消息;如果O_NONBLOCK被指定,mq_receive()那么將不會阻塞,而是返回EAGAIN錯誤。
下面就來看第一個例子:
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <mqueue.h>
#include <fcntl.h>
#include "func.h"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main(int argc,char **argv)
{
int c,flags;
mqd_t mqd;
struct mq_attr attr;
flags = O_RDWR|O_CREAT;
printf("create mqueue.\n");
if((mqd = mq_open(argv[1],flags,FILE_MODE,NULL)) == -1)
{
perror("mq_open() error");
exit(-1);
}
mq_getattr(mqd,&attr);
printf("max #msgs = %ld,max #bytes/msg = %ld,#currently on queue = %ld\n",attr.mq_maxmsg,attr.mq_msgsize,attr.mq_curmsgs);
mq_close(mqd);
exit(0);
}
在執行代碼前,首先要創建消息隊列存放的路徑。一般是/dev/mqueue。沒有的話需要創建一個。然后執行mount -t mqueue none /dev/mqueue進行掛載。參考說明如下
gcc main.c -o main_tmp -lrt
./main_tmp /test123
執行完后,在dev/mqueue路徑下面就能看到創建的隊列文件以及隊列里面的信息。
cat /dev/mqueue/test123
QSIZE:0 NOTIFY:0 SIGNO:0 NOTIFY_PID:0