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

news2025/1/16 22:01:01

在这里插入图片描述

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


目录

  • 一、见见猪跑
  • 二、命名管道的工作原理
  • 三、系统调用接口mkfifo
  • 四、命名管道和匿名管道的区别(特征)
  • 五、 命名管道的四种情况
  • 六、代码

一、见见猪跑

上篇我们学习到的匿名管道主要用于血缘关系的进程之间进行通信,而命名管道则可以在毫不相关的进程之间进行通信。命名管道是基于文件系统的,因此在使用命名管道进行通信时,需要遵循一定的权限规则和文件路径约定(原理部分会说)。

在命令行上使用mkfifo即可创建命名管道

mkfifo <filename>

mkfifo命令创建的命名管道在文件系统中显示为一种特殊的文件类型,通常以绿色文本显示,以便与普通文件区分开来。

请添加图片描述

创建命名管道后,进程可以像操作普通文件一样对其进行读取和写入操作。一个进程可以将数据写入管道,另一个进程可以从管道中读取数据。

在这里插入图片描述

如果可以看到,左端的echo是一个进程,右端的cat也是一个进程,它们两是毫无相关的,而通过命名管道,可以让这两个毫无相关的进程通信。

但需要注意的是,如果没有任何进程打开了管道进行读取操作,那么写入操作将永远被阻塞,直到有进程打开管道进行读取操作为止。这种行为确保了在进程之间进行通信时的同步性,即写入数据的进程会等待读取数据的进程准备好后再继续执行,从而避免了数据丢失或混乱。

二、命名管道的工作原理

请添加图片描述

我们知道:当进程打开文件时,操作系统会在内核中创建struct file结构体对象来描述这个被打开的文件。而一个进程可以打开多个文件,那进程结构体对象task_struct就要存储哪些文件是由哪一个进程打开的。因此,每个进程的task_struct对象都要和打开的文件建立关系!所以每个task_struct对象其实有一个指针struct files_struct* files,这个指针指向结构体files_struct,而这个结构体包含一个指针数组struct file* fd_array[],这个数组我们可以称之为文件描述符表。数组中的每个元素都是指向当前进程所打开文件的指针(地址)!

而如果两个不同的进程打开同一个文件,操作系统只会为这个打开的文件创建一个struct file结构体对象,只不过这个文件的结构体对象有一个引用计数(计数器)字段会是2,表示当前有两个进程打开此文件。

这意味着多个进程可以共享相同的文件资源(各自进程的文件描述表指向同一个文件结构体对象),这不就是进程间通信的本质:让不同的进程能共享同一份资源。一个进程可以向文件写入数据,而另一个进程则可以从文件中读取这些数据,从而实现了通信!

  • 而在上面我们看到当一个进程向管道文件写入数据时,另一个进程在查看该管道文件属性时,其文件大小却是0,但还是可以从该管道文件中获取数据现象。这是为什么?

其原因是:不管是匿名管道还是命名管道,写入数据的时候并不是将数据直接刷新到磁盘(访问磁盘有I/O效率问题)!而是存储在内核缓冲区中!因为操作系统管理着内核中每一个被打开的文件,那么操作系统就很清楚每一个被打开文件的类型。因此,如果操作系统识别是普通文件,那么就根据内核缓冲区的刷新策略,直接刷新到磁盘上;而如果识别到是管道文件,那么数据不会马上刷新到磁盘上,而是存储在内核缓冲区中,等待另外一个进程读取数据。因此命名管道(或匿名管道)通常被视为"内存级文件"。

  • 接下来还有一个边角问题:在匿名管道中,我们可以通过子进程通过继承父进程的文件描述符表,使得不同的进程可以打开同一个管道文件;那在命名管道中,我们怎么保证两个不同的进程打开的是同一个管道文件呢?

这就设计到【文件系统】的知识了,因为磁盘中一定会存在大量没有被打开的文件,那么就需要管理磁盘中每一个文件的属性,注定了存在大量的inode结构(文件属性的集合)。因此为了区分每个文件的inode结构,操作系统会为每个inode结构取一个唯一编号,即inode编号。所以,想要对文件操作,操作系统只认inode编号!但用户都是通过文件名来对文件操作的呀?这又是因为文件名和inode编号的关系是由目录来建立和维护的(目录文件的数据块存储着文件名和inode编号的映射关系)

因此,当不同进程通过需要通过同一个命名管道文件进行通信,必然要找到命名管道的文件路径,那么操作系统会根据文件名到指定的目录下的数据块中查找文件名映射的inode编号。因为inode编号是唯一的,从而可以确保这些进程打开的是同一个管道文件。

三、系统调用接口mkfifo

除了可以用makefifo命令来创建命名管道之外,还可以使用系统调用接口mkfifo函数来创建。

#include <sys/types.h>
#include <sys/stat.h>
    
int mkfifo(const char *pathname, mode_t mode);

其中

  • pathname指定了要创建的命名管道的路径(包含文件名)

  • mode指定了创建出来管道文件的权限(类似于系统调用接口open函数的第三个参数)。

  • 返回值:

    • 成功创建命名管道时,mkfifo函数返回0

    • 失败则返回-1

代码样例思路:首先为了能让两个毫不相干的进程能够通信,创建两个源文件,分别是服务端server.cc和客户端client.cc。服务端主要是创建命名管道、读取管道数据和最后回收命名管道,而客户端主要是向管道中写入数据。

补充:.cc结尾的就是C++源文件的后缀名。常见后缀名有很多种,如.cpp.cc 或者 .cxx等,这取决于个人或团队的偏好。

  • comm.hpp:这个头文件主要是用来存放整个项目所需要的头文件以及自定义的错误码。

在这里插入图片描述

  • server.cc:创建命名管道、读取管道数据和最后回收命名管道。(详细内容可以看代码注释,非常详细!!!)

在这里插入图片描述

  • client.cc:向管道中写入数据。(详细内容可以看代码注释,非常详细!!!)

在这里插入图片描述

  • 程序演示

在这里插入图片描述

需要注意的是:

  • 必须要先运行sever端进程,因为要先将管道文件创建好后,才能开始通信。
  • 服务端启动后,因为是读端,所以会阻塞等待客户端(写端)写入数据,所以你会看到运行完服务端后,会有光标一直在闪烁。
  • 命名管道文件需要写端和读端都打开的时候才可以开始通信,否则只打开其中一个端会进入阻塞。我们可以理解为这是为了防止只打开读端而不打开写端,这是因为不打开写端,读端read函数就会返回0,那么读端也会退出

四、命名管道和匿名管道的区别(特征)

在这里插入图片描述

命名管道的特征大致和匿名管道一样:

  1. 匿名管道和命名管道都是通过文件描述符进行通信的
  2. 无论是匿名管道还是命名管道,都是单向通信的,即数据只能从一端流向另一端。
  3. 面向字节流
  4. 进程是会协同的,同步与互斥的

不同点在于:

  1. 匿名管道通常用于有亲缘关系的进程之间的通信,比如父子进程;而命名管道可以用于没有亲缘关系的进程之间的通信,它是一种独立的通信方式。
  2. 匿名管道直接通过pipe函数创建使用,然后创建子进程进行通信;而命名管道需要先通过mkfifo函数创建,然后再通过open打开使用。
  3. 在匿名管道中,如果父进程创建多个子进程,会出现写端重复继承的情况,导致子进程之间也能互相通信;而命名管道不会出现这种情况。

五、 命名管道的四种情况

命名管道的四种情况和匿名管道的四种情况差不多,主要的区别在于第四点!

  1. 读写端正常,管道如果为空,读端就会被阻塞,等待写端的数据。
  2. 读写端正常,管道为满时,写端阻塞,等待读端读取数据。
  3. 读端正常读,写端关闭,那么读取端会在读取完管道中的所有数据后得到一个特殊的信号,表明已经到达了管道的末尾。这样读取操作就会返回值为0,也就是read函数会返回0,而不会再被阻塞(不会再等待管道中会有新的数据)。因此,当写端关闭后,读端就知道不会再有新的数据写入管道,可以安全地关闭管道,结束通信。
    在这里插入图片描述
    当我ctrl + c终止了写端,对应读端也退出了
    在这里插入图片描述
  4. 写端正常,读端关闭。写入操作不会因为读端关闭而阻塞,写入进程可以继续向管道写入数据,直到读取进程再次打开读端,或者管道被关闭,或者管道被写满。
    在这里插入图片描述
    这也是唯一和匿名管道的区别!对于匿名管道,写端正常,读端关闭,那么操作系统会为写端发送SIGPIPE信号终止该进程。这是因为在匿名管道中,通信的两个进程是有血缘关系的,读取端关闭后,写入端无法将数据传递给任何进程,所以操作系统只能终止;而命名管道的任意进程之间都可以通信,所以写端只管写就行。

六、代码

Gitee代码仓库:点击跳转

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

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

相关文章

【PB案例学习笔记】-18制作一个IP地址编辑框

写在前面 这是PB案例学习笔记系列文章的第18篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

46-4 等级保护 - 网络安全等级保护概述

一、网络安全等级保护概述 原文:没有网络安全就没有国家安全 二、网络安全法 - 安全立法 中华人民共和国主席令 第五十三号 《中华人民共和国网络安全法》已于2016年11月7日由中华人民共和国第十二届全国人民代表大会常务委员会第二十四次会议通过,并自2017年6月1日起正式…

LabVIEW图像采集处理项目中相机选择与应用

在LabVIEW图像采集处理项目中&#xff0c;选择合适的相机是确保项目成功的关键。本文将详细探讨相机选择时需要关注的参数、黑白相机与彩色相机的区别及其适用场合&#xff0c;帮助工程师和开发者做出明智的选择。 相机选择时需要关注的参数 1. 分辨率 定义&#xff1a;分辨率…

C++ - Clion安装Qt msvc2017版本教程,基础环境配置clion+ Qt5.12.12 msvc2017 + VS2019

背景&#xff1a;平时代码开发使用clion&#xff0c;但使用项目要制定mscv2017版本Qt。先装过mingw版本Qt无法运行&#xff0c;但msvc版本依赖装有Visual Studio&#xff0c;本地装的又是2019版。就出现了这个大坑&#xff0c;需要配置好clion Qt msvc2017 VS2019。 文章目录 …

立创EDA专业版设置位号居中并调整字体大小

选择某一个器件位号&#xff0c;右键->查找&#xff1a; 选择查找全部&#xff1a; 下面会显示查找结果&#xff1a; 查看&#xff0c;所有的位号都被选中了&#xff1a; 然后布局->属性位置&#xff1a; 属性位置选择中间&#xff1a; 然后位号就居中了 调整字体大小&a…

串口通信技术基础

1.0 串口通信基础 数据通信的两种常用形式&#xff1a; 1&#xff1a;并行通信 和 串行通信 并行方式&#xff1a;数据的各位使用多条数据线同时发送或同时接收 特点&#xff1a;传送速度快&#xff0c;但因需要多根传输线&#xff0c;曾经在近距离、高速率通信中使用 串行方式…

01——生产监控平台——WPF

生产监控平台—— 一、介绍 VS2022 .net core(net6版本&#xff09; 1、文件夹&#xff1a;MVVM /静态资源&#xff08;图片、字体等&#xff09; 、用户空间、资源字典等。 2、图片资源库&#xff1a; https://www.iconfont.cn/ ; 1.资源字典Dictionary 1、…

验证码识别接口、多种样式验证码识别接口、中英文验证码识别接口

验证码识别接口、多种样式验证码识别接口、中英文验证码识别接口 本文提供一个基于OCR和机器学习的验证码识别接口&#xff0c;能够识别较复杂的中文、英文验证码&#xff0c;在OCR的基础上针对验证码进行算法优化。本接口是收费的&#xff08;最低0.5分1次调用&#xff0c;试…

【最新鸿蒙应用开发】——总结ArkUI生命周期

鸿蒙ArkUI相关的生命周期都有哪些? 1. UIAbility生命周期 onCreate、onWindowStageCreate、onForeground、onBackground、onWindowStageDestroy、onDestroy。 onCreate&#xff1a;Create状态为在应用加载过程中&#xff0c;UIAbility实例创建完成时触发&#xff0c;系统会调…

python tushare股票量化数据处理:笔记

1、安装python和tushare及相关库 matplotlib pyplot pandas pandas_datareader >>> import matplotlib.pyplot as plt >>> import pandas as pd >>> import datetime as dt >>> import pandas_datareader.data as web 失败的尝试yf…

计蒜客:C10 第四部分:深度优先搜索基础 引爆炸弹

【C代码】 #include<bits/stdc.h> using namespace std; int n,m,ans0; char maze[501][501]; bool vis[501][501]; void dfs(int x,int y){vi…

解析中断引起的调度延迟问题

解析软中断引起的调度延迟问题 一、导言二、线程调度的原理三、如何定位中断导致的调度延迟方法一:使用内核 ftrace工具方法二:使用开源ko工具方法三:修改内核源码添加打印一、导言 硬件中断和软件中断都有可能导致调度延迟,但两者的影响方式略有不同。 硬件中断:当硬件设…

stm32MP135裸机编程:启动流程分析

0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf STM32MP135AD数据手册.pdf1 stm32MP135裸机启动流程分析 1.1 启动方式 stm32MP135支持8种启动方式&#xff1a; 注&#xff1a; UART和USB启动并不是指通过UART/USB加载程序&#xff0c;而是通过UA…

Spring Boot 项目启动时在 prepareContext 阶段做了哪些事?

概览 如果你对Spring Boot 启动流程还不甚了解&#xff0c;可阅读《Spring Boot 启动流程详解》这篇文章。如果你已了解&#xff0c;那就让我们直接看看prepareContext() 源码。 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironme…

2024.6.7

思维导图 代码 #include <iostream>using namespace std;//创建一个RMB类 class RMB {friend const RMB operator(const RMB &p1, const RMB &p2);friend const RMB operator-(const RMB &p1, const RMB &p2);friend bool operator>(const RMB &…

《Windows API每日一练》3.1 绘制文本

本节我们将讲述如何在窗口客户区绘制文本。如果在客户区绘制文本&#xff0c;需要将整个客户区或指定文本所在的矩形区域设置为无效区域&#xff0c;然后产生WM_PANIT消息&#xff0c;调用GDI函数绘制文本。此外&#xff0c;如果要绘制文本还需要使用设备环境上下文句柄&#x…

阿里发布最强开源大模型通义千问Qwen2,国产最好用的LLM

前言 近年来&#xff0c;大模型技术发展迅速&#xff0c;开源模型的出现为AI研究和应用带来了新的活力。在这一背景下&#xff0c;阿里云通义千问团队发布了全新升级的Qwen2系列开源模型&#xff0c;为国内外开发者提供了更强大的工具和更丰富的选择。 Huggingface模型下载&am…

springboot3 数据访问

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 数据访问 一、准备数据库表二、项目创建2.1、使用spring initializer 创建2.2、添加数据库配置2.3 mapper2.4 编写controller2.5 总结 三、其他数据源 一、准备数据库表 CRE…

追觅科技2025校园招聘测评已发(真题)

&#x1f4e3;追觅科技 2025校园招聘测评已发&#xff0c;正在申请的小伙伴看过来哦&#x1f440; ㊙本次校招面向全球于2023年7月 - 2025年12月期间毕业的同学&#xff0c;开放了四大类岗位&#xff1a;营销类、研发类、制作供应类、职能类~ ✅测评解析 &#x1f449; 测评自…

Kimichat使用案例012:用Kimichat拆解雷军在小米汽车SU7发布会上的演讲技巧

文章目录 一、介绍二、输入内容三、输出内容四、继续追问五、继续回答六、讲解对比七、对比回答相似之处:不同之处:八、职场人士如何借鉴九、借鉴内容一、介绍 小米SU7发布会可以说是非常成功。雷军的演讲技巧是发布会成功的重要因素之一,很值得借鉴学习。 可以借助Kimichat…