【Linux修行路】进程通信——消息队列、信号量

news2024/11/14 18:59:52

目录

⛳️推荐

一、消息队列

1.1 实现原理

1.2 消息队列接口

1.2.1 msgget——创建、获取一个消息队列

1.2.2 msgctl——释放消息队列、获取消息队列属性

1.2.3 msgsnd——发送数据

1.2.4 msgrcv——从消息队列中检索数据块

1.3 消息队列的指令操作

二、信号量

2.1 数据不一致问题、互斥、临界资源、临界区

2.2 理解信号量

2.3 二元信号量

2.4 信号量也是共享资源

2.5 PV操作

2.6 总结

2.7 信号量的接口

2.8 信号量凭什么是进程通信的一种?

三、内核中对 IPC 资源的管理

3.1 三种描述 IPC 资源的结构体

3.2 操作系统对 IPC 资源的管理


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站【Linux修行路】动静态库详解点击跳转到网站

一、消息队列

1.1 实现原理

由操作系统在内核中创建一个队列,A、B两个进程以数据块的形式将需要发送的数据链接到队列中,为了区分消息队列中的数据块是谁发送的,因此数据块本质上是一个结构体,它里面有一个类型字段用来该标识数据块是由哪个进程发送的。

image-20240307090201559

1.2 消息队列接口

1.2.1 msgget——创建、获取一个消息队列

image-20240307090903999

其中第一个参数 key 和共享内存中的那个 key 一样,也是通过 ftok 函数来获取。

1.2.2 msgctl——释放消息队列、获取消息队列属性

image-20240307091149778

该函数用来控制一个消息队列,可以用它删除一个消息队列、获取消息队列的属性。

用户层描述消息队列的结构体 struct msqid_ds

image-20240307091319979

1.2.3 msgsnd——发送数据

消息队列不需要挂接到进程的地址空间,直接通过系统调用向消息队列中写入数据,或者从消息队列中读取数据。

image-20240307091846161

  • msqid:用户层唯一标识一个消息队列的 ID,就是 msgget 函数的返回值。
  • msgp:发送的数据块,是一个 struct msgbuf结构体类型,需要用户自己定义。
  • msgsz:数据块的大小。
  • msgflg:一般设置为0,表示消息队列如果满了,就阻塞式等待;IPC_NOWAIT 表示消息队列如果为满,不阻塞,立即返回-1。

数据块的类型:

image-20240307092804576

  • mtype:标识数据块的类型。
  • mtext:用发送的信息。
1.2.4 msgrcv——从消息队列中检索数据块

image-20240307092950508

  • msgtyp:表示要检索的数据块类型。
  • msgflag:用来设置当消息队列为空的时候,阻塞还是非阻塞,选项和上面的一样。

1.3 消息队列的指令操作

  • ipcs -q:查看当前操作系统中所有的消息队列。
  • ipcrm -q msqid:释放一个消息队列。

二、信号量

2.1 数据不一致问题、互斥、临界资源、临界区

共享内存没有同步与互斥机制,可能会出现 A 进程正在向共享内存中写入,还没有写完,B 进程就来读取,导致发方和收方的数据不完整,这就是数据不一致问题。

  • A B看到的同一份资源,共享资源,如果不加保护,会导致数据不一致问题。
  • 可以通过加锁来实现互斥访问,在任何时候,只允许一个执行流访问共享资源。
  • 把共享的,任何时刻只允许一个执行流访问(执行访问代码)的资源称为——临界资源。
  • 临界资源一般是操作系统或者用户维护的一段内存空间
  • 把访问临界资源的代码叫做临界区

2.2 理解信号量

信号量是一种用于控制多个进程或线程对共享资源的访问的同步机制,它的本质是一把计数器,用于记录资源的可用数量。

image-20240307140658029

在临界资源充足的情况下,如果出现多个执行流访问同一个临界资源,那属于编码 Bug。信号量保证的是,假设只有 n 个临界资源,不会出现 n+1 个执行流来访问临界资源,如果出现就出导致数据不一致问题。

  • 申请计数器成功,就表示当前执行流具有访问临界资源的权限了。
  • 申请到了计数器,并没有去访问临界资源,申请计数器是对资源的一种预定机制。
  • 计数器可以有效的保证访问临界资源的执行流的数量。
  • 所以,每个执行流想要访问临界资源的时候,不是直接访问,而是先申请计数器资源(信号量)

2.3 二元信号量

如果临界资源只有一份,那么这个计数器的值只能是 1 或者 0,并且,在任何时候都只允许一个执行流访问共享资源。我们把这种只能为 1、0 两态的计数器就叫做——二元信号量二元信号量本质是一把锁。计数器最大为 1,本质上是资源只有一份,也就是不要将临界资源分成很多块,而是当做一个整体,整体申请,整体释放,这样就能实现互斥

总结:二元信号量主要用于实现对临界资源的互斥访问。

2.4 信号量也是共享资源

所有执行流想要使用临界资源,必须先来申请信号量,所以信号量也是一种共享资源。可能出现多个执行流同时来申请同一个信号量,信号量是用来保护临界资源的,前提是信号量得保证自身的安全。而我们的 -- 和 ++ 操作是不安全的,他们转成汇编,一般会对应三条汇编指令:从内存中读取数据到 CPU 中;CPU 内进行操作;CPU 将结果写回内存。进程在运行的时候,随时可能被切换,这就导致在多进程共享信号量的前提下, -- 和 ++ 操作可能会导致信号量的值发生错乱。

2.5 PV操作

PV操作:申请信号量,本质是对计数器 --,称为 P 操作;释放共享资源,本质是对计数器 ++,称为 V 操作。PV 操作一定是原子的。所谓原子性,就是一件事情,要么不做,要做就做完,只有两态,没有正在做这样的概念。站在技术角度来理解原子性就是:该操作只对应一条汇编指令,那么该操作就是原子的。

2.6 总结

image-20240307145723292

2.7 信号量的接口

创建、获取一个信号量(集)——semget

image-20240307152340080

  • nsems:表示创建几个信号量。

控制信号量——semctl

image-20240307152551386

  • semnum:信号量的编号。

image-20240307153504246

可以通过将 cmd 设置为 SETVAL,再传递 union semun 来设置信号量的初始值。

申请释放信号量(PV操作)——semop

image-20240307152820489

image-20240307152908488

  • sem_num:信号量的编号。
  • sem_op:1 表示 V 操作;-1 表示 P 操作。

2.8 信号量凭什么是进程通信的一种?

  • 通信不仅仅是数据传输,相互协同也是,告诉某个执行流接下来可以干什么了,或者不可以干什么。
  • 要协同,本质也是通信,信号量首先要被所有的通信进程看到。

三、内核中对 IPC 资源的管理

共享内存、消息队列、信号量,统称为操作系统中的 IPC 资源。为了管理这些资源,操作系统创建了三个结构体,分别用来描述这三种 IPC 资源。

3.1 三种描述 IPC 资源的结构体

struct shmid_kernel:

struct shmid_kernel /* private to the kernel */
{	
	struct kern_ipc_perm	shm_perm;
	struct file *		shm_file;
	int			id;
	unsigned long		shm_nattch;
	unsigned long		shm_segsz;
	time_t			shm_atim;
	time_t			shm_dtim;
	time_t			shm_ctim;
	pid_t			shm_cprid;
	pid_t			shm_lprid;
	struct user_struct	*mlock_user;
};

struct msg_queue:

struct msg_queue {
	struct kern_ipc_perm q_perm;
	time_t q_stime;			/* last msgsnd time */
	time_t q_rtime;			/* last msgrcv time */
	time_t q_ctime;			/* last change time */
	unsigned long q_cbytes;		/* current number of bytes on queue */
	unsigned long q_qnum;		/* number of messages in queue */
	unsigned long q_qbytes;		/* max number of bytes on queue */
	pid_t q_lspid;			/* pid of last msgsnd */
	pid_t q_lrpid;			/* last receive pid */

	struct list_head q_messages;
	struct list_head q_receivers;
	struct list_head q_senders;
};

struct sem_array:

struct sem_array {
	struct kern_ipc_perm	sem_perm;	/* permissions .. see ipc.h */
	time_t			sem_otime;	/* last semop time */
	time_t			sem_ctime;	/* last change time */
	struct sem		*sem_base;	/* ptr to first semaphore in array */
	struct sem_queue	*sem_pending;	/* pending operations to be processed */
	struct sem_queue	**sem_pending_last; /* last pending operation */
	struct sem_undo		*undo;		/* undo requests on this array */
	unsigned long		sem_nsems;	/* no. of semaphores in array */
};

这三个结构体都有一个共性,结构体中的第一个数据类型都是 struct kern_ipc_perm 类型。

struct kern_ipc_perm:

struct kern_ipc_perm
{
	spinlock_t	lock;
	int		deleted;
	key_t		key;
	uid_t		uid;
	gid_t		gid;
	uid_t		cuid;
	gid_t		cgid;
	mode_t		mode; 
	unsigned long	seq;
	void		*security;
};

3.2 操作系统对 IPC 资源的管理

无论是什么类型的 IPC 资源,一定都有一个属于自己的 struct kern_ipc_perm 结构。所以,操作系统就通过一个 structkern_ipc_perm* 类型的数组将所有的 IPC 资源管理了起来。

struct ipc_ids:

struct ipc_ids {
	int in_use;
	int max_id;
	unsigned short seq;
	unsigned short seq_max;
	struct semaphore sem;	
	struct ipc_id_ary nullentry;
	struct ipc_id_ary* entries;
};

struct ipc_id_arry:

struct ipc_id_ary {
	int size;
	struct kern_ipc_perm *p[0];
};

其中 p 就是维护当前操作系统中所有 IPC 资源的一个柔性数组。如何通过这个数组里存的 struct ipc_id_ary* 找到某一个具体的 IPC 对象呢(如何找到 struct shmid_kernelstruct msg_queuestruct sem_array)?答案是通过强制类型转换,因为 kern_ipc_perm 是这三个结构体中的第一个成员,我们只要知道了一个 kern_ipc_perm 的地址,就相当于知道了某个具体 IPC 对象的起始地址,然后通过强制类型转换就可以访问到该 IPC 对象中的所有成员属性,这样就实现了对一个具体 IPC 对象的访问。例如:(struct shmid_kernel*)p[0]->q_stime。那操作系统是如何知道要将其强制转化成什么类型呢?答案是,在 kern_ipc_perm 中一定有字段来标识该 kern_ipc_perm 是属于那种 IPC 资源的,这就是多态的雏形。我们在用户层面上使用的:shmid、msqid、semid本质上就是内核中 p 数组的下标。

ipc_id_arry 这个数组隶属于操作系统,不属于任何进程,数组下标是一直线性递增的,不会因为 IPC 资源的释放而改变它的递增属性,即当前操作系统中最后一个 IPC 资源的下标是 12,释放掉这个 IPC 资源,下一次再创建 IPC 资源,因为有递增属性,所以它的下标是13,而不是12,当递增到一定值的时候,会回绕到0。

image-20240307113515929

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

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

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

相关文章

Java、python、php版 保险业务管理与数据分析系统 社会保险档案管理系统(源码、调试、LW、开题、PPT)

💕💕作者:计算机源码社 💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流&…

700. 二叉搜索树中的搜索(迭代)

目录 一:题目: 三:结果 二:代码:: 一:题目: 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的…

Ubuntu 超详细保姆级安装教程(每步都有截图)

文章目录 下载安装VMware检查网络适配器Ubuntu下载创建虚拟机启动虚拟机安装 VWware Tools安装 SSH 连接工具配置静态IP 下载安装VMware Desktop Hypervisor Solutions | VMware 官网下载速度慢的可以使用我百度网盘分享的链接来下载 百度网盘 | VMware 17.5.2 百度网盘没有…

vulnhub靶场-DC2

一、环境配置 1.下载地址:https://www.vulnhub.com/entry/dc-2,311/ 2.靶场配置:Nat模式 更改hosts文件,官网提示需要更改hosts文件才能访问到网站,否则访问不了 kali进入编辑模式vim,添加上自己的靶机ip地址保存即可…

Java进阶13讲__第八讲

集合:Collection,List,Set,Map 集合体系 集合结构 单列集合 1.Collection 1.初识Collection package cn.hdc.oop8.Collection;import java.util.ArrayList; import java.util.HashSet; import java.util.List;/*** 目标&#xf…

前端面试模拟:常见的3个JavaScript经典考题

在一次备受期待的前端开发高级岗位面试中,你紧张地走进了会议室,对面坐着的是一位经验丰富的技术面试官。窗外阳光明媚,屋内却有一丝令人紧张的静谧。 第一问:如何使用JavaScript实现事件委托? 面试官微微一笑&#xf…

实战项目:俄罗斯方块(一)

文章目录 🍊自我介绍🍊vt100 控制码1.概述2.数字格式①常用数字控制码②常用控制码 🍊绘制方格 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞关注评论收藏(一键四连)哦~ 🍊自我…

猫头虎分享:关于 Mac OS 系统 .DS_Store 文件的起源、作用及管理全指南

🐯猫头虎分享:关于 Mac OS 系统 .DS_Store 文件的起源、作用及管理全指南 🐯 猫头虎 分享:关于 Mac OS系统 .DS_Store 文件的起源和作用 今天猫头虎带您深入探讨 Mac OS 系统中的 .DS_Store 文件。作为一名开发者,您…

科研绘图系列:R语言差异基因四分图(Quad plot)

介绍 四分图(Quad plot)是一种数据可视化技术,通常用于展示四个变量之间的关系。它由四个子图组成,每个子图都显示两个变量之间的关系。四分图的布局通常是2x2的网格,每个格子代表一个变量对的散点图。 在四分图中,通常: 第一个子图显示变量A和B的关系。第二个子图显示…

足浴城消费系统小程序的设计

管理员账户功能包括:系统首页,个人中心,商品分类管理,商品信息管理,购买信息管理,会员卡申请管理,包厢信息管理,系统管理 微信端账号功能包括:系统首页,公告…

苍穹外卖项目前端DAY01

前端DAY01 1、基于脚手架创建前端工程 使用Vue CLI创建前端工程: 方式一:vue create 项目名称方式二:vue ui(比较慢) 2、vue基本使用方法 Vue的组件文件以.vue结尾,每个组件由三个部分组成: …

AJAX day-02 HTTP格式JSON格式

目录 一. 计算机网络 1.1 网络参考模型 1.2 各层重要对应的协议 1.3 DNS解析(域名解析服务器) 1.4 FTP(文件传输协议) 1.5 UDP(用户数据报协议) 1.6 TCP(传输控制协议) 1.7 IP(网际互连协议) 1.8 …

[Leetcode 46][Medium]-全排列-回溯(全排列问题)

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题地址 二、整体思路 对于全排列问题,很明显要用回溯法。但是和组合问题不一样的是全排列问题是可以取先前遍历的元素的。因此需要另外新建一个状态数组来存储所有元素是否被访问过的状态。回溯时把状态…

QT Sql 实现多个股票成交明细数据文件制成数据库并支持查询

一.背景 上一篇我们学会了,如何用python自动化爬出交易软件的历史成交明细分笔数据,如果你没看到的话,这里有链接: AI 通过python脚本自动化导出交易软件某一天的分笔成交明细-CSDN博客,我们接着讲如何讲用QT分析这些数据并制成数据库文件来查询。 二.效果图 三.实现步骤 …

【Qt】关于QMenuBar创建方式的讨论

关于QMenuBar创建方式的讨论 如果在创建项目的时候,没有勾选自动生成ui文件,此时上述代码是正确的;而如果勾选了自动生成ui文件,上述代码则会出现内存泄漏的问题。因为Qt已经生成了一个QMenuBar了 由于之前程序已经自己创建好了一…

Mysql基础练习题 1083.销售分析2 (力扣)

编写一个解决方案,报告那些买了 S8 而没有买 iPhone 的 买家。注意,S8 和 iPhone 是 Product 表中显示的产品。 题目连接: https://leetcode.cn/problems/sales-analysis-ii/description/ 建表插入数据: Create table If Not …

CentOS 超详细保姆级安装教程(每步都有截图)

文章目录 下载安装VMware检查网络适配器Centos 下载创建虚拟机启动虚拟机配置静态IP 这里提一下, Centos 相关的操作系统, Linux社区已经不再维护了, 建议装 Ubuntu 下载安装VMware 点我跳转 | VMware WorkStation Pro DownLoad 官网下载速度慢的可以使用我百度网盘分享的链接…

简易爬虫平台设计与实现

本来没有架构,写的组件多了,就有了架构。 前言 早期,我为了抓取mp3和一些网站文章,随意写了些零零星星的代码。后来,使用了scrapy和webmagic等爬虫框架,算是走上了正轨。又后来,东一个组件&…

手撕Python之条件语句和循环语句

1.比较运算符 2.判断 单条件判断,我们就需要用到if 程序的正常执行流程是从上往下依次执行 我们可以使用流程控制语句中的if语句来根据不同的情况执行不同的代码 单个条件的判断使用if关键字 两个条件使用if…else 多个条件使用if…elif…else if关键字判断后…

Ho-Lee利率模型的实现

一:Ho-Lee利率模型的介绍 Ho-Lee模型是由Thomas Ho和Sang-bing Lee在1986年提出的,用于描述利率期限结构变动的模型。该模型基于无套利机会假设,认为当前的利率期限结构包含了人们对未来利率预测的所有信息,因此利率期限结构的变…