3 IPC(进程间通信)消息队列
消息队列(Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。
3.1 背景
管道和套接字比较适合两三个进程之间的通信,如果进程成倍增加,管道和套接字的数量也会阶乘级的增加,管理也非常复杂麻烦,于是出现了消息队列。
3.2 POSIX 消息队列
3.3 查看
POSIX
消息队列预览:man mq_overview
- 查看
POSIX
消息队列:ls /dev/mqueue
cat /dev/mqueue/PIC
名字
3.4 接口
头文件:mqueue.h
库:librt.so(real time)
3.5 结构体
struct mq_attr
消息队列属性
成员 | 含义 | 说明 |
---|
mq_flags | 标志 | 在mq_open 时被初始化,在mq_setattr 设置,其值为0 或者O_NONBLOCK |
mq_maxmsg | 队列的消息个数最大值 | 只能在mq_open 时被初始化 |
mq_msgsize | 队列每个消息长度的最大值 | 只能在mq_open 时被初始化 |
mq_curmsgs | 当前队列消息个数 | 在mq_getattr 获取 |
3.6 函数
POSIX 消息队列主要有八个操作
操作 | 函数 |
---|
创建消息队列 | mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr) |
删除消息队列 | int mq_unlink(const char *name) |
打开消息队列 | mqd_t mq_open(const char *name, int oflag) |
关闭消息队列 | int mq_close(mqd_t mqdes) |
发送消息 | 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) |
设置消息队列属性 | int mq_setattr(mqd_t mqdes, struct mq_attr *newattr,struct mq_attr *oldattr) |
获取消息队列属性 | int mq_getattr(mqd_t mqdes, struct mq_attr *attr) |
注:消息队列操作与文件读写操作非常相似
3.6.1 创建消息堆列
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr)
参数 | 含义 |
---|
name | posix IPC 名字,格式为/somename |
oflag | 标志 |
mode | 权限 |
attr | 队列属性,阻塞attr.mq_flag = 0 ;非阻塞attr.mq_flag = NONBLOCK |
标志 | 作用 |
---|
O_CREAT | 没有该对象则创建 |
O_EXCL | 如果O_CREAT 指定,但name 不存在,就返回错误 |
O_NONBLOCK | 以非阻塞方式打开消息队列 |
O_RDONLY | 只读 |
O_RDWR | 读写 |
O_WRONLY | 只写 |
权限 | 作用 |
---|
S_IWUSR | 用户/属主写 |
S_IRUSR | 用户/属主读 |
S_IWGRP | 组成员写 |
S_IRGRP | 组成员读 |
S_IWOTH | 其他用户写 |
S_IROTH | 其他用户读 |
3.6.2 删除消息队列
int mq_unlink(const char *name)
3.6.3 打开消息队列
mqd_t mq_open(const char *name, int oflag)
参数 | 含义 |
---|
name | posix IPC名字 |
oflag | 标志,O_RDONLY 只读;O_RDWR 读写;O_WRONLY 只写 |
3.6.4 关闭消息队列
int mq_close(mqd_t mqdes)
- 注意
mq_close()
和文件的close()
类似,关闭后,消息队列并不从系统中删除。
一个进程结束,会自动关闭打开着的消息队列。
3.6.5 发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned msg_prio)
参数 | 含义 |
---|
msg_ptr | 消息的指针。 |
msg_len | 消息长度,不能大于属性值mq_msgsize 的值 |
msg_prio | 优先级,小于MQ_PRIO_MAX ,数值越大,优先级越高 |
- 注意
消息在队列中将按照优先级大小顺序来排列消息
消息队列已满,mq_send()
函数将阻塞,直到有可用空间再次允许放置消息。
如果O_NONBLOCK
被指定,mq_send()
那么将不会阻塞,而是返回EAGAIN
错误。
3.6.6 接收消息
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 | 优先级,消息在队列中将按照优先级大小顺序来排列消息 |
- 注意
POSIX消息队列在调用mq_receive()
时总是返回队列中最高优先级的最早消息。
如果队列空,mq_receive()
函数将阻塞,直到消息队列中有新的消息。
如果O_NONBLOCK
被指定,mq_receive()
那么将不会阻塞,而是返回EAGAIN
错误。
3.6.7 设置消息队列属性
int mq_setattr(mqd_t mqdes, struct mq_attr *newattr,struct mq_attr *oldattr);
参数 | 含义 |
---|
mqdes | 消息队列描述符 |
newattr | 新属性,只能设置mq_flags:0:NONBLOCK |
oldattr | 旧属性 |
3.6.8 获取消息队列属性
int mq_getattr(mqd_t mqdes, struct mq_attr *attr)
- 注意
mq_setattr()
可以设置的属性只有mq_flags
,用来设置或清除消息队列的非阻塞标志。newattr
结构的其他属性被忽略。
mq_maxmsg
和mq_msgsize
属性只能在创建消息队列时通过mq_open()
来设置。
mq_open()
只会设置该两个属性,忽略另外两个属性。
mq_curmsgs
属性只能被获取而不能被设置。
3.7 实例: 创建POSIX消息队列,实现基本的消息收发
#include <mqueue.h>
#include <cstdio>
#include <iostream>
using namespace std;
int main(int argc,char** argv){
mq_attr attr;
attr.mq_maxmsg = 100;
attr.mq_msgsize = 100;
cout << argv[1] << endl;
mqd_t fd = mq_open(argv[1],O_CREAT,0644,&attr);
if(-1 == fd){
perror("mq_open error");
}
return 0;
}
#include <mqueue.h>
#include <iostream>
using namespace std;
int main(int argc,char** argv){
mqd_t fd = mq_open(argv[1],O_WRONLY);
if(-1 == fd){
perror("mq_open error");
return 1;
}
string msg;
cin >> msg;
mq_send(fd,msg.c_str(),msg.size()+1,0);
mq_close(fd);
}
#include <iostream>
#include <mqueue.h>
using namespace std;
int main(int argc,char** argv){
mqd_t fd = mq_open(argv[1],O_RDONLY);
if(-1 == fd){
perror("mq_open error");
}
char buffer[1024] = {0};
unsigned int p;
mq_receive(fd,buffer,sizeof(buffer),&p);
cout << buffer << endl;
mq_close(fd);
}
步骤:
1)通过该程序创建一个类似通道的交互文件,g++ create_mq.cpp -lrt
。
2)执行./a.out /abc
生成一个名为abc
的通道,目录位于/dev/mqueue
3)执行:./write_mq /abc
发送
4)执行:另一shell窗口./ read_mq /abc
接收
[root@localhost 3]# ./write /abc
a
[root@localhost 3]# ./write /abc
b
[root@localhost 3]# ./write /abc
c
[root@localhost 3]# ./write /abc
1
[root@localhost 3]# ./write /abc
2
[root@localhost 3]# ./write /abc
3
[root@localhost 3]#
...
...
[root@localhost 3]# ./read /abc
a
[root@localhost 3]# ./read /abc
b
[root@localhost 3]#
[root@localhost 3]# ./read /abc
c
[root@localhost 3]# ./read /abc
1
[root@localhost 3]# ./read /abc
2
[root@localhost 3]# ./read /abc
3
[root@localhost 3]#