Linux:进程间通信之system V

一、共享内存

进程间通信的本质是让不同的进程看到同一份代码

1.1 原理

第一步:申请公共内存 

    为了让不同的进程看到同一份资源,首先我们需要由操作系统为我们提供一个公共的内存块。  

第二步:挂接到要通信进程的地址空间中 (共享区)

     然后我们要将该内存挂接到需要进行通信的进程的地址空间中(共享区),这样才能让通信双方都能看到这份资源。 

第三步:通过地址空间的映射实现通信

    然后我们就可以通过代码来让某些进程写,某些进程读,都通过他们的地址空间映射到该共享内存,从而实现了通信

第四步:通信结束后相关进程去关联 

     通信结束释放内存之前一定要去关联,相当于是告诉相关进程不能再使用这些内存了。

第五步:由用户手动释放内存 

    因为我们申请的共享内存关联的进程可能有很多,所以如果我们不手动去释放的话,操作系统并不能知道到底哪个进程结束后需要去释放这段空间,因为担心其他的进程没有结束或者没有去关联。所以共享内存必须由用户通过系统调用接口或者命令去手动释放 

         因为操作系统中可能会存在多个共享内存,所以我们的操作系统必须想办法把共享内存管理起来,所以必然有相关的内核数据结构采用先描述再组织的方式管理起来!! 

  问题:上述的操作都是由进程做的吗??

——>肯定不能由进程做,因为如果由进程做,那么因为进程的独立性所以这个空间并不能被共享 所以我们的共享内存并不能单独属于某个进程,所以必须由操作系统来完成申请和释放,所以我们必须要使用系统调用接口!!!

1.2 创建共享内存——shmget 

系统调用接口shmget 

1.2.1 初识key

1、key是一个数字,这个数字是几并不重要,关键在于他在内核中具有唯一性,能够让不同的进程进行唯一标识!(如果我们申请的共享内存是一扇门,那么key就像是可以开启这扇门的钥匙) 

2、第一个进程通过key创建共享内存,往后的进程只需要拿着同一个key就可以和该进程看到同一块共享内存(相当于你想让别人进你的门,你就给他配钥匙)

3、对于一块已经创建好的共享内存,他的key被保存在描述该共享内存的内核数据结构里!(因为其他进程都要配钥匙,所以钥匙的模具肯定不能放在某个进程中,而是放到专属于这个内存块的内核数据结构中) 

4、 第一次创建共享内存的时候就需要一个key了,那么key从哪来??——>操作系统专门提供了一个ftok函数

问题1:为什么操作系统要提供ftok这样的函数来获取key呢??

——>这个key就相当于一把钥匙(必须具备唯一性),本质上也是也就是一串数字,如果我们随意设置一个key就可能会跟别的共享内存块的key起冲突,所以ftok就是操作系统为我们提供的一个获取key的算法,他可以通过参数(唯一路径和指定的工作id)来尽可能生成一个不会冲突的key(当然也有可能失败,只是因为路径具有唯一性,所以冲突的概率极小),这样后期想跟他使用同一个内存块的进程也就可以用同样的方法来获取key

问题2 : 为什么key要由我们自己通过ftok指定,而不是由操作系统直接帮我们生成??

——> 假设key由操作系统在我们申请共享内存的时候自动生成,那么由于进程具有独立性,你怎么能够把key交给另一个和你通信的进程呢??(你可能会想到用管道将key传过去,但如果我们使用了管道,那么共享内存就不是一个独立的方案了!)所以哪个进程和哪个进程会通信操作系统并不清楚,只有用户清楚。——>所以约定了由用户通过ftok来下达给操作系统生成key,这样后期只要你想让这个进程和之前的进程通信,你只需要和他传递一样的参数(保证不同进程看到同一份共享内存,那么ftok生成的必然是同一个key,就可以进行通信了!

5、因此key和路径一样,具有唯一性 

1.2.2 共享内存标示符 

shmget中返回值是int类型,其含义就是共享内存标示符

 

问题1:shmget返回值  vs  key 

——>key是操作系统内标定唯一性,而shmget返回值是在进程内标定唯一性

问题2:我怎么保证这个共享内存是否存在?

——>key是在内核数据结构中保存的,所以必然会将所有生成的key通过某种数据结构管理起来,这样我们在创建这个key的时候就可以去进行搜索,如果搜索到了就说明这个共享内存存在了!搜索不到就说明生成的是新的共享内存

我们还会发现无论是key还是shmid的返回值,都是一些很奇怪的数字,跟文件系统的fd规则不一样,这说明共享内存是一个独立的体系。

1.3 挂接共享内存——shmat

参数shmaddr:想让共享内存挂接到地址空间的什么位置,一般来说我们默认传null,这样就是由操作系统帮我们决定

参数shmflg:调整权限,比如说我们可以让该进程以只读的方式挂接,但是一般来讲都是默认和该共享内存的权限一样(传0)

其实shmdt和malloc有点像,都是在进程地址空间开辟一段连续的空间 ,返回值都是void* 需要进行一下强转

1.4 解除共享内存——shmdt 

进程退出的时候会自动解关联,但是我们也可以手动通过shmdt去解关联 

 

shmaddr:映射共享内存的那一块在进程地址空间的起始位置  

 shmdt和free有点像,参数都只要传起始地址即可——>这更说明了该共享内存在操作系统层面有相关的结构体,里面记录了共享内存的大小,又因为申请的内存是连续的,所以我们只需要知道起始地址就可以知道共享内存的范围

1.5 ipcs和ipcrm

 ipcs -m :可以查看当前的共享资源

nattch表示关联数 

ipcrm -m (shmid) :释放指定的共享内存

 

共享内存的生命周期是随内核的!!用户不主动关闭,共享内存会一直存在,除非内核重启!(因为操作系统并不知道到底有几个进程要使用这块共享内存,所以不能盲目地释放)

1.6 控制共享内存——shmctl

cmd: 

设置和标记删除的时候,第三个参数传null 

1.7 开始通信 

a文件:要创建和释放共享内存

b文件:不需要创建和释放共享内存 

 

读端:

写端:

      但是其实我们shmaddr就和相当于是我们自己可以使用的空间,所以我们其实不需要单独再搞个缓冲区!

 我们会发现:(1)对读方来说,一旦有人把数据写到共享内存里,里面就能看到了!!(不需经过系统调用)(2)对于写方来说,一旦将共享内存挂接到自己的地址空间上,就直接把他当做自己的空间来用就可以了!!(也不需要系统调用)

——>这说明 共享内存比管道快很多 ,因为采用的是地址空间映射到同一块内存的方法,所以拷贝的次数少了速度就快了!

1.8 共享内存的特点

1、共享内存是所有进程间通信中速度最快的!!(少拷贝)

2、共享内存没有同步和互斥的保护机制!(所以容易出现数据错乱的问题)

3、共享内存内部的数据由用户去维护!!(创建、使用、释放) 

1.9 共享内存的属性 

 

1.10 利用管道进行同步

       由于共享内存没有同步互斥的保护机制,所以很容易出现数据错乱(比如你这个命令写到一半就被读走了,然后由于没有收到完整指令执行不了 就会错乱),因此我们可以尝试用管道来帮助我们进行同步! 

 在写入完毕的时候再通过像管道写入来通知对方  你已经写完了 可以进行读取

     在读取之前,先向管道读取,看看是否收到了写方传达的通知,如果没读到就阻塞着等,读到了才会开始后面的读取命令! 

  问题:那为啥不直接用管道呢??

——>因为共享内存也是有自己的优点的,那就是拷贝次数少所以更快,因为如果在处理一些大数据的时候,通过管道这种较小的消耗来配合共享内存其实是更好的一种方案

二、消息队列原理

      管道是通过让不同进程看到同一个文件缓冲区,共享内促是看到同一个内存块,而消息队列就是看到同一个队列! 

 特点:允许不同的进程,向内核中发送带类型(因为消息队列是将信息以数据块的形式链接到消息队列中,可以双向通信,但其中的内容有别人的数据,也会有自己的数据,所以我们需要通过不同的类型来判断队列中的数据块是别人传的还是自己传的)的数据块! 

大部分接口的设置和共享内存相同 

 但有msgsnd和msgrcv,一个是发送一个是接受

    其参数msgp需要我们自定义一个“块”传过去,这个块包括类型(区分是自己的数据还是别人的数据),以及数据块信息 

三、IPC在内核中的数据结构设计

       其实共享内存、消息队列、信号量都隶属于System V接口,所以他们的接口在设计的时候非常相似,并且也都遵循使用key值。 

     我们会发现无论是  共享内存、消息队列还是信号量,他们都有一个ipc_perm 的结构体,里面存储着key值,并且他们key值的生成方法都是用ftok

——>所以操作系统内部将存储ipc_perm结构体的地址数据管理起来。

(1)可以让共享内存、消息队列、信号量都通过一种方式管理起来,ipc_perm结构体中有key值,方便我们在创建key的时候查看是否有冲突的key。

(2)ipc_perm就相当于是 基类 而包含他们的结构体对象就是 子类,而管理ipc_prem的数组就相当于是 虚函数表    所以其实这个Cpp中的继承和多态是一样的。只不过C语言没有这套规则,所以我们只能用这种方案(用不同的数据结构对象,里面包含着相同的数据结构对象,然后把他们相同的数据结构对象用数组管理起来,然后我们在访问的时候只需要对相关的指针做强转,就可以访问到对应的属性——>多态的本质:相同的指针,但是指针指向不同的对象访问的就是不同的信息)

(3)数组下标按道理是从0开始的,只不过有个起始计数器的概念,实际上是通过线性递增且会回绕的数组下标来定位的(因为我们用的越久可能数组下标就会越大,所以使用的时候会一直递增,直到满的时候再回绕   跟fd中的始终是最小的下标不一样)

四、信号量

4.1 概念铺垫

1、共享内存虽然很快,但是由于没有像管道一样的同步互斥的保护机制,所以当A写入的时候,刚写入一部分就被B拿走了,导致双方收发命令不一致——>共享资源如果不加以保护,就会存在数据混乱问题

2、 数据错乱本质上就是因为多个执行流同时访问公共数据——>所以我们希望任何时候都只有一个执行流在访问这个公共数据(互斥)

3、 任何时刻只允许一个执行流访问的资源,我们叫做临界资源(一般是内存空间)

4、一份代码中可能只有一部分代码是属于访问临界资源,所以我们把这一部分代码叫做临界区

4.2 理解信号量 

 信号量本质性就是一把计数器(描述临界资源中资源数量的多少)

其实就是某些临界资源可能很大,但是实际进程在访问的时候可能只访问其中的一小部分,所以如果我们能够做到以下三点:(1)讲临界资源进行合理拆分 (2)在执行流访问的时候对资源进行合理分配,不让多个执行流访问同一个资源(3) 引入计数器来了解当前是否还存在临界资源  ——>就可以促进多进程的并发运行,提高效率

 可能出现我们最害怕的两种情况:

(1)执行流不超过资源数目时却出现了多个执行流访问同一块资源的情况——>调整执行流访问时资源分配不合理的bug

(2)执行流超过资源数目必然会导致多个执行流访问同一块资源的情况——>在拆分资源的时候顺便搞个计数器,表明当前有多少资源还没被申请,当计数器为0的时候,其他任何执行流都进不来。

所以程序员把这个计数器叫做信号量 

 4.3 计数器

        因为执行流如果超过资源的数目必然会引发多个执行流访问同一块内存的情况,因此我们引入了计数器,并且规定每个执行流在访问共享内存之前,必须要先访问一下计数器 !(相当于看电影要先买票)

1、申请计数器如果成功,则表明我具有了访问特定资源的权限

2、申请了计数器资源,不代表就访问了,这是对资源的一种预定机制

3、计数器有效保证了访问共享内存的执行流数量 

 4、如果计数器为1,意味着只能有一个执行流访问该资源(这就是互斥)!,而我们把只有0、1两态的计数器叫做二院信号量(这就是锁!)

问题1:凭什么计数器为1?

——> 本质上就是希望该资源不要被分成很多块,而是当成一个整体去申请和释放!

问题2:访问临界资源必须先访问计数器资源,可是计数器不也是共享资源么??

——>没错!!所以计数器虽然承担着保护临界资源的任务,但是要想保护好别人也得先保护好自己!! (因为看不到其他进程在做什么,所以我们必须把它设置成原子的才能保证自身安全!

       在技术的角度,一条汇编语句默认就是原子的,因为已经没有办法做区分的,要么是没执行完要么是已经执行完了。

      从生活的角度,比如你的父亲问你以前学费才一千多,现在怎么要五六千了??你妈就会骂他说:儿子都上大学了能一样么,你平时关心过他的学习过程吗??要么就是不关心他,要么就是问他考了多少分——>所以此时你的学习经历对于你的父亲来说就是原子的

4.4 信号量操作

4.5 信号量总结

1、信号量本质上就是一把计数器,PV操作,原子的

2、执行流申请临界资源时,规定了必须先访问计数器申请信号量资源,申请成功了才能访问临界资源 

3、信号值如果是0,1两态的,二元信号量就是互斥功能

4、申请信号量的本质就是对资源的预定机制

5、多个信号量和信号量是几是不同的意思   前者表示有多个共享资源  后者表示一个共享资源的其中某个部分

终极问题:信号量凭什么是进程间通信的一种呢??

——>(1)通信不仅仅是通信数据,协同也是(协助进程间通信) (2)要协同 本质也是通信,且信号量首先要被所有的通信进程都看到!!(3)信号量的存在不是传输数据,而是让两个执行流更好地进行协同读取数据的(维持秩序——>不直接参与通信却影响着通信)。

4.6 mmap函数 

mmap也是一种共享内存技术  (System V的共享内存技术接口是最难的!)

一文读懂 mmap 原理 - 知乎 (zhihu.com)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue进阶之单组件开发与组件通信

书接上篇,我们了解了如何快速创建一个脚手架,现在我们来学习如何基于vite创建属于自己的脚手架。在创建一个新的组件时,要在新建文件夹中打开终端创建一个基本的脚手架,可在脚手架中原有的文件中修改或在相应路径重新创建&#xf…

【Linux网络编程】第四弹---构建UDP服务器与字典翻译系统:源码结构与关键组件解析

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、UdpServer.hpp 1.1、函数对象声明 1.2、Server类基本结构 1.3、构造函数 1.4、Start() 2、Dict.hpp…

数字IC后端设计实现之分段长clock tree经典案例

最近发现很多读者问到分段长clock tree的做法,小编今天给大家分享几个SoC芯片中复杂时钟结构设计的分段长clock tree的应用案例。希望对各位的学习和工作有所助益。 数字后端设计实现之时钟树综合实践篇 数字IC后端实现专家都具备哪些技能?&#xff08…

计算机毕业设计Spark+SpringBoot旅游推荐系统 旅游景点推荐 旅游可视化 旅游爬虫 景区客流量预测 旅游大数据 大数据毕业设计

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

EasyMedia播放rtsprtmp视频流

学习链接 MisterZhang/EasyMedia - gitee地址 EasyMedia转码rtsp视频流flv格式,hls格式,H5页面播放flv流视频 文章目录 学习链接介绍步骤easydarwin启动rtsp服务,ffmpeg推送摄像头(模拟rtsp视频流)nginx添加rtmp支持…

【Linux】开启你的Linux之旅:初学者指令指南

Linux相关知识点可以通过点击以下链接进行学习一起加油! 在 Linux 开发中,GDB 调试器和 Git 版本控制工具是开发者必备的利器。GDB 帮助快速定位代码问题,Git 则提供高效的版本管理与协作支持。本指南将简明介绍两者的核心功能与使用技巧&…

SpringBoot-问题排查 Controller全局打印入参,返回值,响应时间,异常日志

问题: 想要打印每次请求的入参,返回值,响应时间,异常日志,如果给每个方法挨个添加打印日志非常麻烦 解决方案: 使用切面的方式将所有的Controller每个方法加入切入点使用环绕通知的方式可以在切入点执行前后执行切面,符合我们的需求在方法执行前后打印相关日志忽略LogIgnore注解…

mysql数据库varchar截断问题

用了这么多年mysql数据库,才发现varchar是可以截断的,而且是在我们线上数据库。个人觉得dba的这个设置是非常有问题的,用户往数据库里存东西,就是为了以后用的,截断了存放,数据不完整,就用不了了…

C++草原三剑客之一:继承

为王的诞生献上礼炮吧! 目录 1 继承的概念及其定义 1.1 继承的概念 1.2 继承的定义 1.2.1 定义格式 1.2.2 继承方式以及继承基类成员访问方式的变化 1.3 继承类模板 2 基类和派生类之间的转换 3 继承中的作用域 3.1 隐藏规则 3.2 两道考察继承作用的相关…

Crash-SQLiteDiskIOException

目录 相关问题 日志信息 可能原因 问题排查 相关问题 蓝牙wifi无法使用 日志信息 可能原因 磁盘空间不足:当设备上的可用存储空间不足时,SQLite无法完成磁盘I/O操作,从而导致SQLiteDiskIOException。 数据库文件损坏:如果数…

6.824/6.5840 Lab 1: Lab 3: Raft

漆昼中温柔的不像话 静守着他的遗憾啊 旧的摇椅吱吱呀呀停不下 风卷走了满院的落叶落花 ——暮色回响 完整代码见: https://github.com/SnowLegend-star/6.824 在完成Lab之前,务必把论文多读几遍,力求完全理解Leader选举、log日志等过程。 …

【C++动态规划 BFS 博弈】3283. 吃掉所有兵需要的最多移动次数|2473

本文涉及知识点 C动态规划 CBFS算法 数学 博弈 LeetCode3283. 吃掉所有兵需要的最多移动次数 给你一个 50 x 50 的国际象棋棋盘,棋盘上有 一个 马和一些兵。给你两个整数 kx 和 ky ,其中 (kx, ky) 表示马所在的位置,同时还有一个二维数组 …

6.824/6.5840 Lab 2: Key/Value Server

故事里能毁坏的只有风景 谁也摧毁不了我们的梦境 弦月旁的流星划过了天际 我许下 的愿望 该向谁 去说明 ——我是如此相信 完整代码见: https://github.com/SnowLegend-star/6.824 还是那句话,尽量只是参考思路而不是照抄 先阅读几遍实验说明的Introd…

Linux-异步IO和存储映射IO

异步IO 在 I/O 多路复用中,进程通过系统调用 select()或 poll()来主动查询文件描述符上是否可以执行 I/O 操作。而在异步 I/O 中,当文件描述符上可以执行 I/O 操作时,进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其它的任务…

R 语言科研绘图第 1 期 --- 折线图-基础

在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…

企业中数据防泄漏如何防范?有哪些防泄密措施?

企业数据不仅是业务运营的核心,也是企业竞争力的关键所在。 然而,随着信息技术的快速发展,数据泄露的风险也随之增加。 数据一旦泄露,不仅可能导致企业经济损失,还可能损害企业声誉,甚至引发法律纠纷。 …

汽车控制软件下载移动管家手机控车一键启动app

移动管家手机控制汽车系统是一款实现车辆远程智能控制的应用程序‌。通过下载并安装特定的APP,用户可以轻松实现以下功能:‌远程启动与熄火‌:无论身处何地,只要有网络,即可远程启动或熄火车辆,提前预冷或预…

基于事件驱动构建 AI 原生应用

作者:寒斜 AI 应用在商业化服务的阶段会面临诸多挑战,比如更快的服务交付速度,更实时、精准的结果以及更人性化的体验等,传统架构限制于同步交互,无法满足上述需求,本篇文章给大家分享一下如何基于事件驱动…

如何查看阿里云ddos供给量

要查看阿里云上的 DDoS 攻击量,你可以通过阿里云的 云盾 DDoS 防护 服务来进行监控和查看攻击数据。阿里云提供了详细的流量监控、攻击日志以及攻击趋势分析工具,帮助用户实时了解 DDoS 攻击的情况。以下是九河云总结的查看 DDoS 攻击量的步骤&#xff1…

华为HarmonyOS 让应用快速拥有账号能力 - 获取用户手机号

场景介绍 当应用对获取的手机号时效性要求不高时,可使用Account Kit提供的手机号授权与快速验证能力,向用户发起手机号授权申请,经用户同意授权后,获取到手机号并为用户提供相应服务。以下只针对Account kit提供的手机号授权与快…