Linux C语言 40-进程间通信IPC之消息队列

Linux C语言 40-进程间通信IPC之消息队列

本节关键字:C语言 System V IPC 进程间通信 消息队列
相关库函数:ftok、msgget、msgsnd、msgrcv、msgctl

什么是消息队列?

消息队列是System V中的一种进程间通信机制(如管道、信号量、共享内存等),在Linux系统中,消息队列本质上是内核维护的一块内存。

消息队列的特点

  • 消息队列优化了管道的FIFO(First In,First Out)、亲属进程、半双工以及单对单通信的缺点;
  • 消息队列中的消息是有类型的,可以更快的取出想要的数据;
  • 消息队列中的消息是有格式的,发送消息时需要按照一定的格式组数据报;
  • 消息队列可以实现消息的随机查询,没有先进先出的限制,可以按照消息的类型读取;
  • 消息队列允许一个或多个进程写入或读取消息;
  • 消息队列中消息被读出后会被删除;
  • 消息队列拥有唯一的标识符;
  • 消息队列在创建后,只有人工删除或内核重启时才会被删除,否则被创建的消息队列会一直存在与操作系统中;
  • 消息队列中每条消息的长度最多为8K字节;
  • 消息队列的容量最多为16K字节;
  • 消息队列在操作系统中共存的数量最多为1609个;
  • 操作系统中消息的共存数量最多为16384个;

Trip:单个进程中消息队列的限制可以使用命令 ulimit -a 进行查看。

消息队列相关库函数

创建消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>key_t ftok(const char *pathname, int proj_id);
/**
@brief ftok()函数使用给定文件的路径名命名标识(必须引用现有的可访问文件)和proj_id的最低有效位8(必须为非零)来生成key_t类型的System V IPC密钥,该密钥适用于msgget(2)、semget(2)或shmget(2)。
当使用相同的proj_id值时,命名相同文件的所有路径名的结果值都是相同的。当(同时存在的)文件或项目ID不同时,返回的值应该不同。
@param pathname 指定文件的路径名(必须引用现有的可访问文件)
@param proj_id 用来生成System V IPC密钥,低八位必须不为零。当使用相同的proj_id值时,命名相同文件的所有路径名的结果值都相同。当文件或proj_id不同时,返回的值应该不同。
@return 成功返回生成的key_t值。失败返回-1,并设置错误码errno
*/int msgget(key_t key, int msgflg);
/**
@brief 获取消息队列标识符。msgget()系统调用返回与参数key值相关联的消息队列标识符。
@param key 消息队列的System V IPC密钥。以下两种情况会创建新的消息队列:当key值为IPC_PRIVATE时,创建消息队列;当key不是IPC_PRIVATE时,不存在密钥与给定key值相同的消息队列,并且在msgflg中指定了IPC_CREAT,则会创建新的消息队列。
@param msgflg 消息队列的权限值,与mode_t一样(没有可执行权限),相关宏如下:IPC_CREATE  创建消息队列IPC_EXCL    检测消息队列是否存在
@return 成功返回消息队列的标识符,失败返回-1,并设置错误码errno错误码errno类型:
EACCES        存在密钥为key的消息队列,但调用进程没有访问该队列的权限,并且不具有CAP_IPC_OWNER功能。
EEXIST        存在同时指定IPC_CREAT和IPC_EXCL的密钥和消息flg的消息队列。
ENOENT        不存在密钥为key的消息队列,并且msgflg未指定IPC_CREAT。
ENOMEM        必须创建一个消息队列,但系统没有足够的内存用于新的数据结构。
ENOSPC        必须创建消息队列,但将超过消息队列最大数量(MSGMNI)的系统限制。Trip:IPC_PRIVATE不是标志字段,而是key_t类型。如果这个特殊值用于key,则系统调用将忽略除msgflg的最低有效9位之外的所有内容,并创建一个新的消息队列。
*/
查看消息队列

消息队列的查看可以在命令行完成,在消息队列创建成功后,可以使用命令 ipcs 进行查看,ipcs命令的使用参数如下:
-m 查看系统共享内存信息
-q 查看系统消息队列信息
-s 查看系统信号量信息
-a 显示系统内所有的IPC信息

如果要手动删除指定的IPC可以使用 ipcrm [-msq] id 命令完成,ipcrm 命令的使用参数如下:
-m 移除指定的共享内存,例:ipcrm -m shmid
-s 移除指定的信号量,例:ipcrm -m semid
-q 移除指定的消息队列,例:ipcrm -m msgid

消息队列的消息格式
// Trip:消息类型必须是长整型,且必须是结构体中的第一个成员,消息正文可以有多个任意类型的成员。typedef struct _msg
{long mtype;        	// 消息类型,必须大于0char mtext[100];    // 消息正文int  mage;        	// 消息正文...            		// 消息正文可以有多个任意类型的成员
} MSG;
向消息队列发送消息

Trip: msgsnd()被终端后永远不会自动重新启动。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
/**
@brief msgsnd()系统调用向消息队列发送消息,调用进程必须对消息队列具有写权限才能发送消息。msgsnd()系统调用将msgp指向的消息的副本附加到由msgid指定标识符的消息队列中。如果队列中有足够的可用空间,则msgsnd()会立即成功。(队列容量由消息队列的相关数据结构中的msg_bytes字段定义。在创建队列期间,此字段初始化为MSGMNB字节,但可以使用msgctl(2)修改此限制。)如果队列中的可用空间不足,则msgsnd()的默认行为是阻塞,直到空间变为可用。如果在msgflg中指定了IPC_NOWAIT,则调用会失败并返回错误EAGAIN。
@param msgid 消息队列的标识符,表示要向哪个消息队列发送消息
@param msgp 指向调用方定义的结构,结构格式如上述消息队列的消息格式一样
@param msgsz 是一个非负整数,指定msgp结构中mtext字段的大小,结构中mtext字段是一个数组(或其它结构),允许长度为0的消息。
@param msgfg 控制属性,0阻塞发送,直到消息队列由足够的空间去发送msgp指向的消息;IPC_NOWAIT若消息没有立即发送则调用该函数的进程会立即返回
@return 成功返回0,失败返回-1,并设置错误码errnoTrip:阻塞的msgsnd()调用也可能失败,原因如下:
消息队列被删除,这种情况下msgsnd()系统调用失败,errno被设置为EIDRM;
捕获一个信号,这种情况下msgsnd()系统调用失败,errno被设置为EINTR,且不管是否设置了SA_RESTART标志,msgsnd()被中断后都不会自动重新启动;错误码errno类型:
EACCES        调用进程没有对消息队列的写入权限,也没有CAP_IPC_OWNER功能。
EAGAIN        由于队列的msg_qbytes限制,无法发送消息,并且在msgflg中指定了IPC_NOWAIT。
EFAULT        无法访问msgp指向的地址。
EIDRM        消息队列已删除。
EINTR        在消息队列已满的情况下休眠,进程捕获到一个信号。
EINVAL        无效的msqid值、非正的mtype值或无效的msgsz值(小于0或大于系统值MSGMAX)。
ENOMEM        系统内存不足,无法复制msgp指向的消息。
*/
从消息队列读取消息

以下消息队列资源限制会影响msgsnd()调用:

MSGMAX 消息文本的最大大小:8192字节(在Linux上,可以通过/proc/sys/kernel/MSGMAX读取和修改此限制)
MSGMNB 消息队列的默认最大字节大小:16384字节(在Linux上,可以通过/proc/sys/kernel/MSGMNB读取和修改此限制)。超级用户可以通过msgctl(2)系统调用将消息队列的大小增加到MSGMNB之外。

该实现对消息头的系统范围最大数量(MSGTQL)和消息池的系统范围以字节为单位的最大大小(MSGPOOL)没有内在限制。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/**
@brief msgrcv()系统调用从消息队列读取消息,调用进程具有读权限才能接收消息
@param msgid 消息队列的标识符,表示要从哪个消息队列中获取消息
@param msgp 指向存放消息结构体的地址
@param msgsz 消息正文的字节数
@param msgtyp 必须是一个正整数,由接收进程用于消息选择。=0读取消息队列中的第一个消息;>0读取消息队列中消息类型为msgtyp的消息;<0读取消息队列中消息类型值小于或等于msgtyp绝对值的消息,如果有多个,则读取类型值最小的消息。若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的。
在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。
@param msgfg 控制属性。0 阻塞读取,直到成功读取到消息;MSG_NOERROR 若返回的消息字节数比msgsz多,则会被截断且不通知消息发送进程;IPC_NOWAIT 若没有读取到消息立即返回;MSG_EXCEPT 与大于0的msgtyp一起使用,读取队列中消息类型与msgtyp不同的第一条消息。
@return 成功返回0,失败返回-1,并设置错误码errnoTrip:执行成功后,msg_lrpid设置为调用进程的进程ID;msg_qnum递减1;msg_rtime设置为当前时间。错误码errno类型:
E2BIG        消息文本长度大于msgsz,并且未在msgflg中指定MSG_NOERROR。
EACCES        调用进程没有对消息队列的读取权限,也没有CAP_IPC_OWNER功能。
EAGAIN        队列中没有可用的消息,并且在msgflg中指定了IPC_NOWAIT。
EFAULT        无法访问msgp指向的地址。
EIDRM        当进程正在休眠以接收消息时,消息队列已被删除。
EINTR        当进程正在休眠以接收消息时,进程捕获到一个信号;参见signal(7)。
EINVAL        msgqid无效,或者msgsz小于0。
ENOMSG        在msgflg中指定了IPC_NOWAIT,并且消息队列中不存在请求类型的消息。
*/
控制消息队列

ipcs(8)程序使用IPC_INFO、MSG_STAT和MSG_INFO操作来提供有关所分配资源的信息。将来,这些文件可能会被修改或移动到/proc文件系统接口。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>struct msginfo 
{int msgpool; // 用于保存消息数据的缓冲池的大小(以千字节为单位);内核中未使用int msgmap;  // 消息映射中的最大条目数;内核中未使用int msgmax;  // 单个消息中可写入的最大字节数int msgmnb;  // 可以写入队列的最大字节数;用于在创建队列期间初始化msg_qbytes(msgget(2))int msgmni;  // 最大消息队列数int msgssz;  // 消息段大小;内核中未使用int msgtql;  // 系统中所有队列上的最大消息数;内核中未使用unsigned short int msgseg; // 最大分段数;内核中未使用
};struct ipc_perm 
{key_t          __key;       // 提供给msgget(2)的密钥uid_t          uid;         // 所有者的有效UIDgid_t          gid;         // 所有者的有效GIDuid_t          cuid;        // 创建者的有效UIDgid_t          cgid;        // 创建者的有效GIDunsigned short mode;        // 访问权限unsigned short __seq;       // 序列号码
};struct msqid_ds 
{struct ipc_perm msg_perm;     // 所有权和权限time_t          msg_stime;    // 最后成功调用msgsnd()的时间time_t          msg_rtime;    // 最后成功调用msgrcv()的时间time_t          msg_ctime;    // 最后更改的时间unsigned long   __msg_cbytes; // 队列中的当前字节数(非标准)msgqnum_t       msg_qnum;     // 队列中的当前消息数msglen_t        msg_qbytes;   // 队列中允许的最大字节数pid_t           msg_lspid;    // 最后调用msgsnd()的进程PIDpid_t           msg_lrpid;    // 最后调用msgrcv()的进程PID
};
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
/**
@brief msgctl()对标识符为msqid的消息队列执行cmd指定的控制操作,如修改消息队列的属性、删除消息队列等
@param msgid 消息队列的标识符,表示要控制哪个消息队列
@param cmd 需要执行的命令,可选值如下:IPC_STAT 将信息从与msqid相关联的内核数据结构复制到buf指向的msqid_ds结构中。调用方必须具有对消息队列的读取权限。IPC_SET  将buf指向的msqid_ds结构的一些成员的值写入与此消息队列相关联的内核数据结构,同时更新其msg_ctime成员。结构的以下成员将更新:msg_qbytes、msg_perm.uid、msg_pterm.gid和(的最低有效9位)msg_perm.mode。调用进程的有效UID必须与消息队列的所有者(msg_perm.UID)或创建者(msg_pterm.cuid)匹配,或者调用方必须具有特权。将msg_qbytes值提升到系统参数MSGMNB之外需要适当的权限(Linux:CAP_IPC_RESOURCE功能)。IPC_RMID 立即删除消息队列,唤醒所有等待的读写器进程(返回错误并将errno设置为EIDRM)。调用进程必须具有适当的权限,或者其有效的用户ID必须是消息队列的创建者或所有者的ID。IPC_INFO 返回有关系统范围的消息队列限制和buf指向的结构中的参数的信息。如果定义了_GNU_SOURCE功能测试宏,则此结构的类型为msginfo(因此,需要强制转换),在<sys/msg.h>中定义:实际内容如上方展示的struct msginfo;MSG_INFO (特定于Linux)返回一个msginfo结构,该结构包含与IPC_INFO相同的信息,只是返回以下字段,其中包含有关消息队列消耗的系统资源的信息:msgpool字段返回系统上当前存在的消息队列数;msgmap字段返回系统上所有队列中的消息总数;msgtql字段返回系统上所有队列中所有消息的总字节数。MSG_STAT (特定于Linux)返回与IPC_STAT相同的msqid_ds结构。但是,msqid参数不是队列标识符,而是内核内部数组的索引,用于维护系统上所有消息队列的信息。
@param buf msqid_ds数据类型的地址,用来存放或更改消息队列的属性
@return 成功后,IPC_STAT、IPC_SET和IPC_RMID返回0。成功的IPC_INFO或MSG_INFO操作返回内核内部数组中使用次数最多的条目的索引,该数组记录了有关所有消息队列的信息。(此信息可与重复的MSG_STAT操作一起使用,以获得有关系统上所有队列的信息。)成功的MSG_TAT操作将返回索引在msqid中给出的队列的标识符。失败返回-1,并设置错误码errno错误码errno类型:
EACCES        参数cmd等于IPC_STAT或MSG_STAT,但调用进程对消息队列msqid没有读取权限,也不具有CAP_IPC_OWNER功能。
EFAULT        参数cmd的值为IPC_SET或IPC_STAT,但buf指向的地址不可访问。
EIDRM        消息队列已删除。
EINVAL        cmd或msqid的值无效。或者:对于MSG_STAT操作,msqid中指定的索引值引用了当前未使用的数组槽。
EPERM        参数cmd的值为IPC_SET或IPC_RMID,但调用进程的有效用户ID不是消息队列的创建者(在msg_perm.cuid中找到)或所有者(在msg_prerm.uid中找到),并且该进程没有特权(Linux:它不具有CAP_SYS_ADMIN功能)。
*/

消息队列的使用例程

/// 待补充

提示:先做内容框架梳理,后期进行完善补充!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205389.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手把手将Visual Studio Code变成Python开发神器

Visual Studio Code 是一款功能强大、可扩展且轻量级的代码编辑器&#xff0c;经过多年的发展&#xff0c;已经成为 Python 社区的首选代码编辑器之一 下面我们将学习如何安装 Visual Studio Code 并将其设置为 Python 开发工具&#xff0c;以及如何使用 VS Code 提高编程工作…

在 Windows 桌面的redis中远程连接到 VMware 中运行的 Linux 上的 Redis

先修改一下docker容器中的redis(一会连上之后看效果) 我使用的是VMware的虚拟机 选择的网络设置为桥接模式 查到虚拟机独立的ip是如下 允许 Linux 虚拟机上的 Redis 监听外部连接&#xff1a; 打开 Linux 虚拟机上的 Redis 配置文件。在大多数系统上&#xff0c;配置文件位于…

编程应用实例,养生馆会员管理系统软件统计查询教程

一、前言 编程应用实例&#xff0c;养生馆会员管理系统软件&#xff0c; 导航栏菜单有 系统设置&#xff1a;可以设置操作员的权限以及打印机参数设置。 会员信息登记&#xff1a;可以直接用手机号登记电子会员卡 会员卡充值&#xff1a;可以直接报手机号充值&#xff0c;…

【Java 基础】24 日期操作

文章目录 1.Date2.LocalDate3.LocalTime4.LocalDateTime5.DateTimeFormatter6.Period7.Duration8.ZoneId9.ZonedDateTime总结 日期和时间是我们常见的操作对象&#xff0c;Java 也提供了强大的日期和时间处理类库&#xff0c;使我们可以方便的进行日期的表示、计算、格式化等。…

springboot(ssm甘肃旅游工艺品商城 旅游管理系统Java(codeLW)

springboot(ssm甘肃旅游工艺品商城 旅游管理系统Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&…

AntDB数据库助力中国移动结算中心建设

结算中心负责中国移动漫游伙伴进行数据和财务清算支撑。本次结算中心项目涉及结算处理、资料管理、信息管理等模块&#xff0c;用以构建系统的结算能力。 建设需求 结算中心现有传统集中式架构的数据库无法做到根据业务量变化进行弹性扩缩容&#xff0c;目前系统数据量巨大&a…

ESP32-Web-Server编程- 在 Web 上开发动态纪念册

ESP32-Web-Server编程- 在 Web 上开发动态纪念册 概述 Web 有很多有趣的玩法&#xff0c;在打开网页的同时送她一个惊喜。 需求及功能解析 本节演示在 ESP32 上部署一个 Web&#xff0c;当打开对应的网页时&#xff0c;将运行动态的网页内容&#xff0c;显示炫酷的纪念贺词…

如何解决依赖传递引起的版本冲突?

依赖传递引起的版本冲突通常是由于在开发过程中&#xff0c;不同模块之间的依赖版本不匹配造成的。解决这种冲突的方法主要包括以下几种&#xff1a; 版本管理&#xff1a;在项目的配置文件中&#xff08;如 Maven 的 pom.xml 或 Gradle 的 build.gradle&#xff09;清晰地定义…

计算机操作系统4

1.什么是进程同步 2.什么是进程互斥 3.进程互斥的实现方法(软件) 4.进程互斥的实现方法(硬件) 5.遵循原则 6.总结&#xff1a; 线程是一个基本的cpu执行单元&#xff0c;也是程序执行流的最小单位。 调度算法&#xff1a;先来先服务FCFS、短作业优先、高响应比优先、时间片…

YOLOv6 学习笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、YOLOv6贡献和改进二、YOLOv6核心概念三、YOLOv6架构改进四、YOLOv6重参思想五、YOLOv6的损失函数总结 前言 在计算机视觉领域&#xff0c;目标检测技术一直…

有趣的代码——有故事背景的程序设计5

接着上篇文章再和大家分享一下有趣的代码&#xff01; 目录 1.求母串中子串的个数 2.行走机器人 3.荷兰国旗问题 4.统计考研成绩 1.求母串中子串的个数 给定一个母串s和一个子串t&#xff0c;在主串s中寻找子串t的过程为字符串匹配。每匹配成功一次&#xff0c;即母串中含有…

移动app测试要不要做第三方软件测试?

移动app测试是指通过对移动应用进行全面的评估和验证&#xff0c;以确保其功能和性能符合设计要求&#xff0c;以提供给用户最好的体验。通常包括功能测试、界面测试、性能测试、兼容性测试等多个环节。由于现在越来越多的软件企业会选择将测试工作交由第三方软件测试进行&…

【Git】本地代码如何托管到远程仓库(保姆级教程)

注意前提是你本地已经安装了Git 1. 新建远程仓库 选一个git服务器&#xff0c;新建一个远程仓库&#xff0c;这里我选用的是gitee 点击确定后&#xff0c;显示如下界面表示仓库已经新建完成 2.建立本地仓库 本地新建一个空文件夹&#xff0c;在里面写一些你的初始代码文件。…

夜莺专业版网络设备功能介绍

网络设备采集简介 网络设备的问题通常会产生较大范围的影响&#xff0c;因此采集监控网络设备是一项常见的任务。不同公司在实施网络设备采集时可能采用不同的方案&#xff0c;主要有三类&#xff1a; SNMP&#xff08;Simple Network Management Protocol&#xff09;&#x…

2个月拿下信息系统项目管理师攻略(攻略超级全)

信息系统项目管理师&#xff08;高项&#xff09;一次性过啦&#xff01;结合这次备考经验&#xff0c;给大家总结一下复习方法。 先上图&#xff0c;开心一下&#xff01; 一、我为什么选择了高项 为什么我会选信息系统项目管理师&#xff0c;也就是我们常说的高项。 原因1…

深入浅出分析kafka客户端程序设计 ----- 生产者篇----万字总结

前面在深入理解kafka中提到的只是理论上的设计原理&#xff0c; 本篇讲得是基于c语言的kafka库的程序编写&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 首先要编写生产者的代码&#xff0c;得先知道生产者的逻辑在代码上是怎么体现的 1.kafka生产者的逻辑 …

zookeeper集群 +kafka集群

1.zookeeper kafka3.0之前依赖于zookeeper zookeeper是一个开源&#xff0c;分布式的架构&#xff0c;提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构 存储和管理数据&#xff0c;分布式节点上的服务接受观察者的注册&#xff0c…

Date与LocalDate互转

1、Date转LocalDate Date toDay new Date(); LocalDate localDate toDay.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();2、LocalDate转Date LocalDate localDate LocalDate.parse("2023-01-01", DateTimeFormatter.ISO_LOCAL_DATE); Date date D…

ubuntu18.04 vscode 安装 vue.volar Vue Language Features (Volar) , vue3 必备插件

直接在vscode 里面下载老是失败&#xff0c;不是网络问题&#xff0c;而是vue.volar插件配置的vscode版本与vscode版本不一致导致出现安装失败 官网下载最新的 vue.volar插件 https://marketplace.visualstudio.com/ 官网搜索 vue.volar 然后打开 Vue Language Features (Vol…

类和对象,this指针

一、类的引入&#xff1a; 如下&#xff0c;在C中&#xff0c;我们可以在结构体中定义函数&#xff0c;如下&#xff0c;之前我们学习C中中一直是在结构体中定义变量。 struct student{void studentinfo(const char* name,const char* gener,int age){ strcpy(_name,name);st…