linux进程间通信:POSIX 消息队列
文章目錄
- 基本介紹
- 相關編程接口
- 編程實例
- 消息隊列通信實例
- 消息隊列屬性設置實例
基本介紹
關于消息隊列的基本介紹,前面在學習system V的消息隊列時已經有過了解,linux進程間通信:system V消息隊列
- 支持不同進程之間以消息(messages)的形式進行數據交換,消息能夠擁有自己的標識,且內核使用鏈表方式進行消息管理。
- 進程之間的通信角色為:發送者和接受者
發送者:
a. 獲取消息隊列的ID(key或者msgid)
b. 將數據放入一個帶有標識的消息結構體,發送到消息隊列
接受者:
a. 獲取消息隊列的ID
b. 指定標識的消息從消息隊列中讀出,然后進一步后續處理 - 支持不同的進程標記不同的消息優先級(0,1,2…),并由內核態維護對應消息類型的鏈表。
- 內核態的0號消息類型維護了一個鏈表,用來保存按照時間順序加入的消息
相關編程接口
mq_open:創建或打開一個消息隊列mq_send:向消息隊列寫入一條消息mq_receive:從消息隊列中讀取一條消息mq_close:關閉進程打開的消息隊列mq_unlink:刪除一個消息隊列mq_setattr:設置消息隊列的一些額外屬性mq_getattr:獲取消息隊列的一些額外屬性mq_notify:異步通知
-
創建或打開一個消息隊列
a. 頭文件<mqueue.h> <sys/stat.h> <fcntl.h>
b. 函數使用:
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);
c. 函數功能:創建一個新的POSIX消息隊列或者打開一個已存在的消息隊列。且消息隊列是由name標識
d. 函數參數:name用來標識需要創建或者打開的ipc對象oflagO_CREAT/O_RDONLY/O_WRONLY/O_RDWR/O_EXCL/O_NOBLOCK
標記使用什么樣的方式打開ipc對象mode位掩碼,用來設置權限 8進制attr設置消息隊列的屬性,若為NULL,使用默認屬性,linux 3.5以后版本也可以通過/proc查看設置
主要數據結構如下:struct mq_attr {long mq_flags; /* Flags (ignored for mq_open()) */long mq_maxmsg; /* Max. # of messages on queue */long mq_msgsize; /* Max. message size (bytes) */long mq_curmsgs; /* # of messages currently in queue(ignored for mq_open()) */ };
e. 函數返回值
成功:返回消息隊列對象的描述符
失敗:返回-1,并設置errno -
關閉一個消息隊列
a. 頭文件 :#include <mqueue.h>
b. 函數使用:int mq_close(mqd_t mqdes);
c. 函數功能:關閉一個描述符為mqdes的消息隊列
d. 返回值:
成功:返回0
失敗:返回-1
e. 注意:POSIX消息隊列在進程終止或者執行execve()時會自動關閉 -
刪除消息隊列
a. 頭文件:#include <mqueue.h>
b. 函數使用:int mq_unlink(const char *name);
c. 函數功能:刪除通過name標識的消息隊列;在所有進程使用完該隊列之后銷毀該隊列;若打開該隊列的所有進程都已關閉,則立即刪除
d. 返回值:
成功:0
失敗:-1 -
向消息隊列中寫入消息
a. 頭文件:#include <mqueue.h>
b. 函數使用:int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
c. 函數功能:將msg_ptr所指向的緩沖區中的消息內容添加到描述符mqdes所引用的消息隊列中
d. 函數參數:mqdes:消息隊列描述符msg_ptr: 指向存放消息的緩沖區指針msg_len: 消息的長度[10,8192]msg_prio: 消息隊列中按優先級排序,設置為0標識無需優先級;該參數為POSIX消息隊列和system V消息隊列的差異,即POSIX是通過優先級來區分消息,system V是通過消息類型來區分消息
e. 返回值
成功:0
失敗:-1 -
從消息隊列中接收數據
a. 頭文件:#include <mqueue.h>
b. 函數使用:ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
c. 函數功能:默認從mqdes引用的消息隊列中刪除一條優先級最高、存放時間最長的消息;并將刪除的消息保存在msg_ptr;
這里如果接收時指定優先級,可以讀取指定優先級的消息,并存放在msg_ptr
d. 函數參數:mqdes:消息隊列描述符msg_ptr: 指向存放消息的緩沖區指針msg_len: 消息的長度[10,8192]msg_prio: 消息隊列中按優先級排序,設置為0標識無需優先級;
e. 返回值
成功:消息接收的字節數
失敗:-1 -
獲取/設置消息隊列的屬性信息
a. 頭文件#include <mqueue.h>
b. 函數使用:
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
c. 函數功能:獲取或者修改一個由mqdes標識的消息隊列的屬性信息
d. 函數參數:mqdes消息隊列的唯一標識newattr設置修改后的mq_attr結構體類型的屬性信息oldattr修改前的屬性信息,如果當前參數為NULL,則默認設置從mq_getattr中獲取到的屬性信息
關于消息隊列的屬性設置如下兩種方式:
- 通過proc文件系統
echo num > /proc/sys/fs/mqueue/queues_max
echo num > /proc/sys/fs/mqueue/msg_max
echo num > /proc/sys/fs/mqueue/msgsize_max - 通過POSIX系統調用接口設置
mq_setattr僅能設置mq_flags
mq_open僅設置mq_maxmsg和mq_msgsize
編程實例
最終創建的POSIX消息隊列的實例存放在/dev/mqueue路徑下
消息隊列通信實例
mq_demo.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>#include <mqueue.h>int main()
{mqd_t mq_id;//創建一個消息隊列if ((mq_id = mq_open("/posix_msgqueue",O_RDWR | O_CREAT, 0666, NULL)) == -1) {printf("mq_open failed \n");_exit(-1);}struct mq_attr mq;//獲取消息隊列的各個屬性if (mq_getattr(mq_id ,&mq) == -1 ){_exit(-1);}printf("mq_flags %ld\n",mq.mq_flags); //mq_open默認將當前屬性忽略,則一般為0printf("mq_maxmsg %ld\n",mq.mq_maxmsg); //消息隊列可以接收的最大消息容量為10個,當達到10個,則當前進程阻塞printf("mq_msgsize %ld\n",mq.mq_msgsize); //每個消最大容量printf("mq_curmsgs %ld\n",mq.mq_curmsgs);int ret;if ((ret = fork()) == -1) {printf("fork failed \n");_exit(-1);}//創建子進程用來接收消息隊列的信息,且接收的優先級為NULL,即默認接收優先級最高且存放時間最長的消息else if (ret == 0 ) {char msg_buf[mq.mq_msgsize];memset (msg_buf , 0 , mq.mq_msgsize);while(1) {if (mq_receive(mq_id,msg_buf,mq.mq_msgsize,NULL) == -1) {printf("mq_receive failed \n");_exit(-1);}printf("child process receive msg :%s \n",msg_buf);sleep(1);}}else {while(1) {//父進程用來發送消息隊列的信息,并設置發送的消息優先級為1//如果父進程發送多個優先級消息,子進程指定具體的一個優先級消息進行接收即可if (mq_send(mq_id,"hello world",sizeof("hello world"),1) == -1) {printf("mq_send failed\n");_exit(-1);}printf("parent process :send msg to mqueue success\n");sleep(1);}}//關閉當前進程對消息隊列 mq_id的引用mq_close(mq_id);sleep(5);//引用計數為0時,刪除當前進程創建的消息隊列if(mq_unlink("/posix_msgqueue") == -1) {printf("mq_unlink failed\n");_exit(-1);}return 0;
}
輸出如下:
消息隊列屬性設置實例
我們知道如下三個參數,POSIX消息隊列默認的屬性值為
mq_flags = 0
mq_maxmsg = 10
mq_msgsize = 8192
通過如下代碼可以分別看到mq_open和mq_setattr對屬性值的設置
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <string.h>int main () {mqd_t mq_id;/*使用mq_open同時對三個參數更改*/struct mq_attr addr;addr.mq_flags = O_NONBLOCK;addr.mq_maxmsg = 5;addr.mq_msgsize = 4096;//將需要設置對屬性參數設置為addrmq_id = mq_open("/notify",O_WRONLY | O_CREAT, 0666, &addr);if (mq_id == -1) {printf("mq_open failed\n"); _exit(-1);}if (mq_getattr(mq_id,&addr) == -1) {printf("mq_getattr failed\n");_exit(-1);}//打印最終對設置結果printf("mq_open set mq_flags = %ld\n",addr.mq_flags);printf("mq_open set mq_maxmsg = %ld\n", addr.mq_maxmsg);printf("mq_open set mq_msgsize = %ld\n", addr.mq_msgsize);/*同樣對所有的屬性參數,使用mq_setattr進行設置*/struct mq_attr attr;attr.mq_flags = O_NONBLOCK;attr.mq_maxmsg = 8;attr.mq_msgsize = 2048;if(mq_setattr(mq_id,&attr,NULL) == -1) {printf("mq_set attr failed \n");_exit(-1);}if (mq_getattr(mq_id,&attr) == -1) {printf("mq_getattr failed\n");_exit(-1);}printf("mq_setattr set mq_flags = %ld\n",attr.mq_flags);printf("mq_setattr set mq_maxmsg = %ld\n", attr.mq_maxmsg);printf("mq_setattr set mq_msgsize = %ld\n", attr.mq_msgsize);return 0;
}
輸出結果如下:
mq_open set mq_flags = 0
mq_open set mq_maxmsg = 5
mq_open set mq_msgsize = 4096
mq_setattr set mq_flags = 2048
mq_setattr set mq_maxmsg = 5
mq_setattr set mq_msgsize = 4096
可見,mq_flags參數只有mq_setattr系統調用能夠成功設置
mq_maxmsg和mq_msgsize對屬性僅能由mq_opn系統調用設置成功
總結
以上是生活随笔為你收集整理的linux进程间通信:POSIX 消息队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个女生qq网名伤感霸气
- 下一篇: linux进程间通信:POSIX 消息队