Linux进程间通信(IPC) Linux消息队列:讲解POSIX消息队列在Linux系统进程间通信中的应用和实践

简介: Linux进程间通信(IPC) Linux消息队列:讲解POSIX消息队列在Linux系统进程间通信中的应用和实践


  • 消息队列介绍

在应用开发中,生产者,消费者的模型非常常见,一方产生数据并把数据放入队列中,而另一方从队列中取数据,先进先出。

同样,在操作系统内核中,也实现了类似的功能,队列中存放的是“消息”。称之为消息队列,消息也可理解为数据。

主要用途是进程间通信(IPC),所谓通信,就是进行数据交互。

消息队列中的每条消息通常具有以下属性:

  • 一个无符号整数优先级(Posix)或一个长整型类型(System V)
  • 消息的数据部分长度(可以为0)
  • 数据本身(如果长度大于0的话.)

posix相关接口函数

Library interface  System call
mq_close(3) close(2)
mq_getattr(3) mq_getsetattr(2)
mq_notify(3) mq_notify(2)
mq_open(3) mq_open(2)
mq_receive(3) mq_timedreceive(2)
mq_send(3) mq_timedsend(2)
mq_setattr(3)   mq_getsetattr(2)
mq_timedreceive(3) mq_timedreceive(2)
mq_timedsend(3) mq_timedsend(2)
mq_unlink(3) mq_unlink(2)
  • Headers file
//Link with -lrt.
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>
#include <time.h>
  • Open/Close mqueue
 //打开/创建 消息队列,消息队列名称前面必须加上斜杆。
 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);
 
 
 //关闭消息队列
 int mq_close(mqd_t mqdes);
 //删除消息队列
 int mq_unlink(const char *name); //成功返回0,失败返回-1

Parameter Description:

name:表示消息队列的名字,它符合POSIX IPC的名字规则。

oflag:表示打开的方式,和open函数的类似。有必须的选项:O_RDONLY,O_WRONLY,O_RDWR,还有可选的选项:O_NONBLOCK,O_CREAT,O_EXCL。

mode:是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时,才需要提供该参数。表示默认访问权限。可以参考open。

attr:也是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时才需要。该参数用于给新队列设定某些属性,如果是空指针,那么就采用默认属性。

注意:每个消息队列都有一个保存其当前打开着的描述符数的引用计数器,所以使用mq_unlink删除类似删除文件的机制:当一个消息队列的引用计数大于0时,其name就能删除,但是该队列的析构要到最后一个mq_close发生时才能进行.

  • Get/Set mqueue Attributes
//获取/设置消息队列属性
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);
struct mq_attr {
    longmq_flags;    /* Flags: 0 or O_NONBLOCK */
 
    longmq_maxmsg;   /* Max. # of messages onqueue,can only be initialized when mq_open is created*/
 
    longmq_msgsize;  /* Max. message size (bytes),can only be initialized when mq_open is created*/
 
    longmq_curmsgs;  /* # of messages currently inqueue */
 
}

Description:

mq_getattr用于获取当前消息队列的属性,mq_setattr用于设置当前消息队列的属性。

其中mq_setattr中的oldattr用于保存修改前的消息队列的属性,可以为空。

mq_setattr可以设置的属性只有mq_flags,用来设置或清除消息队列的非阻塞标志。newattr结构的其他属性被忽略。

mq_maxmsgmq_msgsize属性只能在创建消息队列时通过mq_open来设置。

mq_open只会设置该两个属性,忽略另外两个属性。mq_curmsgs属性只能被获取而不能被设置。

Note:

  • attr.mq_maxmsg 不能超过文件 /proc/sys/fs/mqueue/msg_max 中的数值,我的机器上面是10。
  • attr.mq_msgsize不能超过 /proc/sys/fs/mqueue/msgsize_max 的数值。
  • Send/Receive messages through the message queue
//发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned int msg_prio);
//接收消息,总是返回指定消息队列最高优先级的最早信息,而且该优先级能随着该消息的内容以及长度一同返回.
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,size_t msg_len, unsigned int *msg_prio);
 
int mq_timedsend(mqd_t mqdes, const char *msg_ptr,
                     size_t msg_len, unsigned int msg_prio,
                     const struct timespec *abs_timeout);
 
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
                          size_t msg_len, unsigned int *msg_prio,
                          const struct timespec *abs_timeout);

Parameter Description:

mqdes:消息队列描述符;

msg_ptr:指向消息体缓冲区的指针;

msg_len:消息体的长度,其中mq_receive的该参数不能小于能写入队列中消息的最大大小,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。

如果mq_receive中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度可以为0。

msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。

POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。

如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive的msg_prio置为NULL。

const struct timespec:设置消息发送/接收的超时时间。

abs_timeout指向一个结构,该结构指定了呼叫将被阻止的时间上限。

此上限是自1970年1月1日00:00:00 +0000(UTC)以来的绝对超时(以秒和纳秒为单位)

如果队列已满,默认情况下mq_send会阻塞,可以使用mq_timedsend函数设置阻塞超时时间。

如果队列为空,默认情况下mq_receive会阻塞,可以使用mq_timedreceive函数设置阻塞超时时间。

由于默认情况下mq_send和mq_receive是阻塞进行调用,也可以通过mq_setattr来设置为O_NONBLOCK.

  • Asynchronous notification of message queue
//建立或者删除消息到达通知事件
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
 
//sigevent 结构
#include <signal.h>
union sigval {          /* Data passed with notification */
  int  sival_int;         /* Integer value */
    void   *sival_ptr;      /* Pointer value */
};
 
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) */
};

Parameter Description:

  • 如果sevp参数为非空,那么当前进程希望在有一个消息到达所指定的先前为空的对列时得到通知。
  • 如果sevp参数为空,而且当前进程被注册为接收指定队列的通知,那么已存在的注册将被撤销。

sigev_notify :

SIGEV_NONE:空的提醒,事件发生时不做任何事情

SIGEV_SIGNAL:向进程发送sigev_signo中指定的信号,具体详细的状况参照上面的文档,这涉及到sigaction的使用

SIGEV_THREAD:通知进程在一个新的线程中启动sigev_notify_function函数,函数的实参是sigev_value,系统API自动启动一个线程,我们不用显式启动。

Note:

  • 任意时刻只有一个进程可以被注册为接收某个给定队列的通知。
  • 当有一个消息到达先前为空的消息队列,而且已有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用中的前提下,通知才会发出。即说明,在mq_receive调用中的阻塞比任何通知的注册都优先。
  • 当通知被发送给它的注册进程时,其注册被撤消。进程必须再次调用mq_notify以重新注册注意:重新注册要放在从消息队列读出消息之前而不是之后。

Posix消息队列容许 异步事件通知,以告知何时有一个消息放置到某个空消息队列中,这种通知有两种方式可以选择:

  • 产生一个信号,通过信号处理函数进行处理消息.
    struct sigevent sev = { 0 };
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = (void *)p; //线程入口函数
    sev.sigev_notify_attributes = NULL;//线程属性,根据需求设置
    sev.sigev_value.sival_ptr = (void *)mqdes;  // 传递给线程的参数
  • 创建一个线程来执行一个指定的函数,在新的线程中处理消息.
    struct sigevent sev = { 0 };
    signal(signo,p);  //设置信号处理函数
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = signo ;  //信号值

  • 消息队列相关限制

mq_maxmsg:          队列中最大的消息数

mq_msgsize:          给定消息的最大字节数

MQ_OPEN_MAX:    一个进程能够同时拥有的打开着消息队列的最大数目

MQ_PRIO_MAX:     任意消息的最大优先级值+1

POSIX消息队列本身的限制就是mq_attr中的mq_maxmsg和mq_msgsize

这两个参数可以在调用mq_open创建一个消息队列的时候设定。当这个设定是受到系统内核限制的。

MQ_OPEN_MAXMQ_PRIO_MAX 定义在<unistd.h>中,可以在运行的时候听过调用sysconf函数获取.

ulimit -a |grep message
# POSIX message queues     (bytes, -q) 819200
# 限制大小为800KB,该大小是整个消息队列的大小,不仅仅是最大消息数*消息的最大大小;还包括消息队列的额外开销。
 
# POSIX消息队列默认的最大消息数和消息的最大大小分别为:
# mq_maxmsg = 10
# mq_msgsize = 8192
  • Posix消息队列与System V消息队列的区别
  1. 对Posix消息队列的读总是返回最高优先级的最早信息.
  2. 对System V消息队列的读取则可以返回任意指定优先级的消息.


  1. 当往一个空队列放置一个消息时,Posix消息队列允许产生一个信号或者启动一个线程;
  2. System V消息队列则不提供类似机制.


相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
2月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
736 10
|
2月前
|
安全 Linux 数据安全/隐私保护
为Linux系统的普通账户授予sudo访问权限的过程
完成上述步骤后,你提升的用户就能够使用 `sudo`命令来执行管理员级别的操作,而无需切换到root用户。这是一种更加安全和便捷的权限管理方式,因为它能够留下完整的权限使用记录,并以最小权限的方式工作。需要注意的是,随意授予sudo权限可能会使系统暴露在风险之中,尤其是在用户不了解其所执行命令可能带来的后果的情况下。所以在配置sudo权限时,必须谨慎行事。
313 0
|
2月前
|
Ubuntu Linux 开发者
国产 Linux 发行版再添新成员,CutefishOS 系统简单体验
当然,系统生态构建过程并不简单,不过为了帮助国产操作系统优化生态圈,部分企业也开始用国产操作系统替代 Windows,我们相信肯定会有越来越多的精品软件登录 Linux 平台。
109 0
|
2月前
|
Ubuntu 安全 Linux
Linux系统入门指南:从零开始学习Linux
Shell脚本是一种强大的自动化工具,可以帮助您简化重复的任务或创建复杂的脚本程序。了解Shell脚本的基本语法和常用命令,以及编写和运行Shell脚本的步骤,将使您更高效地处理日常任务。
200 0
|
22天前
|
Unix Linux 程序员
Linux文本搜索工具grep命令使用指南
以上就是对Linux环境下强大工具 `grep` 的基础到进阶功能介绍。它不仅能够执行简单文字查询任务还能够处理复杂文字处理任务,并且支持强大而灵活地正则表达规范来增加查询精度与效率。无论您是程序员、数据分析师还是系统管理员,在日常工作中熟练运用该命令都将极大提升您处理和分析数据效率。
96 16
|
14天前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
|
3月前
|
监控 Linux 网络安全
Linux命令大全:从入门到精通
日常使用的linux命令整理
661 13
|
4月前
|
Linux 网络安全 数据安全/隐私保护
使用Linux系统的mount命令挂载远程服务器的文件夹。
如此一来,你就完成了一次从你的Linux发车站到远程服务器文件夹的有趣旅行。在这个技术之旅中,你既探索了新地方,也学到了如何桥接不同系统之间的距离。
540 21
|
4月前
|
JSON 自然语言处理 Linux
linux命令—tree
tree是一款强大的Linux命令行工具,用于以树状结构递归展示目录和文件,直观呈现层级关系。支持多种功能,如过滤、排序、权限显示及格式化输出等。安装方法因系统而异常用场景包括:基础用法(显示当前或指定目录结构)、核心参数应用(如层级控制-L、隐藏文件显示-a、完整路径输出-f)以及进阶操作(如磁盘空间分析--du、结合grep过滤内容、生成JSON格式列表-J等)。此外,还可生成网站目录结构图并导出为HTML文件。注意事项:使用Tab键补全路径避免错误;超大目录建议限制遍历层数;脚本中推荐禁用统计信息以优化性能。更多详情可查阅手册mantree。
linux命令—tree
|
2月前
|
Linux 网络安全 开发工具
技术栈:这50条最常用的 Linux 命令你一定要会!
建议多在终端中实践,遇到不懂的命令就用 man 或 --help 了解详情!
387 0