tlpi: POSIX消息队列(mq_open, mq_send, mq_receive, mq_close, mq_unlink)

POSIX消息队列

消息队列是Linux IPC很常见的一种通信方式,它允许进程以消息的形式交换数据,下面将介绍POSIX消息队列

POSIX消息队列的特征

  • 引用计数

    仅当所有当前使用队列的进程都关闭了队列之后才会对队列进行标记然后删除

  • 优先级

    队列中的消息是严格按照顺序排队的

  • notify特性

    对消息队列由空变为非空时,POSIX消息队列允许队列向监听它的进程发送一条通知(notification)

  • 异步

    当一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待消息的到达

    读取也是一样

  • 持续性

    消息队列是内核中的一个对象,只有当系统终止时,消息队列才会被删除,当调用进程退出,消息队列不收影响

消息队列的打开、关闭、断开连接、数据发送/接受

请查看这篇文章Linux进程通信之POSIX消息队列

fork()、exec()以及进程终止时对消息队列的影响

  • 父进程使用fork()之后,子进程会接受父进程消息队列描述符的副本,他们指向同一个消息队列句柄(handle)

    当父进程对消息队列注册了监控函数时,子进程不会继承

  • 当一个进程执行了exec()或者终止后,其所有打开的消息队列描述符均会被关闭,进程对消息队列的监控也会被撤销

消息队列描述符与消息队列之间的关系

消息队列描述符与文件描述符十分相似,实际上Linux就是使用文件描述符实现了消息队列描述符

在这里插入图片描述

消息队列属性

mq_open(), mq_getattr()以及mq_setattr()函数均会接受mq_attr类型的参数,下面进行解析

struct mq_attr{
long mq_flags
long mq_maxmsg;
long mq_msgsize;
long mq_cumsgs;
}
  • mq_flags

    mq_flags可以取如下值

    标记描述
    O_CREAT队列不存在时创建队列
    O_EXCL与O_CREAT搭配使用,用于创建新队列
    O_RDONLY只读打开
    O_WRONLY只写打开
    O_RDWR读写打开
    O_NONBLOCK以非阻塞形式打开
    • O_EXCL

      在打开消息队列时使用 O_EXCL | O_CREAT 可以用来创建新文件,如果文件已经存在就会调用失败

    • O_NONBLOCK

      如果队列已满,在使用mq_send()时指定O_NONBLOCK标志会使得mq_send()调用失败,返回EAGAIN错误

      如果队列为空,在使用mq_receive()时指定O_ONOBLOCK标志会使得mq_receive()调用失败,返回EAGAIN错误

消息通知

mq_notify()函数使得调用进程对指定消息队列进行监控

#include<mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
										//成功:返回0  失败:返回-1
  • 仅当消息队列由空变为非空时调用进程才会收到通知

    更特殊的是,如果此时还有一个进程使用了mq_receive()读取队列,并且处于阻塞状态,那么调用进程就不会接受到通知

  • 当调用进程接受到通知后,它对队列的监控就结束了,如果想要继续监控,即必须再次使用mq_notify()函数

  • 在任何时刻只能够由一个进程监控某个消息队列

notification参数解析

  • sigevent简单原型 --------->(简化版本)

在这里插入图片描述

在这里插入图片描述

最主要的是这个sigev_notify字段,该字段可以取如下三个值,表示通知的方式

  • SIGEV_NONE

    对队列进行监控,但是不接受通知,当新消息进入空队列时监控结束

  • SIGEV_SIGNAL

    以信号的方式来监控进程,当有新消息进入空队列时,产生sigev_signo字段所含的信号给监控进程

​ 如果sigev_signo为实时信号,那么sigev_value字段将会携带伴随信息

union sigval{
	int sigval_int;  //int型伴随数据
	void *sigval_ptr //指针型伴随数据
}

获取到该伴随信息有三种办法

  1. 当创建设置了SA_SIGINFO的信号处理器函数时,可以从信号处理器函数参数的siginfo_t结构中共获取到该信息
  2. 使用sigwaitinfo()可以获取到siginfo_t结构
  3. 使用sigtimedwait()可以获取到siginfo_t结构

采用上述方法获取到的siginfo结构中的一些字段也会被相应的填充

  1. si_code-------->SI_MESSGQ
  2. si_pid------------>发送消息进程的进程id
  3. ui_uid------------->发送消息进程的ruid
  • SIGEV_THREAD

    通过调用在sigev_norify_function字段中的函数来通知进程,很类似与一个新线程的创建,该函数的参数为sigev_value字段

在Linux上使用POSIX消息队列

  • 在编译使用POSIX消息队列的程序时需要指定-lrt标志
  • 消息队列的名称应该以/打头,如/mq
  • 可以在/dev/mqueue目录下查看到系统中的消息队列对象,这些文件中会拥有一些字段
    • QSIZE:队列中数据的字节总数
    • NOTIFY:0表示SIGEV_SIGNAL,1表示SIGEV_NONE,2表示SIGEV_THRED
    • SIGNO: 如果NOTIFY为SIGEV_SIGNAL, 那么该字段即表示用来通知的信号
    • NOTIFY_PID: 当前监控该队列的进程ID

版权声明:本文为qq_52554396原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。