Linux进程间通信(IPC)编程实践(十二)Posix消息队列--基本API的使用
posix消息队列与system v消息队列的区别:
(1)对posix消息队列的读总是返回最高优先级的最早消息,对system v消息队列的读则能够返回随意指定优先级的消息。
(2)当往一个空队列放置一个消息时,posix消息队列同意产生一个信号或启动一个线程,system v消息队列则不提供类似机制。
队列中的每一个消息具有例如以下属性:
1、一个无符号整数优先级(posix)或一个长整数类型(system v)
2、消息的数据部分长度(能够为0)
3、数据本身(假设长度大于0)
Posix消息队列操作函数例如以下:
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);
參数:
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(Makefile中使用实时链接库-lrt)
2. 关闭一个消息队列
#include <mqueue.h>
int mq_close(mqd_t mqdes);
返回: 成功时为0,出错时为-1。
功能: 关闭已打开的消息队列。
注意: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);
**/
返回: 成功时为0,出错时为-1
功能: 从系统中删除消息队列。
对上述三个函数的综合使用:
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. 获取/设置消息队列属性
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *attr);
均返回:成功时为0, 出错时为-1
參数:
newattr: 须要设置的属性
oldattr: 原来的属性
每一个消息队列有四个属性:
struct mq_attr
{
long mq_flags; /* message queue flag : 0, O_NONBLOCK */
long mq_maxmsg; /* max number of messages allowed on queue*/
long mq_msgsize; /* max size of a message (in bytes)*/
long mq_curmsgs; /* number of messages currently on queue */
};
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;
}
对照System V:
通过msgctl函数, 并将cmd指定为IPC_STAT/IPC_SET来实现
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
另外每一个消息均有一个优先级,它是一个小于MQ_PRIO_MAX的无符号整数
#define MQ_PRIO_MAX 32768
5. 发送消息/读取消息
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *priop);
返回:成功时为0,出错为-1
返回:成功时为消息中的字节数,出错为-1
參数: 最后一个是消息的优先级
消息队列的限制:
MQ_OPEN_MAX : 一个进程可以同一时候拥有的打开着消息队列的最大数目
MQ_PRIO_MAX : 随意消息的最大优先级值加1
/** 演示样例: 向消息队列中发送消息, 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;
}
/** 演示样例: 从消息队列中获取消息 **/
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;
}
6.建立/删除消息到达通知事件
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
返回: 成功时为0,出错时为-1
功能: 给指定队列建立或删除异步事件通知
sigev_notify代表通知的方式: 一般经常使用两种取值:SIGEV_SIGNAL, 以信号方式通知; SIGEV_THREAD, 以线程方式通知
假设以信号方式通知: 则须要设定一下两个參数:
sigev_signo: 信号的代码
sigev_value: 信号的附加数据(实时信号)
假设以线程方式通知: 则须要设定下面两个參数:
sigev_notify_function
sigev_notify_attributes
union sigval
{
int sival_int; /* Integer value */
void *sival_ptr; /* pointer value */
}; struct sigevent
{
int sigev_notify; /* SIGEV_{ NONE, ISGNAL, THREAD} */
int sigev_signo; /* signal number if SIGEV_SIGNAL */
union sigval sigev_value; /* passed to signal handler or thread */
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attribute;
};
參数sevp:
NULL: 表示撤销已注冊通知;
非空: 表示当消息到达且消息队列当前为空, 那么将得到通知;
通知方式:
1. 产生一个信号, 须要自己绑定
2. 创建一个线程, 运行指定的函数
注意: 这样的注冊的方式仅仅是在消息队列从空到非空时才产生消息通知事件, 并且这样的注冊方式是一次性的!
** 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以又一次注冊(假设须要的话),可是要注意: 又一次注冊要放在从消息队列读出消息之前而不是之后(如同演示样例程序);
异步信号安全函数
#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
能够使用sigwait函数取代信号处理程序的信号通知。将信号堵塞到某个函数中,只等待该信号的递交。採用sigwait实现上面的程序例如以下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h> int main(int argc,char *argv[])
{
mqd_t mqd;
int signo;
void *buff;
ssize_t n;
sigset_t newmask;
struct mq_attr attr;
struct sigevent sigev;
if(argc != 2)
{
printf("usage :mqnotify <name>");
exit(0);
}
mqd = mq_open(argv[1],O_RDONLY);
mq_getattr(mqd,&attr);
buff = malloc(attr.mq_msgsize);
sigemptyset(&newmask);
sigaddset(&newmask,SIGUSR1);
sigprocmask(SIG_BLOCK,&newmask,NULL); sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
if(mq_notify(mqd,&sigev) == -1)
{
perror("mq_notify error");
exit(-1);
}
for(; ;)
{
sigwait(&newmask,&signo); //堵塞并等待该信号
if(signo == SIGUSR1)
{
mq_notify(mqd,&sigev);
while((n = mq_receive(mqd,buff,attr.mq_msgsize,NULL))>=0)
printf("read %ld bytes\n",(long) n);
if(errno != EAGAIN)
{
perror("mq_receive error");
exit(-1);
}
}
}
eixt(0);
}
启动线程处理消息通知,程序例如以下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h> mqd_t mqd;
struct mq_attr attr;
struct sigevent sigev;
static void notify_thread(union sigval); int main(int argc,char *argv[])
{ if(argc != 2)
{
printf("usage :mqnotify <name>");
exit(0);
}
mqd = mq_open(argv[1],O_RDONLY | O_NONBLOCK);
mq_getattr(mqd,&attr); sigev.sigev_notify = SIGEV_THREAD;
sigev.sigev_value.sival_ptr = NULL;
sigev.sigev_notify_function = notify_thread;
sigev.sigev_notify_attributes = NULL; if(mq_notify(mqd,&sigev) == -1)
{
perror("mq_notify error");
exit(-1);
}
for(; ;)
{
pause();
}
eixt(0);
}
static void notify_thread(union sigval arg)
{
ssize_t n;
void *buff;
printf("notify_thread started\n");
buff = malloc(attr.mq_msgsize);
mq_notify(mqd,&sigev);
while((n = mq_receive(mqd,buff,attr.mq_msgsize,NULL))>=0)
printf("read %ld bytes\n",(long) n);
if(errno != EAGAIN)
{
perror("mq_receive error");
exit(-1);
}
free(buff);
pthread_exit(NULL);
}
附-查看已经成功创建的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
Linux进程间通信(IPC)编程实践(十二)Posix消息队列--基本API的使用的更多相关文章
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...
- Java并发编程(十二)-- 阻塞队列
在介绍Java的阻塞队列之前,我们简单介绍一下队列. 队列 队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向 ...
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
- Linux IPC实践(7) --Posix消息队列
1. 创建/获取一个消息队列 #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For m ...
- Linux环境编程之IPC进程间通信(五):Posix消息队列1
对于管道和FIFO来说.必须应该先有读取者存在.否则先有写入者是没有意义的. 而消息队列则不同,它是一个消息链表,有足够写权限的线程可往别的队列中放置消息,有足够读权限的线程可从队列中取走消息.每一个 ...
- Linux进程间通信(IPC)
序言 linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的. 而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心) ...
- Linux进程间通信IPC学习笔记
linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...
- Kali Linux Web 渗透测试— 第十二课-websploit
Kali Linux Web 渗透测试— 第十二课-websploit 文/玄魂 目录 Kali Linux Web 渗透测试— 第十二课-websploit..................... ...
随机推荐
- pat 团体天梯赛 L3-007. 天梯地图
L3-007. 天梯地图 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 本题要求你实现一个天梯赛专属在线地图,队员输入自己学校 ...
- 【linux命令】lscpu,/etc/cpuinfo详解
lscpu 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 i2000:~ # lscpu Architecture: ...
- WebService 序列化和反序列化
参考了Fish LI的Xml读取文章,写了XML序列化和反序列化的文章. 序列化:把实体列转化成XML.反序列化:把XML按一定的规则转化成需要的实体列. 序列化和反序列化化使用到的类, using ...
- Docker(一):什么是docker
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Linux 基金会,遵从了 ...
- Codeforces 912E Prime Gift(预处理 + 双指针 + 二分答案)
题目链接 Prime Gift 题意 给定一个素数集合,求第k小的数,满足这个数的所有质因子集合为给定的集合的子集. 保证答案不超过$10^{18}$ 考虑二分答案. 根据折半的思想,首先我们把这个 ...
- 关于bug的沟通
关于BUG的沟通 一个人要去做一件事情,一般来说是按照自己的意愿去做的,如果不是自己想做而是被要求这么做的话,心里一定会留下点不愉快,特别是那种有自信有自己主见的人,比如说开发人员,当测试人员发现一个 ...
- POJ 3254 Corn Fields [DP]
题意:略. 思路:第一次做状态压缩的dp. 在这里说一下状态压缩的原则.因为每一行只有最多12个格子,每个格子只有1(可放牛)和0(不可放牛)两种状态,这总共是2^12种状态,直接用一个int整型变量 ...
- 基于Bootstrap的页面排版知识
标题: Bootstrap定义了所有HTML的标题样式,<h1>...<h6>标签或者在标签内加入.h1 class等可以得到一样的效果 效果: 副标题: 标签<smal ...
- [深入浅出iOS库]之数据库 sqlite
一,sqlite 简介 前面写了一篇博文讲如何在 C# 中使用 ADO 访问各种数据库,在移动开发和嵌入式领域也有一个轻量级的开源关系型数据库-sqlite.它的特点是零配置(无需服务器),单磁盘文件 ...
- Sample example for Speech to Text in iOS
There are several libraries for this kind of conversion - I host two of those on GitHub: libsprec (t ...