Linux IPC实践(7) --Posix消息队列
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);
参数:
name: 消息队列名字;
oflag: 与open函数类型, 可以是O_RDONLY, O_WRONLY, O_RDWR, 还可以按位或上O_CREAT, O_EXCL, O_NONBLOCK.
mode: 如果oflag指定了O_CREAT, 需要指定mode参数;
attr: 指定消息队列的属性;
返回值:
成功: 返回消息队列文件描述符;
失败: 返回-1;
注意-Posix IPC名字限制:
1. 必须以”/”开头, 并且后面不能还有”/”, 形如:/file-name;
2. 名字长度不能超过NAME_MAX
3. 链接时:Link with -lrt.
/** System V 消息队列
通过msgget来创建/打开消息队列
int msgget(key_t key, int msgflg);
**/
2. 关闭一个消息队列
int mq_close(mqd_t mqdes);
/** System V 消息队列没有类似的该函数调用**/
3. 删除一个消息队列
int mq_unlink(const char *name);
/** System V 消息队列
通过msgctl函数, 并将cmd指定为IPC_RMID来实现
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
**/
//示例
int main()
{mqd_t mqid = mq_open("/abc", O_CREAT|O_RDONLY, 0666, NULL);if (mqid == -1)err_exit("mq_open error");cout << "mq_open success" << endl;mq_close(mqid);mq_unlink("/abc");cout << "unlink success" << endl;
}
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);
参数:
newattr: 需要设置的属性
oldattr: 原来的属性
//struct mq_attr结构体说明
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: 消息队列当前保存的消息数 */
};
/** System V 消息队列
通过msgctl函数, 并将cmd指定为IPC_STAT/IPC_SET来实现
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
**/
/** 示例: 获取消息队列的属性
**/
int main(int argc,char **argv)
{mqd_t mqid = mq_open("/test", O_RDONLY|O_CREAT, 0666, NULL);if (mqid == -1)err_exit("mq_open error");struct mq_attr attr;if (mq_getattr(mqid, &attr) == -1)err_exit("mq_getattr error");cout << "Max messages on queue: " << attr.mq_maxmsg << endl;cout << "Max message size: " << attr.mq_msgsize << endl;cout << "current messages: " << attr.mq_curmsgs << endl;mq_close(mqid);return 0;
}
5. 发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned msg_prio);
参数:
msg_ptr: 指向需要发送的消息的指针
msg_len: 消息长度
msg_prio: 消息的优先级
/** System V 消息队列
通过msgsnd函数来实现消息发送
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
**/
/** 示例: 向消息队列中发送消息, prio需要从命令行参数中读取 **/
struct Student
{char name[36];int age;
};
int main(int argc,char **argv)
{if (argc != 2)err_quit("./send <prio>");mqd_t mqid = mq_open("/test", O_WRONLY|O_CREAT, 0666, NULL);if (mqid == -1)err_exit("mq_open error");struct Student stu = {"xiaofang", 23};unsigned prio = atoi(argv[1]);if (mq_send(mqid, (const char *)&stu, sizeof(stu), prio) == -1)err_exit("mq_send error");mq_close(mqid);return 0;
}
6. 从消息队列中读取消息
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,size_t msg_len, unsigned *msg_prio);
参数:
msg_len: 读取的消息的长度, 注意: 此值一定要等于mq_attr::mq_msgsize的值, 该值可以通过mq_getattr获取, 但一般是8192字节 [this must be greater than the mq_msgsize attribute of the queue (see mq_getattr(3)).]
msg_prio: 保存获取的消息的优先级
返回值:
成功: 返回读取的消息的字节数
失败: 返回-1
注意: 读取的永远是消息队列中优先级最高的最早的消息, 如果消息队列为, 如果不指定为非阻塞模式, 则mq_receive会阻塞;
/** System V 消息队列
通过msgrcv函数来实现消息发送的
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
**/
/** 示例: 从消息队列中获取消息 **/
int main(int argc,char **argv)
{mqd_t mqid = mq_open("/test", O_RDONLY);if (mqid == -1)err_exit("mq_open error");struct Student buf;int nrcv;unsigned prio;struct mq_attr attr;if (mq_getattr(mqid, &attr) == -1)err_exit("mq_getattr error");if ((nrcv = mq_receive(mqid, (char *)&buf, attr.mq_msgsize, &prio)) == -1)err_exit("mq_receive error");cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "<< buf.name << ", age: " << buf.age << endl;mq_close(mqid);return 0;
}
7. 建立/删除消息到达通知事件
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
参数sevp:
NULL: 表示撤销已注册通知;
非空: 表示当消息到达且消息队列当前为空, 那么将得到通知;
通知方式:
1. 产生一个信号, 需要自己绑定
2. 创建一个线程, 执行指定的函数
注意: 这种注册的方式只是在消息队列从空到非空时才产生消息通知事件, 而且这种注册方式是一次性的!
//sigevent结构体
struct sigevent
{int sigev_notify; /* Notification method */int sigev_signo; /* Notification signal */union sigval sigev_value; /* Data passed with notification */void (*sigev_notify_function) (union sigval); /* Function used for thread notification (SIGEV_THREAD) */void *sigev_notify_attributes; /* Attributes for notification thread (SIGEV_THREAD) */pid_t sigev_notify_thread_id; /* ID of thread to signal (SIGEV_THREAD_ID) */
};
union sigval /* Data passed with notification */
{int sival_int; /* Integer value */void *sival_ptr; /* Pointer value */
};
sigev_notify代表通知的方式: 一般常用两种取值:SIGEV_SIGNAL, 以信号方式通知; SIGEV_THREAD, 以线程方式通知
如果以信号方式通知: 则需要设定一下两个参数:
sigev_signo: 信号的代码
sigev_value: 信号的附加数据(实时信号)
如果以线程方式通知: 则需要设定以下两个参数:
sigev_notify_function
sigev_notify_attributes
/** Posix IPC所特有的功能, System V没有 **/
/**示例: 将下面程序多运行几遍, 尤其是当消息队列”从空->非空”, 多次”从空->非空”, 当消息队列不空时运行该程序时, 观察该程序的状态;
**/
mqd_t mqid;
long size;
void sigHandlerForUSR1(int signo)
{//将数据的读取转移到对信号SIGUSR1的响应函数中来struct Student buf;int nrcv;unsigned prio;if ((nrcv = mq_receive(mqid, (char *)&buf, size, &prio)) == -1)err_exit("mq_receive error");cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "<< buf.name << ", age: " << buf.age << endl;
}int main(int argc,char **argv)
{// 安装信号响应函数if (signal(SIGUSR1, sigHandlerForUSR1) == SIG_ERR)err_exit("signal error");mqid = mq_open("/test", O_RDONLY);if (mqid == -1)err_exit("mq_open error");// 获取消息的最大长度struct mq_attr attr;if (mq_getattr(mqid, &attr) == -1)err_exit("mq_getattr error");size = attr.mq_msgsize;// 注册消息到达通知事件struct sigevent event;event.sigev_notify = SIGEV_SIGNAL; //指定以信号方式通知event.sigev_signo = SIGUSR1; //指定以SIGUSR1通知if (mq_notify(mqid, &event) == -1)err_exit("mq_notify error");//死循环, 等待信号到来while (true)pause();mq_close(mqid);return 0;
}
/** 示例:多次注册notify, 这样就能过多次接收消息, 但是还是不能从队列非空的时候进行接收, 将程序改造如下:
**/
mqd_t mqid;
long size;
struct sigevent event;
void sigHandlerForUSR1(int signo)
{// 注意: 是在消息被读走之前进行注册,// 不然该程序就感应不到消息队列"从空->非空"的一个过程变化了if (mq_notify(mqid, &event) == -1)err_exit("mq_notify error");//将数据的读取转移到对信号SIGUSR1的响应函数中来struct Student buf;int nrcv;unsigned prio;if ((nrcv = mq_receive(mqid, (char *)&buf, size, &prio)) == -1)err_exit("mq_receive error");cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "<< buf.name << ", age: " << buf.age << endl;
}int main(int argc,char **argv)
{// 安装信号响应函数if (signal(SIGUSR1, sigHandlerForUSR1) == SIG_ERR)err_exit("signal error");mqid = mq_open("/test", O_RDONLY);if (mqid == -1)err_exit("mq_open error");// 获取消息的最大长度struct mq_attr attr;if (mq_getattr(mqid, &attr) == -1)err_exit("mq_getattr error");size = attr.mq_msgsize;// 注册消息到达通知事件event.sigev_notify = SIGEV_SIGNAL; //指定以信号方式通知event.sigev_signo = SIGUSR1; //指定以SIGUSR1通知if (mq_notify(mqid, &event) == -1)err_exit("mq_notify error");//死循环, 等待信号到来while (true)pause();mq_close(mqid);return 0;
}
mq_notify 注意点总结:
1. 任何时刻只能有一个进程可以被注册为接收某个给定队列的通知;
2. 当有一个消息到达某个先前为空的队列, 而且已有一个进程被注册为接收该队列的通知时, 只有没有任何线程阻塞在该队列的mq_receive调用的前提下, 通知才会发出;
3. 当通知被发送给它的注册进程时, 该进程的注册被撤销. 进程必须再次调用mq_notify以重新注册(如果需要的话),但是要注意: 重新注册要放在从消息队列读出消息之前而不是之后(如同示例程序);
附-查看已经成功创建的Posix消息队列
#其存在与一个虚拟文件系统中, 需要将其挂载到系统中才能查看
Mounting the message queue filesystem On Linux, message queues are created in a virtual filesystem.
(Other implementations may also provide such a feature, but the details are likely to differ.) This
file system can be mounted (by the superuser, 注意是使用root用户才能成功) using the following commands:
mkdir /dev/mqueue
mount -t mqueue none /dev/mqueue
还可以使用cat查看该消息队列的状态, rm删除:
cat /dev/mqueue/abc
rm abc
还可umount该文件系统
umount /dev/mqueue
附-Makefile
.PHONY: clean all
CC = g++
CPPFLAGS = -Wall -g
BIN = main
SOURCES = $(BIN.=.cpp)
all: $(BIN)%.o: %.c$(CC) $(CPPFLAGS) -c $^ -o $@
main: main.o$(CC) $(CPPFLAGS) $^ -lrt -o $@clean:-rm -rf $(BIN) *.o bin/ obj/ core
Linux IPC实践(7) --Posix消息队列相关推荐
- Linux IPC实践(10) --Posix共享内存
1. 创建/获取一个共享内存 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #inc ...
- POSIX 消息队列相关问题
一.查看和删除消息队列 要想看到创建的posix消息队列,需要在root用户下执行以下操作: # mkdir /dev/mqueue # mount -t mqueue none /dev/mqueu ...
- Linux IPC实践(4) --System V消息队列(1)
消息队列概述 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法(仅局限于本机); 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值. 消息队列也有管道一样的不足: ...
- Linux IPC POSIX 消息队列
模型: #include<mqueue.h> #include <sys/stat.h> #include <fcntl.h> mq_open() //创建/获取消 ...
- linux进程间通信:POSIX 消息队列
文章目录 基本介绍 相关编程接口 编程实例 消息队列通信实例 消息队列属性设置实例 基本介绍 关于消息队列的基本介绍,前面在学习system V的消息队列时已经有过了解,linux进程间通信:syst ...
- IPC通信:Posix消息队列的属性设置
IPC通信:Posix消息队列的属性设置 Posix消息队列的属性使用如下结构存放: struct mq_attr { long mq_flags; /*阻塞标志位,0为非阻塞(O_NONBLOCK) ...
- linux进程间通信:POSIX 消息队列 ----异步通信
在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞. 阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情, ...
- Linux进程间通信四 Posix 消息队列简介与示例
目录 1. Posix 消息队列简介 2. API接口 2.1 创建或打开消息队列 2.2 发送消息 2.3 接收消息 2.4 获取.设置消息队列属性 2.5 关闭消息队列 2.6 删除消息队列 2. ...
- Linux IPC实践(8) --共享内存/内存映射
概述 共享内存区是最快的IPC形式.一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图). 共享内存 VS ...
最新文章
- Alphabet旗下自驾公司Waymo入华,变身“慧摩”!
- MySQL Packet for query is too large
- cgi web 调用多次启动_CGI、FastCGI和PHPFPM有什么关系呢?
- JSP简单练习-javaBean的简单应用
- 跳跃游戏—leetcode55
- Android开发之Handler机制记录
- debian 访问 windows 共享_【续】windows环境redis未授权利用方式梳理
- axios (get post请求、头部参数添加)傻瓜式入门axios
- python使用opencv_教你快速使用OpenCV/Python/dlib進行眨眼檢測識別!
- 双刃剃须刀行业调研报告 - 市场现状分析与发展前景预测
- ecshop始终显示全部分类
- jfinal 普通java工程_JFinal getModel方法如何在java项目中使用
- Java——用户激活邮件工具类
- 读 Irving M. Copi 之《逻辑学导论》
- 快速查看本机公网IP地址
- win7 计算机定时关机脚本,Win7定时关机命令 win7系统怎么设置定时关机
- (抖音快手)三种代码生成炫酷代码雨!!!
- 【转】艺术设计、数字媒体、环艺、影视动画、摄影、广编专业…等…视频、教程、资讯、图库、作品汇总大全
- loop variable ‘numerator‘ creates a copy from type ‘const std::string‘ [-Wrange-loop-construct]
- kubernetes国内镜像代理
热门文章
- 基础编程题之奇数位(偶数位)都是奇数(偶数)
- 9-5:C++多态之多态和继承的经典题目以及面试中常考内容
- linux 下进程和线程指定CPU运行
- Redis的发布订阅(消息队列,比如ActiveMQ,一方得到数据后,多方得到信息)
- 一款回到顶部的 jQuery 插件,支持 Div 中的滚动条回到顶部
- 069 Sqrt(x) 求平方根
- [转]Eclipse插件开发之基础篇(2) 第一个Eclipse插件
- 终于有人把平均数、中位数和众数讲明白了
- 2021年,AI哪个方向最火?看看大神们怎么说
- 戳破“砖家”假面:唯快不破的时代,为什么这件事一定要慢慢做?