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)

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

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

相关文章

Python数组拆分(array_split())

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

微信小程序——文档下载功能分享(含代码)

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

LabVIEW氢气纯化控制系统

基于LabVIEW的氢气纯化控制系统满足氢气纯化过程中对精确控制的需求,具备参数设置、过程监控、数据记录和报警功能,体现了LabVIEW在复杂工业控制系统中的应用效能。 项目背景 在众多行业中,尤其是石油化工和航天航空领域,氢气作为…

【linux】(23)对象存储服务-MinIo

MinIO 是一个高性能的对象存储服务,兼容 Amazon S3 API。 Docker安装MinIo 前提条件 确保您的系统已经安装了 Docker。如果还没有安装 Docker,可以参考 Docker 官方文档进行安装。 1. 拉取 MinIO Docker 镜像 首先,从 Docker Hub 拉取 Mi…

(超详细图文)PLSQL Developer 配置连接远程 Oracle 服务

1、下载配置文件 (超详细图文详情)Navicat 配置连接 Oracle-CSDN博客 将下载的文件解压到单独文件夹,如:D:\App\App_Java\Oracle\instantclient-basic-windows.x64-19.25.0.0.0dbru 2、配置 打开 PLSQL Developer,登…

【网络篇】HTTP知识

键入网址到网页显示,期间发生了什么? 浏览器第一步是解析URL,这样就得到了服务器名称和文件的路径名,然后根据这些信息生成http请求,通过DNS查询得到我们要请求的服务器地址,然后添加TCP头、IP头以及MAC头&…

C 语言学习的经典书籍有哪些?

学习C语言的理由 C语言是一种程席设计语言,它是由美国AT&T公司贝尔实验室的Dennis Ritchie于1972年发明的。C语言之所以流行,是因为它简单易用。学习C语言的几个理由如下: (1)C、C#和Java使用一种被称为面向对象程序设计(0bject-Orient…

webrtc ios h264 硬编解码

webrtc ios h264 硬编解码 一 ios 系统支持 从ios8开始,苹果公司开放了硬解码和硬编码API(即 VideoToolbox.framework API) 二 主要api 1 主要解码函数 VTDecompressionSessionCreate // 创建解码 session VTDecompressionSession…

RVO动态避障技术方案介绍

原文:RVO动态避障技术方案介绍 - 哔哩哔哩 我们在开发游戏的时候经常会遇到这样的问题,当我们寻路的时候,其它人也在寻路,如何避免不从其它人的位置穿过。这个叫做动态避障,目前主流的解决方案就是RVO。本节我们来介绍…

(免费送源码)计算机毕业设计原创定制:Java+ssm+JSP+Ajax SSM棕榈校园论坛的开发

摘要 随着计算机科学技术的高速发展,计算机成了人们日常生活的必需品,从而也带动了一系列与此相关产业,是人们的生活发生了翻天覆地的变化,而网络化的出现也在改变着人们传统的生活方式,包括工作,学习,社交…

对比学习与自监督任务

对比学习与自监督任务 笔者在之前上课时候被迫接受学习了NLP的许多相关的知识,BERT GPT等许多的NLP领域的大模型,都采用了半监督或者说是无监督的训练方法,最近在视觉的领域视觉自监督的模型受到了越来越多的关注。现在需要自己了解一下自监督…

SpringCloud2~~~

Nacos Nacos就是替代 注册中心【Eureka】 和 配置中心【Config】 支持AP和CP,可以切换 了解即可 下载和运行 下载版本(找自己想要的版本):Tags alibaba/nacos GitHub 本地有良好的 Java8 Maven环境 解压安装包,直接…

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

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

VPS默认是通过密钥文件登陆机器,编译~让机器能直接通过root密码登陆。

SSH 登录机器 登陆机器 输入命令切换到root权限并修改密码: sudu su #切换root权限psddwd #修改密码 修改登陆方式 输入命令: vi /root/.ssh/authorized_keys 找到 “ssh-rsa”字样, 按键盘 ” i ” 进入编辑模式&#xf…

map用于leetcode

//第一种map方法 function groupAnagrams(strs) {let map new Map()for (let str of strs) {let key str ? : str.split().sort().join()if (!map.has(key)) {map.set(key, [])}map.get(key).push(str)} //此时map为Map(3) {aet > [ eat, tea, ate ],ant > [ tan,…

文件比较和文件流

文件比较和文件流 一、文本比较工具 diff1.基本用法1.1输出格式 2.常用选项 二、文件流1.文件的打开模式2.文件流的分类ifstreamofstreamfstrem区别 3.文件流的函数1. 构造函数2. is_open 用于判断文件是否打开3. open4. getline5. close6. get()7. read8. write9. put10. gcou…

《如何使用Unity的Avatar人偶以及启动重定向-实现2个或多个人物模型使用同一个动画片段》

8.5 使用Avatar和人物重定向 注意事项: 这个人偶以及重定向技术只能作用于人物模型! 这个人偶以及重定向技术只能作用于人物模型! 这个人偶以及重定向技术只能作用于人物模型! 1. 基本原理 在Unity中,Avatar人偶和…

windows 应用 UI 自动化实战

UI 自动化技术架构选型 UI 自动化是软件测试过程中的重要一环,网络上也有很多 UI 自动化相关的知识或资料,具体到 windows 端的 UI 自动化,我们需要从以下几个方面考虑: 开发语言 毋庸置疑,在 UI 自动化测试领域&am…

百度 文心一言 vs 阿里 通义千问 哪个好?

背景介绍: 在当前的人工智能领域,随着大模型技术的快速发展,市场上涌现出了众多的大规模语言模型。然而,由于缺乏统一且权威的评估标准,很多关于这些模型能力的文章往往基于主观测试或自行设定的排行榜来评价模型性能…

基于协同推荐的黔醉酒业白酒销售系统

文末获取源码和万字论文 摘 要 基于协同推荐的黔醉酒业白酒销售系统主要针对黔醉酒业的具体业务需求所设计,现阶段阶段我国大型企业都会有自己的电商平台以及销售管理系统,其功能对于中小型过于冗长复杂,成本也不是中小型企业能够承受的&…