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.xdnf.cn/news/18852.html

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

相关文章

Vue基础(2)_el和data的两种写法

举例&#xff1a; <div id"root"><h1>你好&#xff0c;{{name}}</h1> </div> el和data的2种写法 1.el有2种写法 (1).new Vue时候配置el属性。 // 第一种写法&#xff1a;new Vue时候配置el属性。// 优点&#xff1a;简单、直接new Vue({e…

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

文章目录 前言1. 指令要处理的数据有多长&#xff1f;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 问题背景&…

c++多态(深度刨析)

C系列-----多态 文章目录 C系列-----多态前言一、多态的概念二、多态的定义及实现2.1、多态构成的条件2.1.1、虚函数2.1.2、虚函数的重写 2.2、C11 override 和 final2.3、重载、覆盖(重写)、隐藏(重定义)的对比2.4、抽象类2.5、 接口继承和实现继承 三、多态的原理3.1、虚函数…

FPGA开发技能(9)快速生成约束XDC文件

文章目录 1.从Cadence导出csv约束文件2.python程序将csv导出为xdc文件。3.python生成exe4.exe使用注意事项5.传送门 前言&#xff1a; 作为一名FPGA工程师&#xff0c;通常公司会对该岗位的人有一定的硬件能力的要求&#xff0c;最基础的就是需要依据原理图的设计进行FPGA工程内…

css uniapp背景图宽度固定高度自适应可以重复

page {height: 100%;background-image: url(https://onlinekc.a.hlidc.cn/uploads/20241115/350f94aaf493d05625a7ddbc86c7804e.png);background-repeat: repeat;background-size: contain;} 如果不要重复 把background-repeat: repeat;替换background-repeat: no-repeat;

Stable Diffusion核心网络结构——U-Net

​ &#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&a…

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

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AnimationMixer 动画…

【Linux】指令 + 重定向操作

Linux基本指令 一.Linux基本指令1.mv&#xff08;重要&#xff09;2.cat3.more和less&#xff08;重要&#xff09;4.head和tail5.date6.cal7.find&#xff08;重要&#xff09; 二.Linux相关知识点1. Linux系统中&#xff1a;一切皆文件2. 重定向操作1. 输出重定向2. 追加重定…

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

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

Vue3.0 + Ts:动态设置style样式 ts 报错

error TS2322: Type ‘{ width: string; left: string; ‘background-color’: unknown; ‘z-index’: number; }’ is not assignable to type ‘StyleValue’ 在 vue3.0 ts 项目中&#xff0c;动态设置样式报错 在 Vue 3 TypeScript 项目中&#xff0c;当你使用 :style 绑…

跨平台WPF框架Avalonia教程 十六

SelectableTextBlock 可选文本块 SelectableTextBlock 块是一个用于显示文本的标签&#xff0c;允许选择和复制文本。它可以显示多行&#xff0c;并且可以完全控制所使用的字体。 有用的属性​ 您可能最常使用这些属性&#xff1a; 属性描述SelectionStart当前选择的起始字…

【MySQL】库的基础操作入门指南

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;MySQL入门指南&#xff1a;从零开始的数据库之旅 欢迎大家点赞收藏评论&#x1f60a; 目录 ☁创建数据库语法说明&#xff1a; 创建数据库案例 &#x1f308;字符集和校验规则查看系统默认字符集…

数据仓库数据湖湖仓一体解决方案

一、资料介绍 数据仓库与数据湖是现代数据管理的两大核心概念。数据仓库是结构化的数据存储仓库&#xff0c;用于支持企业的决策分析&#xff0c;其数据经过清洗、整合&#xff0c;以固定的模式存储&#xff0c;适合复杂查询。数据湖则是一个集中存储大量原始数据的存储库&…

人工智能英伟达越来越“大”的GPU

英伟达&#xff1a;让我们遇见越来越“大”的GPU 在2024年台北ComputeX大会上&#xff0c;英伟达CEO黄仁勋发表了题为《揭开新工业革命序幕》的演讲。他手持一款游戏显卡(很有可能是4090),自豪地宣称&#xff1a;“这是目前最先进的游戏GPU。”紧接着&#xff0c;他走到一台DGX…

知识库搭建:高科技行业的智慧基石与未来展望

一、引言 在科技日新月异的今天&#xff0c;知识密集型作业已成为高科技企业竞争力的核心。面对快速的技术迭代和激烈的市场竞争&#xff0c;如何高效地管理和运用知识资源&#xff0c;成为高科技企业必须面对的挑战。知识库&#xff0c;作为知识管理的核心平台&#xff0c;正…

算法编程题-删除子文件夹

算法编程题-删除子文件夹 原题描述设计思路代码实现复杂度分析 前一段时间面试字节的时候&#xff0c;被问到gin框架的路由结构。gin框架的路由结构采用的一般是前缀树来实现&#xff0c;于是被要求手写前缀树来实现路由的注册和查找。 本文以 leetcode 1233为例介绍一下前缀树…

利用SSH中的弱私钥

import paramiko import argparse import os from threading import Thread, BoundedSemaphore # 设置最大连接数 maxConnections 5 # 创建一个有界信号量&#xff0c;用于控制同时进行的连接数 connection_lock BoundedSemaphore(valuemaxConnections) # 用于控制是否停止所…

力扣整理版七:二叉树(待更新)

满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。深度为k&#xff0c;有2^k-1个节点的二叉树。 完全二叉树&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&am…

如何使用可靠UDP协议(KCP)

希望这篇文章&#xff0c;对学习和使用 KCP 协议的读者&#xff0c;有帮助。 1. KCPUDP 流程图 2. 示例代码 #include <iostream>int main() {// 代码太多&#xff0c;暂存仓库return 0; } 具体使用&#xff0c;请参考代码仓库&#xff1a;https://github.com/ChivenZha…

论文复述:(TRPCA)t-Shatten-p

一个基于TNN-TRPCA的简单创新的论文&#xff0c;Tensor Robust PCA主要是将一个tensor分解为low-rank和sparse两个component&#xff0c;主要思想是引入了weighted tensor Schatten-p norm进行建模。