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

目录

  • 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.mfbz.cn/a/883108.html

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

相关文章

前端接口415状态码【解决】

前端接口415状态码【解决】 一、概述 415状态码是HTTP协议中的一个标准响应状态码,代表“Unsupported Media Type”(不支持的媒体类型)。当客户端尝试上传或发送一个服务器无法处理的媒体类型时,服务器会返回这个状态码。这通常意…

深度学习:常见损失函数简介--名称、作用和用法

目录 1. L1 Loss 2. NLL Loss (Negative Log Likelihood Loss) 3. NLLLoss2d 4. Gaussian NLL Loss 5. MSE Loss (Mean Squared Error Loss) 6. BCE Loss (Binary Cross-Entropy Loss) 7. Smooth L1 Loss 8. Cross Entropy Loss 1. L1 Loss 作用:计算预测值…

Arm Cortex-R52+ Generic Timer分析

目录 1.Generic Timer初识 2.R52的Generic Timer 3.如何配置Timer中断 4.小结 1.Generic Timer初识 Arm Cortex-R52内部实现了Generic Timer(通用计时器),它可以基于递增计数来产生中断和事件流。 事实上,该计时器和Armv8-R AArch32中的定义完全一…

纯生信分析如何冲Microbiome

瘤胃微生物组对于反刍动物的消化过程至关重要,它能够将难以消化的饲料转化为高质量的蛋白质,但这一过程会产生甲烷,加速了气候暖化进程,还造成了饲料中营养和能量的损失。以往的研究主要集中在瘤胃细菌与反刍动物生产特性之间的关…

PHP探索校园新生态校园帮小程序系统小程序源码

探索校园新生态 —— 校园帮小程序系统,让生活更精彩! 🌱【开篇:走进未来校园,遇见新生态】🌱 你是否厌倦了传统校园的繁琐与单调?是否渴望在校园里也能享受到便捷、智能的生活体验&#xff1…

python爬虫:从12306网站获取火车站信息

代码逻辑 初始化 (init 方法): 设置请求头信息。设置车站版本号。 同步车站信息 (synchronization 方法): 发送GET请求获取车站信息。返回服务器响应的文本。 提取信息 (extract 方法): 从服务器响应中提取车站信息字符串。去掉字符串末尾的…

spring boot 项目中redis的使用,key=value值 如何用命令行来查询并设置值。

1、有一个老项目,用到了网易云信,然后这里面有一个AppKey,然后调用的时候要在header中加入这些标识,进行与服务器进行交互。 2、开发将其存在了redis中,一开始的时候,我们测试用的老的key,然后提…

深入解析:HTTP 和 HTTPS 的区别

网络安全问题正变得日益重要,而 HTTP 与 HTTPS 对用户数据的保护十分关键。本文将深入探讨这两种协议的特点、工作原理,以及保证数据安全的 HTTPS 为何变得至关重要。 认识 HTTP 与 HTTPS HTTP 的工作原理 HTTP,全称超文本传输协议&#xf…

Spring Boot 点餐系统:您的移动餐饮伙伴

第二章关键技术的研究 2.1相关技术 网上点餐系统是在Java MySQL开发环境的基础上开发的。Java是一种服务器端脚本语言,易于学习,实用且面向用户。全球超过35%的Java驱动的互联网站点使用Java。MySQL是一个数据库管理系统,因为它的…

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时,记得表明转载哦 步骤 在需要复制的csdn 文章页面,打开浏览器开发者工具(F12)Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…

升压站成套设备厂家

那么,本文呢,就是将围绕着升压站设备厂家这个关键词,来为您、为潜在的光伏升压站成套设备的采购们分享 一些干货,以及说说升压站设备生成厂家的情况。 我知道,很多人关注的所谓的升压站设备或许比较的多。比如包括了逆…

指挥平台在应急场所中的主要表现有哪些

在应对自然灾害、公共安全事件等突发危机时,指挥平台作为应急管理体系的核心枢纽,其重要性不言而喻。它不仅承载着信息的快速汇聚、精准分析与高效调度功能,更在应急场所中有一定的关键表现。接下来就跟着北京嘉德立一起了解一下。 一、信息集…

什么是电商云手机?可以用来干什么?

随着电商行业的迅速发展,云手机作为一种创新工具正逐渐进入出海电商领域。专为外贸市场量身定制的出海电商云手机,已经成为许多外贸企业和出海电商卖家的必备。本文将详细介绍电商云手机是什么以及可以用来做什么。 与国内云手机偏向于游戏场景不同&…

GPT-4o在matlab编程中性能较好,与智谱清言相比

边标签由矩阵给出 s [1 2 3 3 3 3 4 5 6 7 8 9 9 9 10]; t [7 6 1 5 6 8 2 4 4 3 7 1 6 8 2]; G graph(s,t); plot(G) ------------------- GPT-4o给出的代码可用, clc;clear; % 定义边的起点和终点 s [1 2 3 3 3 3 4 5 6 7 8 9 9 9 10]; t [7 6 1 5 6 8 2 …

中国蚁剑(antSword)安装使用

antSword下载 antSword-Loader下载 作者:程序那点事儿 日期:2024/09/12 19:35 中国蚁剑(AntSword)是一款跨平台的开源网站管理工具,旨在满足渗透测试人员的需求。它是一个功能强大的工具,可以帮助用户管理…

一,初始 MyBatis-Plus

一,初始 MyBatis-Plus 文章目录 一,初始 MyBatis-Plus1. MyBatis-Plus 的概述2. 入门配置第一个 MyBatis-Plus 案例3. 补充说明:3.1 通用 Mapper 接口介绍3.1.1 Mapper 接口的 “增删改查”3.1.1.1 查询所有记录3.1.1.2 插入一条数据3.1.1.3 …

LeetCode[中等] 155. 最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…

【html网页制作】旅游风景主题网页制作含css动画及js特效(8页面附效果源码)

HTMLCSS旅游风景主题旅游网页制作 🍔涉及知识🥤写在前面🍧一、网页主题🌳二、网页效果菜单切换效果PageA、整体页Page1、首页Page2、旅行趣事页Page3、旅行美景页Page4、旅行指南页Page5、旅行视频页Page6、留言页Page7、西湖简介…

python-比较月亮大小/数组下标/人见人爱a+b

一:比较月亮大小 题目描述 小理是一名出色的狼人。众所周知,狼人只有在满月之夜才会变成狼。 同时,月亮的大小随着时间变化,它的大小变化 3030 天为一循环。 它的变化情况(从第一天开始)为 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,13,12,1…

深度学习之概率论预备知识点(3)

在深度学习中,概率论和数理统计是理解许多算法背后的理论基础。这些知识在处理不确定性、估计模型参数、理解数据分布等方面非常关键 1、概率 一种用来描述随机事件发生的可能性的数字度量,表示某一事件发生的可能性。 概率并不客观存在,是…