【Linux】进程间通信之匿名管道

news2024/11/25 10:42:18

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 一、进程间通信介绍
      • 1.1 是什么
      • 1.2 为什么(目的)
      • 1.3 怎么做到的(浅聊)
      • 1.4 进程间通信的实现方式
  • 二、什么是管道
  • 三、匿名管道
      • 3.1 匿名管道的工作原理
      • 3.2 站在文件描述符角度-深度理解管道
      • 3.3 pipe系统调用接口
      • 3.4 匿名管道的特征
      • 3.5 匿名管道的四种情况
  • 四、简单模拟进程池(匿名管道的应用场景)
  • 五、相关代码

一、进程间通信介绍

1.1 是什么

进程间通信IPCInter-Process Communication)就是两个或多个进程实现数据层面的交互

1.2 为什么(目的)

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

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

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

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

1.3 怎么做到的(浅聊)

  • 进程间通信的本质:让不同的进程能共享同一份“资源”。这个“资源”可以理解为特定形式的内存空间。当一个进程将数据写入共享内存后,另一个进程就可以从同一份内存空间中读取这些数据,从而实现了进程之间的数据传递和共享。

  • 这个资源(内存)一般由操作系统提供。那为什么不是两个进程中的其中一个提供这个“资源”呢?可以假想一下,假设这个资源真的由其中一个进程提供,而进程是具有独立性的,各自拥有自己的虚拟地址空间和资源,那么它就会暴露自己的内部状态和数据给其他进程,如果提供资源的进程出现故障或意外终止,其他进程可能会受到影响,因为它们依赖于这个进程提供的资源。

  • 相比之下,如果资源由操作系统提供,而进程相当于用户,如果想使用资源,那么操作系统必定会提供统一接口

  • 而我们知道,因为系统中不止一对进程在进行通信,可能会存在多个,那么操作系统就要提供多份“资源”,因此操作系统就必须对“资源”进行管理,这又得搬出管理的六字真言:先描述,再组织

1.4 进程间通信的实现方式

请添加图片描述

二、什么是管道

Linux中,管道可以被视为一种特殊类型的文件,它是基于文件级别的通信方式。它使得一个进程的输出可以直接成为另一个进程的输入,从而实现了进程之间的数据传输和协作。在Linux中,你可以使用管道符号 |将一个进程的输出发送到另一个进程的输入。

  • 比方说你想要统计一个文件中包含的单词数量。

请添加图片描述

其中,当cat命令和wc命令运行起来后就是两个进程,cat进程通过标准输出将数据传输到管道当中,wc进程再通过标准输入从管道当中读取数据,至此便完成了两个进程间通信。

请添加图片描述

Linux中,有两种类型的管道,一种是匿名管道(Anonymous Pipe),另一种是命名管道(Named Pipe)。因此,这篇博客重点是要解释匿名的工作原理等方面。(命名管道在下一篇博客讲解)

三、匿名管道

3.1 匿名管道的工作原理

  • Linux中,管道可以被视为一种特殊类型的文件,它是基于文件级别的通信方式。为什么这样说呢?管道的使用方式类似于文件的读写,它是通过文件描述符进行访问,管道的读取端和写入端分别对应着文件的读取和写入。在管道中写入的数据会被暂存在内核的缓冲区中,然后读取进程从缓冲区中读取,因为它在文件系统中没有具体的文件路径或名字,因此被称为匿名管道。它其实是操作系统在内存中创建的一段缓冲区。(后面会介绍)

  • Linux中,使用管道符号 | 本质上就是创建了一个匿名管道,它常用于父子进程(或者有血缘关系)之间的通信。

不知道大家有没有好奇:进程通信时每次都要向文件中读取数据,而文件是存储在磁盘上的,这不会导致效率非常低吗?如果效率非常低的话,Linux为什么还要开发出管道符号|呢?因此,我们必须要知道匿名管道的工作原理。

进程间通信的本质:让不同的进程看到同一份资源。因此,使用匿名管道实现父子进程间通信的原理就是:让两个父子进程先看到同一份被打开的文件资源,然后父子进程就可以对该文件进行写入或是读取操作,进而实现父子进程间通信。

在这里插入图片描述

学习到现在,我们都清楚(复习):

  • 当一个程序运行起来(加载到内存),就是一个进程,操作系统会管理该进程并且其创建task_struct结构体对象。

  • 当进程打开文件时,操作系统会在内核中创建数据结构来描述这个已打开的文件对象struct file。它包含了inode(文件的所有属性)、指向文件操作函数表的指针(文件操作方法)、内核缓冲区等等。

  • 而进程可以打开多个文件。所以,操作系统还需要对打开的文件进行管理,所以进程task_struct对象里有一个指针struct files_struct* files,这个指针指向一个结构体files_struct,而这个结构体包含一个指针数组struct file* fd_array[],这个数组我们可以称之为文件描述符表。数组中的每个元素都是指向当前进程所打开文件的指针(地址)!默认情况下,当一个进程启动时,操作系统会打开三个标准流(文件):stdin(键盘文件)、stdout(显示器文件)、stderr(显示器文件)。

回归正题:

此时fork创建子进程,操作系统会以父进程为模板,为子进程创建新的进程控制块task_structfiles_struct等。但并不会复制父进程打开的文件对象 struct file,因为文件操作通常是在用户空间的,与进程无关;而子进程会拷贝父进程的文件描述符表 files_struct,这是因为文件描述符表是进程的一部分,它记录了进程打开的文件描述符和相应的文件对象。因此,子进程的 struct file* fd_array[] 中的元素会指向父进程打开的文件对象,即父子进程(不同的进程)可以看到同一份资源(打开的文件),这不就是通信的本质吗?!

又因为文件对象struct file是由操作系统管理的,如果操作系统识别到是普通文件,就通过内核缓冲区刷新策略往磁盘上读写;而如果识别到时管道文件,操作系统不会将数据刷新到磁盘,而是直接在内核缓冲区中进行数据传输。因此,这种操作方式使得管道的数据传输更加高效。

注意:匿名管道在相关进程的生命周期内有效,但是并不是一旦相关进程结束就会自动关闭并释放资源。而是会依靠文件对象的引用计数机制来管理资源的释放。只有当所有相关进程都关闭了对管道的引用时,文件对象的引用计数变为零,才会触发操作系统对管道资源的释放。

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

如果父进程以只读方式打开管道,并且在创建子进程时,子进程也会以只读方式打开管道。那么进程间该如何通信呢?

显然以上这种方式是无法进行通信的。如果要实现通信,一种常见的方法(步骤)是:

  1. 父进程在打开管道的时候,以读和写的方式打开

请添加图片描述

  1. 接下来父进程fork创建子进程,子进程会拷贝父进程的文件描述符表,所以子进程也会以读写的方式打开管道文件

请添加图片描述

  1. 接下来根据实际需求,可以通过关闭相应的管道端口来实现单向通信。比方说,你想让父进程向管道写入数据,而子进程从管道读取数据。那么你就需要关闭父进程的读取端,关闭子进程的写入端。

请添加图片描述

接下来可能有人会想:为什么管道两端不设计成既可读,也可写呢?

  1. 竞态条件:如果管道的两端都可以读写,那么可能会出现竞态条件。两个进程同时尝试在管道中读取和写入数据,可能导致数据的不一致性和混乱。

  2. 死锁:读写管道的两端同时进行读写操作时,可能会导致死锁。例如,一个进程在等待从管道中读取数据,而另一个进程在等待向管道中写入数据,这种情况下可能会导致两个进程相互等待,最终造成死锁。

  3. 数据丢失:如果管道的两端同时读写,可能会导致数据丢失。例如,一个进程正在向管道中写入数据,同时另一个进程正在从管道中读取数据,这种情况下可能会导致部分数据被丢失。

  4. 混乱的通信:同时读写管道可能会导致通信的混乱和不可预测性。由于数据的读写是同时进行的,可能会导致数据的顺序混乱或部分数据丢失,使通信变得不可靠。

因此,为了避免这些问题,一般建议使用管道时一端只负责写入数据,另一端只负责读取数据。这样可以确保通信的可靠性和一致性。

3.3 pipe系统调用接口

Linux中,除了使用管道符号|创建和使用匿名管道之外,也可以在程序中使用 pipe 系统调用进行创建和使用。

【函数原型】

#include <unistd.h>
int pipe(pipefd);

其中:

  • pipefd:做输出型参数,传一个整型数组,用于存放管道文件的两个文件描述符。pipefd[0] 是管道的读取端,pipefd[1] 是管道的写入端。

  • 返回值

    • 成功时,返回值为0,并且在 pipefd 数组中存放管道的两个文件描述符。

    • 失败时,返回值为-1,错误码保存在 errno 中。

在创建匿名管道实现父子进程间通信的过程中,需要pipe函数和fork函数搭配使用,我们设置父进程只能向管道读取数据,而子进程从管道写入数据,代码如下(配代码注释)

在这里插入图片描述

【程序结果】

在这里插入图片描述

3.4 匿名管道的特征

  1. 匿名管道常用于具有血缘关系的进程。

  2. 管道只能单向通信。

  3. 父子进程是会协同的,同步与互斥的,是为了保护管道文件的数据安全(信号量及多线程再谈)。意思就是说:在管道通信过程中,如果父进程负责从管道读取数据,而子进程负责向管道写入数据,要确保进程在通信时以正确的顺序发送和接收消息,避免出现数据竞争和不确定行为。

  4. 管道的生命周期随进程。因为管道在操作系统中被看作是一种文件,但是它其实是是通过内存缓冲区来传输数据。当所有打开管道的进程(读取端和写入端)都关闭了管道,管道所占用的资源就会被操作系统释放。

  5. 管道是面向字节流的。(网络再谈)

3.5 匿名管道的四种情况

  1. 读写端正常,管道如果为空,读端就会被阻塞,等待写端的数据。

比方说,写端只向管道写三次数据,那么对应的读端就会接收三次数据,后续读端就会被阻塞,等待写端数据

在这里插入图片描述

【程序结果】

在这里插入图片描述

  1. 读写端正常,管道如果被写满,写端就要被阻塞。这说明管道是有限大小的缓冲区,此大小由操作系统决定。

比方说:子进程不断写入,父进程不读取(死循环啥也不干)

在这里插入图片描述

【程序结果】

在这里插入图片描述

  1. 读端正常读,写端关闭,那么读取端会在读取完管道中的所有数据后得到一个特殊的信号,表明已经到达了管道的末尾。这样读取操作就会返回值为 0,也就是read函数会返回0,而不会再被阻塞(不会再等待管道中会有新的数据)。因此,当写端关闭后,读端就知道不会再有新的数据写入管道,可以安全地关闭管道,结束通信。

在这里插入图片描述

【程序结果】

在这里插入图片描述

因此,当写端关闭后,读端就知道不会再有新的数据写入管道,可以安全地关闭管道,结束通信。

在这里插入图片描述

  1. 写端正常,读端关闭。那么这两个进程之间通信就没有意义了,因此,操作系统就要杀掉正在写入的进程。那如何杀掉呢?— 当操作系统希望终止一个正在运行的进程时,通常会发送一个特定的信号给该进程来杀掉。
    请添加图片描述
    (该图片来自于往期博客)

那么写端进程退出时究竟是收到了什么信号,我们可以来验证一下

在这里插入图片描述

【程序结果】

在这里插入图片描述

运行结果显示,子进程(写端)退出时收到的是13号信号

通过kill -l命令可以查看13对应的具体信号。

在这里插入图片描述

由此可知,当发生情况四时,操作系统向子进程发送的是SIGPIPE信号将写端进程终止的。

四、简单模拟进程池(匿名管道的应用场景)

在另一篇博客中,博主正在加工中~

五、相关代码

本篇博客的相关代码:点击跳转

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

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

相关文章

易飞销货单出货时审核库存检查

公司接到一客户因品种多而数量少&#xff0c;单一出货计划行比较多&#xff0c;而只上了生产ERP易飞&#xff0c;审核时经常会出现倒催货&#xff0c;提前做销售单&#xff0c;行数有时超30行以上&#xff0c;审核跳窗报错时也不方便查找&#xff0c;特写一外挂程序&#xff0c…

代码随想录算法训练营第36期DAY49

DAY49 139单词拆分 没有思路。 回溯法 回溯怎么做呢&#xff1a;拼接str&#xff0c;看能不能拼出来。注意每个单词能用多次&#xff0c;不是用了就没。 但是语法还是难写。 自己的思路不好&#xff0c;题解思路&#xff1a;枚举所有分割字符串&#xff0c;判断是否在字典…

红队神器Evil-winrm的使用

前言 Evil-winrm 工具最初是由 Hackplayers 团队开发的。开发该工具的目的是尽可能简化渗透测试&#xff0c;尤其是在 Microsoft Windows 环境中。 Evil-winrm 使用 PowerShell 远程协议 (PSRP)&#xff0c;且系统和网络管理员经常使用Windows Remote Management 协议进行上传和…

哈尔滨三级等保测评需要测哪些设备?

哈尔滨三级等保测评需要测的设备&#xff0c;主要包括物理安全设备、网络安全设备和应用安全设备三大类别。这些设备在保障哈尔滨地区信息系统安全方面发挥着至关重要的作用。 首先&#xff0c;物理安全设备是确保信息系统实体安全的基础。在哈尔滨三级等保测评中&#xff0c;物…

Deepin安装PostGresql

最近要把开发环境完全从Windows移到Deepin上&#xff0c;本次介绍在Deepin借助apt-get安装和配置数据库。同时可以用Dbever提供图形化管理工具。 安装PostGreSQL数据库和创建数据库 #安装postgresql zhanglianzhuzhanglianzhu-PC:/$ sudo apt-get install postgresql-16 正在…

PyCharm中 Fitten Code插件的使用说明一

一. 简介 Fitten Code插件是是一款由非十大模型驱动的 AI 编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试 Bug&#xff0c;节省您的时间&#xff0c;另外还可以对话聊天&#xff0c;解决您编程碰到的问题。 前一篇文章学习了 PyCharm…

mysql数据库密码破解

一、Mysql数据库密码破解 ①一旦获取了网站一定的权限后&#xff0c;如果能够获取MSQL中保存用户数据&#xff0c;通过解密后&#xff0c;即可通过正常途径来访问数据库;一方面可以直接操作数据库中的数据&#xff0c;另一方面可以用来提升权限。 ②MySQL数据库用户密码跟其它…

安泰高压放大器应用领域分享:介电电泳(DEP)技术的具体应用

介电电泳&#xff08;Dielectrophoresis—DEP&#xff09;技术描述的是位于非匀称电场的中性微粒由于介电极化的作用而产生的平移运动。产生在微粒上的偶极矩可以有两个相同带电量但极性相反的电荷来表示&#xff0c;当它们在微粒界面上不对称分布时&#xff0c;产生一个宏观的…

小型柴油发电机不发电的原因

小型柴油发电机不发电的原因 小型柴油发电机不发电的原因可能有多种&#xff0c;以下是一些常见的原因&#xff1a; 发动机问题&#xff1a; 发动机油路不通畅&#xff0c;可能导致燃油无法顺利到达燃烧室。 气缸压缩不正常&#xff0c;影响发动机的正常工作。 润滑油粘度过大…

【c语言】自定义类型----结构体

结构体是c语言的一种自定义类型&#xff0c;自定义类型对于开发者及其重要的类型&#xff0c;它可以随意由开发者进行谱写功能&#xff0c;而今天的结构体可以用来表示一种变量的单个或多种具体属性&#xff0c;再编写代码时有着不可替代的作用&#xff01;&#xff01;&#x…

Android 代码打印meminfo

旨在替代adb shell dumpsys meminfo packageName&#xff0c;在log打印meminfo&#xff0c;以便分析内存情况 ActivityManager.MemoryInfo memoryInfo new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); long totalMemory Runtime.getRuntime(…

绘唐官网绘唐科技

绘唐AI工具是一种基于人工智能技术的绘画辅助工具。 使用教程&#xff1a;https://iimenvrieak.feishu.cn/docx/CWwldSUU2okj0wxmnA0cHOdjnF 它可以根据用户提供的输入或指令生成各种类型的图像。 绘唐AI工具可以理解用户的绘画需求&#xff0c;并根据用户的要求生成具有艺术…

Python爬取与可视化-豆瓣电影数据

引言 在数据科学的学习过程中&#xff0c;数据获取与数据可视化是两项重要的技能。本文将展示如何通过Python爬取豆瓣电影Top250的电影数据&#xff0c;并将这些数据存储到数据库中&#xff0c;随后进行数据分析和可视化展示。这个项目涵盖了从数据抓取、存储到数据可视化的整个…

【python解决】查询报%d format: a number is required, not str问题

【Python解决】查询报%d format: a number is required, not str问题 在Python中&#xff0c;字符串格式化是一种常见的操作&#xff0c;用于创建包含变量的字符串。如果你在使用%操作符进行格式化时遇到了%d format: a number is required, not str的错误&#xff0c;这意味着…

当前主流的App开发技术综述

一、引言 随着移动互联网的蓬勃发展&#xff0c;App&#xff08;应用程序&#xff09;已经成为人们日常生活中不可或缺的一部分。无论是社交、购物、娱乐还是工作学习&#xff0c;App都以其便捷、高效和个性化的特点深受用户喜爱。而在这一过程中&#xff0c;App开发技术也在不…

3D39无人机摇杆电位器食用指南

这个摇杆精度会非常的不错&#xff0c;虚位只在后面有一点&#xff0c;当然价格也比较贵。最便宜某宝上也得 &#xffe5;15 一个。 使用这个摇杆和使用2块钱一个的 PS 写的代码都一样&#xff0c;只是注意下接线上的一些问题就行。 需要注意两个ADC引脚最好不要挨着&#xf…

加热炉钢坯温度计算传热学应用

非常感谢“计算传热学大叔”&#xff0c;大家了解更多&#xff0c;请移步前期文章&#xff1a;https://blog.csdn.net/weixin_37928884/article/details/127709215 第一类边界条件 clc clear close all %直接在此修改参数 length 0.135; %长度 Tb 930; %初始…

记录一次springboot、ruoyi若依前后端不分离项目和vue项目的合并整合问题,搞了一天总结

项目场景&#xff1a; 因为此功能只做为客户方一个小模块&#xff0c;客户方使用的是springboot前后端不分离的架构。而我们的项目是使用前后端分离springbootvue的架构。在接项目前&#xff0c;项目已经存在&#xff0c;所以不存在设计架构的前提。实际是在原有基础上修改的。…

计算机二级Access操作题总结——简单应用

查询设计 创建一个查询&#xff0c;能够在客人每次结账时根据客人的姓名提示统计这个客人已住天数和应交金额&#xff0c;并显示“姓名”、“房间号”、“已住天数”和“应交金额”&#xff0c;所建查询命名为“qT2”。 注&#xff1a;输入姓名时应提示“请输入姓名”。已住天…

C++开源项目:pathcopycopyV20源码及运行程序

PathCopyCopy 是一个开源的 Windows 资源管理器扩展项目&#xff0c;旨在为用户提供一个更加高效、便捷的文件路径复制和管理工具。以下是关于 PathCopyCopy 开源项目的详细介绍&#xff1a; 1. 项目概述 2. 项目技术分析 3. 项目功能 4. 项目特点 5. 项目应用场景 6. 项目…