Linux :进程间通信之管道

一、进程间通信

1.1 是什么和为什么

1、进程间通信是什么??

——>两个或多个进程实现数据层面的交互,但是由于进程独立性的存在,导致通信的成本比较高。

2、既然通信成本高,那为什么还要通信呢??

——> 在某些场景下我们需要不同进程之间进行(1)基本数据的交互。(2)发送命令。(3)实现某种协同。(4)通知某些信息……

1.2 如何实现进程间通信 

1、进程间通信的本质:要想办法让不同的进程看到同一份资源(以特定形式存在的内存空间)! 

2、这个资源必须由操作系统提供!!

问题: 为什么必须由操作系统提供呢??难道不能由其中一个进程提供么??

——>假设由一个进程提供,而我们又让另一个进程看到这份资源,那么这份资源应该属于谁呢?? ——>所以这种做法会破坏进程的独立性!!所以我们必须需要第三方空间,因此只能由操作系统提供!

      举个例子:保持进程独立性,可以理解为就是我俩不能见面,就好比说一个绑匪把你绑架了,但是他并不会直接去你家取赎金,也不会让你家人把赎金送到自己的住所,而是会要求你的家人把钱放到一个地方,然后你再去取。

 3、我们进程访问这个空间,本质上就是在访问操作系统!!

 ——>因为进程代表的就是用户,而操作系统并不相信用户,所以“资源”从创建、使用再到释放,必须使用系统调用接口!! 

4、一般操作系统,会有一个独立的通信模块——隶属于文件系统——IPC通信模块  其中有两套标准 system V && posix

5、基于文件级别的通信方式——管道 

 1.3 进程间通信的方案设计

     进程是具有独立性的,但是早期有的人发现我们很多时候需要通信,比如果进程需要通信,网络也需要通信,所以大家发现通信很重要,另一方面由于通信模块的设计相对简单,且可实现方案多样化,因此大家你做你的,我做我的,导致现在很多Linux发型版本都不一样,即使一样,内部的标准也不一样!  

     因此当市面上出现了各种各样的通信方案后,我们需要做两件事 (1)选择一个最合适的方案。(2)需要去定制一个标准(这样强迫大家都遵守这个规则,不同的操作系统在设计的时候就不会有太大差异!!)

 ——>互联网行业标准十分重要,就好比为什么你的华为手机和苹果手机差异很大,但是却能实现通信! 就是因为各个领域都会有佼佼者在定制互联网标准!这样才能保证设备之间的通信!!    ——>所以标准如果没制定好,往小了说就是功能交互实现不了,往大了说互联网和物联网都实现不了!! 所以技术无论再怎么自由,背后都必须遵循一套标准,否则就是不入流!

      但是这个标准如何制定呢??可能很多不同的机构和公司都提供了不同的通信方案,而定制标准是有版权的,就比如说有的标准用的是欧美的标准,就得付专利费,所以大家都在争这个,谁也不服谁(但其实无非就是代码有点差异)。但是标准肯定不是谁想定就能定的 必须满足(1)能力强且德高望重 (2)技术方案特别成熟,可以让其他人自愧不如!

 1.4 进程通信的分类

管道:

匿名管道pipe

命名管道

System V IPC:

System V 消息队列

System V 共享内存

System V 信号量

POSIX IPC:

消息队列

共享内存

信号量

互斥量

条件变量

读写锁

1.5 进程通信的目的 

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源。

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.6 一些现状

       大部分互联网公司其实都想做一些低成本高收入的应用,想赚快钱,其实对研发一些新东西的使命感很低。 

      如果做社会导向的一些业务是值得尊重,但是很多公司并没有这种魄力,因为一旦产业路径形成后,后期就很那去转型。

      在这方便比较有责任使命感的是华为,因为华为一方面在做一些商业化的东西,一方面也会去搞研发,他其实推动了国内很多东西的发展,比如鸿蒙、芯片…… 区别就是不会像别的公司一样,别人用什么就用什么。

二、管道文件

2.1 管道的原理

        既然一个文件可以被多个进程打开,那么文件其实就算是一个公共资源,其实也可以做到一个进程读一个进程写,从而实现通信

——>但是这样会有一个问题,就是数据必须要写到外设上,所以就会有效率方面的问题

——>因此我们希望这个通信能在内存里面去进行而不牵扯到外设,所以我们需要有一个“内存级文件”概念(让用户能够通过扫描目录结构看到文件,但是只存在于内存中),然后我们需要让操作系统知道不要把内存级文件刷到磁盘上。而是只保留在缓冲区里。 

 ——>管道就是一个内存级文件,其中又根据方案的不同,存在匿名管道命名管道!

2.2 匿名管道

      接下来我们要思考的就是,我们究竟如何让两个进程看到同一份资源并实现通信,所以第一个方案就是——>父子进程!!因为子进程可以做到和父进程看到同一份代码,所以可以尝试让父进程和子进程进行通信!

       创建子进程的时候,pcb和文件描述符表,肯定是要拷贝的,但是并不会创建新的文件!!因为进程管理和文件系统是两个模块(你能打开我是因为操作系统,你还敢要求这么多??我们是平级!!)

    但是常规的文件虽然可以被看到,但是必然会被刷新到磁盘上,所以我们不想让他刷新的话,就必须引入“内存级文件”——匿名管道,这样操作系统能区分开来

用fork来共享管道原理:

站在文件描述符角度-深度理解管道:

问题1:父进程和子进程一个读一个写实现了通信,但是如果其中一方不小心关掉了会不会导致另一方出错呢??

——>理论上是有可能的,但是操作系统已经考虑到了,就是通过引用计数

问题2:管道文件是只读方式打开的。所以子进程继承的时候也只有只读权限,那么两个都是读就没办法通信了吗??

——>所以我们必然不能在父进程中用open打开这个文件,而是用pipe这个专门为管道文件设置的接口,  普通文件需要通过open打开,而管道文件在设计的时候得用pipe,一边以只读方式打开,一边以只写的方式打开(占据了两个fd的位置),然后关掉其中一个不用的,然后继承给子进程也关掉一个不用的,最后实现父子进程一个是写端一个是读端的单向通信(这就是管道这个名字的由来)。

——>管道在设计的时候就是不能支持同时读写的!

——>另一方面 open是有路径的,因为他是一个真实存在的文件,而我们如果想打开一个内存级文件,就必须用接口pipe! 

       参数是一个 pipefd[2] 输出型参数 他会在该进程的文件描述符表中找到两个下标最小的位置,然后一个为读端打开的fd 放在pipefd[0]中  一个为写端打开的fd 放在pipefd[1]中  这样用户可以通过这个输出型参数拿到fd  关掉其中一个不用的  然后进行使用 

     成功了返回0  不成功返回-1  

站在内核角度-管道本质:

     所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。  

2.3 匿名管道的设计

makefile: 

1、建立通信

2、实现读写方法  

 fd的规则就是 无论这个文件是什么文件 我们都可以去进行操作

问题:我们为什么不直接定义出全局变量,这样fork之后至子进程不就能看到了吗??

——>这样是继承而不是通信,因为我们要拿到的数据可能是会变化的,这样通信才有意义

 2.4 匿名管道的特征

1、具有血缘关系的进程进行进程间通信

2、管道只能单向通信(实现双向通信就必须有两个管道)

3、父子进程是会进程协同、同步与互斥的 ——>保护管道文件的数据安全

        对于父进程来说,子进程写了多少次根本不重要,只要管道里有数据,有多少就会读多少,前提条件是我们缓冲区足够大。也就是说,当子进程向管道写满了,当父进程在读的时候,就会把多次写的信息一次读了出来,在父进程看来,它读到的就是一个一个的字符,对于我们用户用什么存取,如何区分,这是我们用户的事。所以我们得出一个管道的特点,管道是面向字节流的

4、管道是面向字节流的

       写端写了自己,我不管,反正我读端默认会把整个缓存区都读出来(因为在我看来缓冲区就是一个个字节) 至于你要把读出来的这些怎么做分离,是你用户的事情,不是我这个管道应该操心的——>这种特点就是字节流

       就好比石油管道,无论你放多少石油,我都会流向出口,但是你在出口是要用碗接,还是用桶接,我一点也不关心,那是你自己的事情

 ——>所以如果我们想改变这种字节流的话,比如说希望一次性只读取15个字节,那么就需要涉及到协议的知识!

5、管道是有固定大小的(Linux中是64KB)且具有原子性,但是在不同的内核里可能有区别 

 验证管道大小:

最后写到65536说明管道大小是64kb 

ulimit 命令用于限制 shell 进程及其所创进程的资源使用

 ulimit -a查看到的pipe size一次原子写入为:512 bytes * 8 = 4096 bytes 。(4KB)

问题:可以我们验证的时候管道大小是64kb,那为什么pipesize是4kb呢??

——> 因为管道具有原子性!!pipesize的意思是管道一次原子写入的大小,意思就是只要你写入的内容不超过这个大小,那么在你写的期间,父进程不会来读!

 6、管道是基于文件的,而文件的生命周期是随进程的

 2.5 管道中的四种情况

1、读写端正常,管道如果为空,那么读端就会阻塞 (防止读入一些垃圾数据)

2、读写端正常,管道如果被写满,写端就要阻塞  (防止覆盖之前的数据)

——>父子进程是会进程协同的,同步和互斥的 都是为了保护管道文件的数据安全!! 

3、读端正常读,写端关闭,读端最后会读到0,表明读到了文件pipe结尾,不会被阻塞!

所以读端(父进程读到0的时候)发现读到文件结尾,没有读的意义了 就可以break。

4、写端正常写入,但是读端关闭了,操作系统就要杀掉正在写入的进程——>通过信号杀掉!

   首先我们要知道,操作系统是不会做低效、浪费资源和时间等类似的工作的,如果做了,操作系统就是bug;所以我们想,写端正常,读端关闭后,还有实际意义吗?没有了!因为写满了又怎样呢,又没有进程去读,所以当写端正常,读端关闭了,操作系统就要 kill 掉正在写入的进程。如何 kill 呢?通过信号,其实操作系统会使用13号信号 SIGPIPE kill 掉正在写入的进程

    所以为什么我们一般让父进程读子进程写呢??因为这样我们父进程可以在回收子进程的时候检测到子进程是被信号杀死的,还是正常退出的!!

2.6 管道的应用场景 

       那么我们上面学的管道,和我们以前学过的哪些有关系呢?

1、首先我们以前接触过 | 这个符号,其实这个就是管道,例如我们在多条指令中使用 |

     我们会发现他们的PID不一样,但是PPID是一样的,说明他们的父进程都是bash,而他们是具有血缘关系的进程,所以“ | ”就相当于操作系统为他们创建了匿名管道来实现通信!

 2、可以实现进程池(通过系统调用的的次数来提高内存的申请速度)

 所以,当父进程想布置任务的时候,无非就是做两件事,一就是选择任务,二是选择进程。

2.7 命名管道 

      上面我们学到的匿名管道是没有名字的,因为打开那个文件的时候并没有告诉我们文件名,也就是管道并没有命名。我们直接以读方式写方式打开父子进程,各自拿一个读写端就可以通行。正是因为它没有名字,那么所以匿名管道必须得让我们对应的父子进程看到通信资源,它采用的是让父子继承的方案看到的

     如果毫不相关的进程进行进程间通信呢??所以我们需要有下一个方案叫做命名管道。接下来我们先使用一下命名管道,先看现象再解释。其中建立命名管道的接口为 mkfifo  

那么如何实现通信呢??我们创建两个终端,一个读一个写观察

 该管道看起来是在磁盘中存在,但是它实际数据并不会刷新到磁盘上。

 我们一直往管道里写,管道的大小都不会发生变化

问题1:父子进程可以通过继承看到同一个文件,那两个毫不相关的进程,我怎么知道这俩进程打开的是不是同一个文件呢??

——>同一路径下的文件名:路径+文件名 (唯一性)

命名管道有自己的名字,所以他的体系还是跟文件一模一样的体系,只不过区别是他不会刷盘!

问题2:如果两个进程打开同一个文件,在内核中,操作系统会打开几个文件呢??

——> 只会打开一个文件,维护一个缓冲区

      难道不怕两个进程写在缓冲区会混乱么??——>本身两个进程同时打开一个不受保护的文件,即使有两个缓冲区,写入也是会混乱的,所以你用户都不怕了,我操作系统怕什么??? 

 

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

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

相关文章

“乐鑫组件注册表”简介

当启动一个新的开发项目时,开发者们通常会利用库和驱动程序等现有的代码资源。这种做法不仅节省时间,还简化了项目的维护工作。本文将深入探讨乐鑫组件注册表的概念及其核心理念,旨在指导您高效地使用和贡献组件。 概念解析 ESP-IDF 的架构…

ATmaga8单片机Pt100温度计源程序+Proteus仿真设计

目录 1、项目功能 2、仿真图 ​3、程序 资料下载地址:ATmaga8单片机Pt100温度计源程序Proteus仿真设计 1、项目功能 设计Pt100铂电阻测量温度的电路,温度测量范围是0-100摄氏度,要求LCD显示。画出电路图,标注元器件参数&am…

【代码pycharm】动手学深度学习v2-05 线性代数

课程链接-05 线性代数 可以先看完特定轴求和再去看p2 import torch xtorch.tensor([3.0]) ytorch.tensor([2.0]) #标量 print(1.标量只有一个元素:\n,xy,x*y,x/y,x**y) x2torch.arange(4) #向量 print(2.向量视为标量值组成的列表:\n,x2) print(3.访问张…

SpringBoot源码解析(四):解析应用参数args

SpringBoot源码系列文章 SpringBoot源码解析(一):SpringApplication构造方法 SpringBoot源码解析(二):引导上下文DefaultBootstrapContext SpringBoot源码解析(三):启动开始阶段 SpringBoot源码解析(四):解析应用参数args 目录…

ZSTD 内存泄漏问题

优质博文:IT-BLOG-CN Zstandard(简称zstd)是一种无损压缩算法,由Facebook开发并开源。它旨在提供高压缩比和高解压速度的平衡,适用于多种数据压缩需求。 特点 【1】高压缩比: zstd能够在保持较高压缩比的…

前端:HTML (学习笔记)【1】

一,网络编程的三大基石 1,URL (1)url —— 统一资源定位符: 网址——整个互联网中可以唯一且准确的确定一个资源的位置。 【项目外】 网址——https://www.baidu.com/ …

【C++动态规划】3148. 矩阵中的最大得分|1819

本文涉及知识点 C动态规划 LeetCode 3148. 矩阵中的最大得分 给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧的任意单元格(不必相邻)。从值为 c1 的单元格移动到值为 c2 的单元格的得…

STM32完全学习——使用标准库点亮LED

一、使用标准库建立工程 (1)首先我们在ST的网站上面,下载标准库 (2)将标准外设库加入到项目中 我们一般只会使用到红色标注的那个文件夹,我们一般也只会将这个文件夹导入到工程里面,其他的还有…

解决微信小程序自定义tabbar点击两次才能跳转

在每个页面的js文件下加上此代码,selected属性代表每一个页面的下标,在不同的js文件下,要对应不同的selected值 代码: onShow() { // 确保 TabBar 存在并且设置选中项 if (this.getTabBar && this.getTabBar()) { this.…

学习threejs,使用AnimationMixer实现变形动画

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.AnimationMixer 动画…

Solana应用开发常见技术栈

编程语言 Rust Rust是Solana开发中非常重要的编程语言。它具有高性能、内存安全的特点。在Solana智能合约开发中,Rust可以用于编写高效的合约代码。例如,Rust的所有权系统可以帮助开发者避免常见的内存错误,如悬空指针和数据竞争。通过合理利…

【汇编语言】数据处理的两个基本问题(二) —— 解密汇编语言:数据长度与寻址方式的综合应用

文章目录 前言1. 指令要处理的数据有多长?1.1 通过寄存器指明数据的尺寸1.1.1 字操作1.1.2 字节操作 1.2 用操作符X ptr指明内存单元的长度1.2.1 访问字单元1.2.2 访问字节单元1.2.3 为什么要用操作符X ptr指明 1.3 其他方法 2. 寻址方式的综合应用2.1 问题背景&…

【算法】【优选算法】前缀和(下)

目录 一、560.和为K的⼦数组1.1 前缀和1.2 暴力枚举 二、974.和可被K整除的⼦数组2.1 前缀和2.2 暴力枚举 三、525.连续数组3.1 前缀和3.2 暴力枚举 四、1314.矩阵区域和4.1 前缀和4.2 暴力枚举 一、560.和为K的⼦数组 题目链接:560.和为K的⼦数组 题目描述&#x…

分布式cap理论学习

【分布式】CAP理论详解 一致性(Consistency) 代表数据在任何时刻,任何分布式节点,看到的都是符合预期的。有点类似于幂等,无论访问哪个节点,得到结果数据一致。 可用性(Availability) 强调的是任意时刻一定能读到数据&#xff…

主机型入侵检测系统(HIDS)——Elkeid在Centos7的保姆级安装部署教程

一、HIDS简介 主机型入侵检测系统(Host-based Intrusion Detection System 简称:HIDS);HIDS作为主机的监视器和分析器,主要是专注于主机系统内部(监视系统全部或部分的动态的行为以及整个系统的状态)。 HIDS使用传统的C/S架构,只需要在监测端安装agent即可,且使用用户…

Python蓝桥杯刷题1

1.确定字符串是否包含唯一字符 题解:调用count函数计算每一个字符出现的次数,如果不等于1就输出no,并且结束循环,如果等于1就一直循环直到计算到最后一个字符,若最后一个字符也满足条件,则输出yes import…

【ARM】MDK在debug模式下的Registers窗口包含哪些内容

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决客户对于Debug模式下,对于Registers窗口包含的内容了解。 2、 问题场景 Registers窗口是在进入到debug模式下后,就会出现一个窗口。窗口中包含了很多寄存器信息。但是对于具体内容不了解…

河道无人机雷达测流监测系统由哪几部分组成?

在现代水利管理中,河道无人机雷达监测系统正逐渐成为一种重要的工具,为河道的安全和管理提供了强大的技术支持。那么,这个先进的监测系统究竟由哪几部分组成呢? 河道无人机雷达监测系统工作原理 雷达传感器通过发射电磁波或激光束…

浅谈数据仓库的架构及其演变

一、数据仓库分层架构 数据仓库分层一般分为三层,分别为数据仓库ODS层(数据进出口贴源层)、CDM层(数据公共层)和ADS层(数据应用层)。 1. ODS层:这是数据仓库的最底层,直接…

event_base

build default event_base event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。如果发生错误,则返回NULL。选择各种方法时,函数会选择OS支持的最快方法。 event_base_new…