进程间通信 (一)【管道通信(上)】

目录

  • 1. 概况
  • 2. 管道通信的原理
    • 2.1 初步理解
    • 2.2 深入理解

1. 概况

  • 是什么:两个及以上的进程实现数据层面的交互,称为进程间的通信。

    因为进程独立性的存在,所以一个进程无法直接访问另一个进程的数据,即便是父子进程,子进程也只能看到父进程所共享的代码和数据,一旦一方有数据写入行为,就会发生写时拷贝,所以进程通信的成本是比较高的。

  • 为什么:因为有通信的需求,所以多进程直接需要进行通信,场景可以有如下几种:

    • 进程间发送基本数据
    • 发送命令(让一个进程通过对另一个进程发送一些命令达到控制另一个进程的目的)
    • 协同工作
    • 通知进程(让 A 进程告知 B 进程某些事件发生,也可以是一个父进程只负责监听其所有子进程的,当条件满足时,通知唤醒子进程去协同工作等等)

  • 怎么办:

    • 进程间是互相独立的,又为了保证不打破进程之间的独立性,但又必须让不同的进程看到同一份 “资源”,这样才能够让进程间进行通信。
    • 这一份让不同进程都能访问的资源是一段特定形式的内存空间,一般由操作系统提供。而为什么这段资源不能由一方的进程提供呢?还是因为进程之间具有独立性,如果有一方提供,那么其它进程要访问该资源,就等价于访问这个进程的数据,这样就破坏了进程的独立性。因此由第三方提供最合适不过了。
    • 所以进程访问这段空间进行通信,本质就是访问操作系统。而进程代表着用户,操作系统又不允许用户直接访问它,因此该 “资源” 从创建 ---- 使用 ---- 释放,都只能通过系统调用接口来实现。
    • 操作系统为了进程间能够通信,并且不能破坏进程的独立性,必然要从底层设计、接口设计,都要由操作系统自己独立设计。而一般的操作系统,会有一个独立的通信模块,隶属于文件系统 ------ IPC通信模块。
    • 关于操作系统的通信模块,很多企业都在做,因此就会存在各种各样的通信方案,所以需要定制一套通信标准。因此进程间通信是有标准的,System V && POSIX。(因为通信有标准,所以不同品牌设备,不同的底层硬件也好,不同的操作系统之间,才能够进行通信,比如我的 windows 依旧可以给 mac 电脑的你发信息等等)
    • 除此之外,还有一种文件级别的通信,即管道通信。

2. 管道通信的原理

在这里插入图片描述

who 命令:查询当前连接主机的用户量
wc -l :统计行数量
这两个命令在将来运行起来都是一个进程,而管道的整体逻辑就是将 who 这个进程的输出信息写入管道文件,再由 wc 这个进程读取数据,然后做标准输出给用户层。

一个进程被创建,有 task_struct 内核数据结构,里面有一个 files_struct 结构体,结构体内存储了 struct file* fd_array[ ] 这样的文件描述符表,存储指向打开文件对象的指针,并且默认打开三个标准输入输出流。当我们打开一个文件,操作系统创建 struct file 对象,然后为该文件分配一个没有被使用的最小的文件描述符,将 struct file 的地址填到文件描述符表对应的下标处。

不仅如此,每个文件都有自己独立的 inode,提供一个用于各种底层设备读写的 file_operators 方法集,里面存储的是指向各个底层外设读写方法的函数指针,以此来实现一切皆文件的理念。接着,每个文件还要提供属于自己的文件页缓冲区。而不管是该文件是被创建于磁盘中,还是从磁盘中打开的,对文件做读写时,都需要先将文件的数据加载到内存中,然后再内存中做修改,修改完后再刷新回磁盘(只读取文件内容也需要先加载到内存中)。

  • 我们创建一个文件,但是该文件在磁盘中并不存在(按照以前对于文件系统的理解,打开文件如果不存在即在磁盘中创建,然后分配 inode 和数据块,修改位图结构等等),但是此刻我想要创建的这种文件,一样有 inode,有 file_operators 方法集,也有文件页缓冲区,但是就是不存在于磁盘中,能实现吗??

    能实现!这就是内存级别的文件,不与外设交互的文件,inode 里面的各种文件属性照常,只需要把方法集内原本指向外设读写的函数指针改为直接指向页缓冲区做读写,这样就跟外设没有任何关系了,也不需要往磁盘刷新数据之类的操作了。在操作系统内核中是存在很多诸如这样的内存级文件的。

2.1 初步理解

在这里插入图片描述

接着,当一个进程打开一个文件时,我在这个进程中创建出一个子进程,操作系统就需要以父进程为模板,为子进程创建一个 task_strcut、拷贝父进程的进程地址空间 和 页表等内核结构,包括父进程的 files_struct 结构体也同样要拷贝一份!说白了,这个 files_struct 代表的就是一个进程打开的文件的列表,也算是进程的一个数据结构,同时子进程也需要能够看到父进程的一切数据(在不发生写时拷贝的前提),父进程打开的所有文件也是一种数据,因此子进程需要看到父进程打开的各个文件,所以这个 files_struct 子进程同样会拷贝父进程的。

但是,父进程打开文件时,创建的各种文件对象 struct file,子进程则不拷贝这些文件对象。因为文件对象是文件管理模块的东西,进程是进程,文件是文件,不代表该文件是这个进程打开的,该文件就属于这个进程的了。文件与进程是操作系统两个模块来着的,文件不属于任何一个进程,往深一点说,文件是操作系统帮助进程打开的,因此操作系统才是文件的管理者,而非进程,文件与进程只是关联关系。所以既然该文件不属于进程,为什么子进程要拷贝呢?子进程想拷贝也无法拷贝,因为这不是父进程的东西。

所以既然子进程也拷贝了父进程的 files_struct,那么子进程的 files_struct 结构体也同样指向了父进程所打开的文件对象(三个标准输入输出流 以及 父进程单独打开的一个文件),而我们在概况进程通信时就说过,进程通信的本质前提就是要让不同的进程看到同一份 “资源”!那子进程的 files_struct 中的某一个文件描述符也指向父进程所打开的那个文件啊,这不就是父子进程看到同一份 “资源” 了吗?!

加上我们说的,创建一个文件但可以让它不存在于磁盘中,所以父子进程看到的这一同个文件,父进程往文件写入数据,子进程读取数据,这样不就可以使父子进程通信起来了吗?!

而我们上述说的这一切,就是管道的本质,管道就是一个内存级别的文件。

  • 现在父子进程的文件描述符表都指向了同一个文件,当一方正在对文件做读写操作时,另一方突然把文件关闭了,会影响到正在写入的进程吗,又或者影响对文件写入的这个操作吗??

    不影响,我们要知道,struct file 结构体里面是维护了一个引用计数 count 的,用于记录当前指向该文件的进程个数,所以当创建了子进程,多了一个进程指向该文件,那么 count + 1,当一方在关闭该文件的文件描述符,count - 1,但是只要 count ≠ \neq = 0,该文件的 struct file 对象以及文件的属性,内容等数据就不会被释放,因此不影响。

  • 哎哎哎,你上面说子进程是拷贝父进程的 file_struct 结构体,那我父进程要是打开文件时是以 r 权限打开的呢,那子进程拷贝下来的 file_struct,对该文件的权限不也是 r,大家都是 r,通信个屁啊。还怎么做到父进程写、子进程读,或者子进程写、父进程读这样的通信
    在这里插入图片描述

    但其实父进程在打开一个管道文件时,并不是只以 w / r 的方式打开的,而是同时打开以读和写的方式打开文件,之后父进程创建出来的子进程也同样的以读和写的方式指向该文件,再结合用户的实际需求,关闭一方的读或写端口,这样只有一方保持的一种状态,即可完成进程间的通信。

2.2 深入理解

在这里插入图片描述

因为要建立通信,所以父进程在打开一个文件时,不仅仅是以读或写的方式打开,父进程需要以两种方式打开文件,而对于该行为,操作系统会为同一个文件创建两个struct file,一个读方式,一个写方式,并且让进程中的文件描述符表分别指向读文件和写文件。而至于为什么操作系统要创建两个struct file,不直接将读写混为一个文件,可以理解为当读写都在一个文件进行时,父进程写完,子进程要读取时,文件内部的指针是指向父进程写入之后的位置,那么文件在做读取操作时,也是从文件指针往后开始读取,这样的话,子进程就读不出来父进程写的数据。当然,在技术实现上,这个问题并不能,可以将文件指针置为文件起始处,然后再进行读取,但这样就显得有些复杂麻烦了,有点简单问题复杂化的感觉,明明可以创建两个 struct file 搞定的事情,一定要频繁的移动文件指针。所以将读写文件分开,父进程写入时,由写文件的文件指针记录着位置,下次写入直接继续往后写入即可,子进程同理,读取到什么位置,都有一个文件指针维护着,这样既不互相干扰,何乐而不为呢?这也是操作系统的设计理念,可以用简单的方式解决问题,绝对不用复杂的方案。

不过虽然读写文件分开了,但因为这是属于同一个文件,文件属性,数据块都是相同的,因此这两个 struct file 都指向同一个文件缓冲区。接着,父进程创建子进程,子进程也以读写的方式各自指向这两个struct file 文件。

但是操作系统为了不让父子进程混淆各自的数据,即只让父子进程进行单向通信!单向通信即关闭父子进程其中一端文件操作,只让父子进程有读或写的一段。如果父子进程同时对文件的保留读写权限,那么父进程就既可以向文件写入,也可以读取,这样的话,父子进程就需要去确认哪些数据是自己写入的,哪些数据是从另一方进程读取过来的,这样又把简单问题复杂化了,因此操作系统在这件事上,只支持单向通信!如果确实有双向通信的需求,那么再重新建立一个管道文件,实现另一个方向的通信即可。

因为两个struct file 文件都指向的同一个页缓冲区,所以父子进程同时对这个缓冲区是可视状态的,而为了双方进程都互不干涉,互不影响,一般都会关闭该进程不使用的一端文件操作的权限,即如果父进程不使用读取端,指向struct file r 的文件描述符就会被关闭,子进程同理。

而上述这种基于文件级别的通信,就是管道通信的原理!但是,对于管道这个名字的由来,不要把因果关系搞反了,是因为这种通信是基于文件级别的 + 单向通信,因此才叫做管道,而不是它叫管道,所以它才是单向通信的。

  • 如果两个进程不具备父子关系,或者没有任何关系,能不能用管道文件的方式进行通信呢??

    如果两个进程不具备任何关系,那么答案是 不能! 如果两个进程具有血缘关系,那么是可以进行管道通信的。

    血缘关系就是父进程( A ) 的子进程( B ) 再创建了一个子进程( C ),那么这个 C 进程就是 A 进程的孙子进程,它是可以跟 A 和 B 进程进行通信,因为 C 进程中的 file_struct 是拷贝 B 进程的,B 进程中的 file_struct 是拷贝 A 进程的,因此它们都指向同一个 struct file,所以它们能进行通信;不仅如此,假如 A 再创建了一个子进程 B2,B2 也可以跟 A、B、C的任何一个进程通信,换言之,只要进程间具有血缘关系,那么就可以使用管道通信。

  • 匿名管道: 诸如上述这种内存级别的文件,它没有文件名,也没有路径,没有 inode 等文件属性信息,因为它不需要通过路径来定位该文件,它存在于内存中,它也不需要根据文件名 和 inode 来区分与其它文件的关系,因为这种文件只有父子进程这样具有血缘关系的进程才能够看到,诸如父子进程这样的进程也不用担心找不到该文件资源,这一份父子进程共同看到的 “资源”,是在进程创建时就被继承下来了,因此不需要任何文件信息,父子进程依旧能够看到这个 “资源”,而这种管道文件就称为 匿名管道

至此,两个进程还没有进行通信!你没听错,上述的一切,只是建立了通信信道,进程间并没有开始通信起来。这就是我们在概况中提到的,进程间具有独立性,因此进程间的通信是有成本的!


由于篇幅问题,关于管道是如何建立通信的,以及管道的应用场景,管道通信(下)

如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!

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

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

相关文章

使用 KuboardSpray 安装kubernetes_v1.23.1

ps:亲测有效,十分方便,记录下来 pps:下面文档来自官网 使用 KuboardSpray 安装kubernetes_v1.23.1 #Kuboard-Spray Kuboard-Spray 是一款可以在图形界面引导下完成 Kubernetes 高可用集群离线安装的工具,开源仓库的地址为 Kuboard-Spray (opens new window) 安…

git push出错Push cannot contain secrets

报错原因: 因为你的代码里面包含了github token明文信息,github担心你的token会泄漏,所以就不允许你推送这些内容。 解决办法: 需要先把代码里面的github token信息删除掉,并且删掉之前的历史提交,只要包…

【深海王国】初中生也能画的电路板?目录合集

Hi٩(๑ ^ o ^ ๑)۶, 各位深海王国的同志们,早上下午晚上凌晨好呀~辛勤工作的你今天也辛苦啦 (o゜▽゜)o☆ 今天大都督为大家带来系列文章《初中生也能画的电路板》,帮你一周内快速入门PCB设计,手把手教你从元器件库添加、电路原理图绘制、…

go 运行报错missing go.sum entry for module providing package

运行: #清理go.mod中不再需要的模块,并且会添加缺失的模块条目到go.sum中 go mod tidy

前端vue-实现富文本组件

1.使用wangeditor富文本编辑器 工具网站&#xff1a;https://www.wangeditor.com/v4/ 下载安装命令&#xff1a;npm i wangeditor --save 成品如下图&#xff1a; 组件实现代码 <template><div><!-- 富文本编辑器 --><div id"wangeditor">…

《热血江湖》v23巅峰对决游戏程序(真端+最新官方版本)

《热血江湖》v23巅峰对决游戏程序&#xff08;真端最新官方版本&#xff09; 下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【游戏】《热血江湖》v23巅峰对决游戏程序&#xff08;真端最新官方版本&#xff09; 链接: https://pan.baidu.com/s/18svlGuFnPM9ccwEAb7oBMw…

python-获取浏览器静态/动态素材

f12浏览器中 1&#xff1a;静态爬取 2.动态资源图片获取。斗鱼 3获取视频-抖音 一长串&#xff0c;最后一个http就是视频

相亲交友系统小程序:都市青年的新社交选择

在当今社会&#xff0c;都市青年面临着诸多挑战&#xff0c;其中之一就是如何在繁忙的工作和生活中找到理想的伴侣。相亲交友系统小程序应运而生&#xff0c;成为了都市青年的新社交选择。它不仅简化了传统相亲流程&#xff0c;还利用现代科技手段&#xff0c;如人工智能和大数…

【C++掌中宝】用最少的话让你全方位理解内联函数

文章目录 引言1. 什么是内联函数2. 工作原理3. 内联函数的编程风格4. 使用限制5. 内联函数与宏的比较6. 优缺点7. 何时使用内联函数8. 补充9. 总结结语 引言 在C编程中&#xff0c;函数的调用开销是程序运行效率的一个重要影响因素。为了解决频繁调用函数时的性能问题&#xf…

计算机视觉必备模型YOLO系列模型的知识点,提供YOLOv1-v8模型结构与代码实例

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉必备模型YOLO系列模型的知识点&#xff0c;提供YOLOv1-v8模型结构与代码实例。本文全面介绍了计算机视觉领域中必备的YOLO系列模型&#xff0c;详细梳理了YOLOv1至YOLOv8模型的结构及其演变过程。文章内容…

linux设置常见开机自启动命令

本文介绍了三种开机自启的方式&#xff0c;重点介绍使用systemctl的方式自启动的 方式一、修改 /etc/rc.d/rc.local 文件 /etc/rc.d/rc.local 文件会在 Linux 系统各项服务都启动完毕之后再被运行。所以你想要自己的脚本在开机后被运行的话&#xff0c;可以将自己脚本路径加到…

SQL - 进阶语法(二)约束

1. SQL约束 约束用于约束表中的数据规则&#xff0c;如若存在违反行为&#xff0c;行为会被约束终止。 • NOT NULL 确保列不能有NULL值 如果添加一行新的数据&#xff0c;不能有null值&#xff0c;否则无法添加 新建表格 CREATE TABLE new_table( ID int NOT NULL, NAME …

C语言中易混淆概念的关键字

最快的关键字---- register register&#xff1a; 这个关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内 存寻址访问以提高效率。注意是尽可能&#xff0c;不是绝对。你想想&#xff0c;一个 CPU 的寄存器也就那么 几个或几十个&#xff0c;你要是定义了很多很…

【环境踩坑系列】centos7安装python3.10.X

前言 虽然centOS8已经发布了相当一段时间了&#xff0c;但是基于稳定性、成熟的社区等原因&#xff0c;大家在选择centOS作为服务器操作系统的时候仍然会选择centOS7作为首选。但是centOS7自带的是python2.7.5&#xff0c;当前大量的python程序要用到的又是python3&#xff0c…

day-60 字符串中最多数目的子序列

思路 由题目可以得出&#xff0c;当字符串开头插入pattern[0]或在字符串结尾插入pattern[1]这两种情况中的一种所得到的子序列数目一定是最多的 解题过程 我们可以遍历字符串&#xff0c;统计pattern[0]的个数&#xff0c;每当遇到一个pattern[1]时&#xff0c;序列数就会加上…

蓝桥杯15届C/C++B组省赛题目

问题描述 小蓝组织了一场算法交流会议&#xff0c;总共有 5050 人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 77 个人&#xff0c;这 77 人彼此之间没有进行握手 (但这 77 人与…

ProgrammerAI—AI辅助编程学习指南

前言 随着AIGC&#xff08;AI生成内容&#xff09;技术的快速发展&#xff0c;诸如ChatGPT、MidJourney和Claude等大语言模型相继涌现&#xff0c;AI辅助编程工具正逐步改变程序员的工作方式。这些工具不仅可以加速代码编写、调试和优化过程&#xff0c;还能帮助解决复杂的编程…

通义千问重磅开源Qwen2.5,性能超越Llama

简介&#xff1a; 击败Meta&#xff0c;阿里Qwen2.5再登全球开源大模型王座 9月19日云栖大会&#xff0c;阿里云CTO周靖人发布通义千问新一代开源模型Qwen2.5&#xff0c;旗舰模型Qwen2.5-72B性能超越Llama405B&#xff0c; 再登全球开源大模型王座。Qwen2.5全系列涵盖多个尺…

Tesla T4 P2P测试

Tesla T4 P2P测试 一.测试环境二.测试步骤1.获取设备信息2.查看PCIE拓扑结构3.选择9B、9E这二张4.查看逻辑设备ID5.设置环境变量(需要用逻辑设备ID,通过UUID跟smi看到的物理ID关联)6.不同地址的原子操作2.P2P与非P2P的性能差异3.GPU带宽测试 Tesla T4 P2P测试 通过物理ID找到逻…

多个ECU测试方案-IP地址相同-DoIP刷新-环境测试耐久测试

情况1&#xff1a;只有一个ECU进行测试 - 接口模块只需要使用一个车载以太网转换器&#xff1b; 情况2&#xff1a;多ECU同时测试&#xff0c;但ECU IP地址不一样&#xff0c;上位机多个网口 - 上位机测试软件&#xff0c;需要通过PC的不同网卡&#xff0c;访问各个ECU&#…