【Linux系统化学习】深入理解匿名管道(pipe)和命名管道(fifo)

目录

进程间通信

进程间通信目的

进程间通信的方式

管道

System V IPC(本地通信)

POSIX IPC(网络通信)

管道

什么是管道

匿名管道

匿名管道的创建

匿名管道的使用

匿名管道的四种情况

匿名管道的五种特性

命名管道

指令级的命名管道

代码级的命名管道

读端

写端

匿名管道与命名管道的区别


进程间通信

从Linux这个专栏开始我们已经系统学习了两大块内容——进程和文件系统。但是内存中的文件离不开进程;因此可见进程的重要性,但是我们只是对单一的一个进程进行研究。可实际我们总能发现需要将一个程序的输出交给另一个程序进行处理,这就是进程间的通信;但是进程具有独立性,我们不可以将一个进程的数据拷贝给另一个进程,因此两个进程通信必须含有一个中间媒介用于音系交流。

进程间通信的本质就是:让不同的进程先看到同一份资源。

进程间通信目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的方式

管道

  • 匿名管道pipe
  • 命名管道FIFO

System V IPC(本地通信)

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC(网络通信)

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道

什么是管道

  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

注意:是因为含有一种数据的传送方式类似管道才有的管道,而不是因为管道这个名词而建立的一种数据传送方式。(注意这两种的因果关系)

 


匿名管道

匿名管道的创建

#include<unistd.h>
int pipe(int fd[2]);

功能:创建一个匿名管道

参数

  • fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
  • 返回值:成功返回0,失败返回错误码。

匿名管道的使用

#include<iostream>
#include<cstring>
#include<unistd.h>
#include<cassert>
#include<wait.h>
using namespace  std;
#define MAX 1024

int main()
{
    //第一步,建立管道
    int fd[2]={0};
    int n = pipe(fd);
    assert(n==0);
    (void)n;

    //第二步,创建子进程
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
        return 1;
    }
    //子写父读
    //第三步:父子双方关闭不需要的fd,形成单行通道
    if(id==0)
    {
        //child
        close(fd[0]);
        char massage[MAX];
        int cnt =10;
        while(cnt)
       {
         snprintf(massage,sizeof(massage),"I am a child , pid : %d ppid : %d  cnt : %d ",getpid(),getpid(),cnt--);
        // w - 只向管道写入
         write(fd[1],massage,strlen(massage));
         sleep(1);
       }
       exit(0);
    }

    //father
    close(fd[1]);
    char buffer[MAX];
    while(true)
    {
        // r - 只从管道读取
        ssize_t n = read(fd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]={0};
            cout<<getpid()<<"child say:"<<buffer<<endl;
        }
        sleep(1);
    }
    pid_t rid = waitpid(id,nullptr,0);
    if(rid==id)
    {
        cout<<"wait success"<<endl;
    }
    return 0;
}

现象的解释:

在创建子进程前,建立管道;然后创建子进程,父进程关闭写端只做读取,子进程关闭读端只做写入;通过管道子进程写入的数据通过管道被父进程读取。

匿名管道的四种情况

1. 正常情况,如果管道没有数据了,读端必须等待,直到有数据为止(写端写入数据了)

2. 正常情况,如果管道被写满了,写端必须等待,直到有空间为止(读端读走数据)

3. 写端关闭,读端一直读取, 读端会读到read返回值为0, 表示读到文件结尾

4. 读端关闭,写端一直写入,操作系统会直接杀掉写端进程,通过想目标进程发送SIGPIPE(13)信号,终止目标进程。

匿名管道的五种特性

1. 匿名管道,可以允许具有血缘关系的进程之间进行进程间通信,常用与父子,仅限于此

2. 匿名管道,默认给读写端要提供同步机制 

3. 面向字节流的

4. 管道的生命周期是随进程的

5. 管道是单向通信的,半双工通信的一种特殊情况

从文件描述符的角度来理解管道的原理

管道的原理需要结合文件系统来描述。当进程分别以读和写打开同一个文件,进程会创建PCB;PCB含有指向关于该进程打开的所有文件信息结构体(struct files_struct)的指针(struct files_struct*),这个结构体中含有一个数组,数组的每个下标代表所打开的每个文件,数组的每个元素为一个指针(struct file* fd——array[])指向被打开的文件;读和写在内存中都会加载该内存,在内存中虽然有两个文件但是这两个文件公用一个缓冲区。当fork()创建子进程的时候,会发生浅拷贝;因此子进程中的所有数据和父进程是一样的,包括指针信息。子进程中数组元素也是指向父进程所打开的读写文件,这两个文件又公用一个缓冲区;这两个文件被连个指针所指向,使用引用计数而实现需不需要文件的关闭。关闭父进程的写文件和关闭子进程的的读文件,这样子进程将信息写到缓冲区中,父进程将数据自己读取,不加载到内存中,这样就实现了管道。


命名管道

  • 匿名管道的一个限制就是只能在具有共同祖先(具有血缘关系)的进程间通信
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

指令级的命名管道

命名管道可以在命令行上创建,使用下面指令:

mkfifo 文件名

 

现象的解释:这个过程是动态的,由于动图太大不方便演示。在一个文件夹中创建了一个管道文件。echo指令进行循环写入,在另一个端口下cat命令进行读取。通过这个管道文件实现了两个不相关进程之间的通信。这个管道文件为中间媒介是实现了两个进程间的通信。

代码级的命名管道

管道也可以在程序里创建,相关函数为:

int mkfifo(const char *filename,mode_t mode)

其实命名管道就是个文件,在一个进程中创建这个文件,进行写入/读取,或者在同时在另一个文件中进行读取和写入操作,本质就是:两个不同的进程同时对同一个文件进行文件操作。这个文件就实现了进程间的通信,这个文件就是管道。

读端

在程序的一开始,直接打开这个文件;当文件不存在时创建这个管道文件,直到创建成功为止;

然后使用系统调用文件操作读函数,对文件进行读取。

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "RW.h"

bool MakeFifo()
{
    int n = mkfifo(FILENAME, 0666);
    if(n < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        return false;
    }

    std::cout << "mkfifo success... read" << std::endl;
    return true;
}

int main()
{
Start:
    int rfd = open(FILENAME, O_RDONLY);
    if(rfd < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        if(MakeFifo()) goto Start;
        else return 1;
    }
    std::cout << "open fifo success..." << std::endl;

    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout << "Client say# " << buffer << std::endl;
        }
        else if(s == 0)
        {
            std::cout << "client quit, server quit too!" << std::endl;
            break;
        }
    }

    close(rfd);
    std::cout << "close fifo success..." << std::endl;

    return 0;
}

写端

也是在程序一开始直接打开指定的管道文件,判断是否打开成功;打开成功后使用你系统调用文件操作写函数对文件进行写入。

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "RW.h"

int main()
{
    int wfd = open(FILENAME, O_WRONLY);
    if (wfd < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        return 1;
    }
    std::cout << "open fifo success... write" << std::endl;

    std::string message;
    while (true)
    {
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);

        ssize_t s = write(wfd, message.c_str(), message.size());
        if (s < 0)
        {
            std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
            break;
        }
    }

    close(wfd);
    std::cout << "close fifo success..." << std::endl;

    return 0;
}

现象的解释:当我们同时在两个窗口运行这两个可执行程序时,在写端写入回车成功后;将数据写到管道文件中,当读端检测到管道文件中数据时会将这条消息读取。其实这个过程是同步进行的,由于这里动图太大,不方便演示。 

从底层来看命名管道的原理和匿名管道的原理基本相同这里就不过多赘述了。


匿名管道与命名管道的区别

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的意义。

今天对Linux下管道实现进程间通信的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!   

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

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

相关文章

VSCODE上使用python_Django_创建最小项目

接上篇 https://blog.csdn.net/weixin_44741835/article/details/136135996?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136135996%22%2C%22source%22%3A%22weixin_44741835%22%7D VSCODE官网&#xff1a; Editing Python …

无人机快递(物流)技术方案,无人机快递(物流)基础知识

无人机快递技术是一种利用无人机进行快递配送的先进技术。通过利用无人机&#xff0c;快递企业能够在偏远地区或难以通行的地区提供配送服务&#xff0c;同时提高配送效率并降低人力成本。 无人机基本情况 无人驾驶飞机简称“无人机”&#xff0c;是利用无线电遥控设备和自备的…

SQLite 的使用

SQLite 是一个轻量级、自包含和无服务器的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于嵌入式系统、移动应用程序和小中型网站。它易于创建、需要的配置较少&#xff0c;并且提供了用于管理和操作数据的强大功能集。本文&#xff0c;我们将带领你…

linux---防火墙拓展

目录 一、iptables 1.基本语法 2.四表五链——重点记忆 2.1四表 2.2五链 2.3总结 3.iptables选项示例 3.1 -Z 清空流量计数 3.2 -P 修改默认规则 3.3 -D 删除规则 3.4 -R 指定编号替换规则 4.白名单 5.通用匹配 6.示例 6.1添加回环网卡 6.2可以访问端口 6.3 主…

Encoder-decoder 与Decoder-only 模型之间的使用区别

承接上文&#xff1a;Transformer Encoder-Decoer 结构回顾 笔者以huggingface T5 transformer 对encoder-decoder 模型进行了简单的回顾。 由于笔者最近使用decoder-only模型时发现&#xff0c;其使用细节和encoder-decoder有着非常大的区别&#xff1b;而huggingface的接口为…

设计模式——抽象工厂模式

定义: 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;提供一个创建一系列或相互依赖对象的接口&#xff0c;而无须指定它们具体的类。 概述:一个工厂可以提供创建多种相关产品的接口&#xff0c;而无需像工厂方法一样&#xff0c;为每一个产品都提供一个具体…

【开源】JAVA+Vue.js实现医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的遥感目标检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;本文介绍了一种基于深度学习的遥感目标检测系统系统的代码&#xff0c;采用最先进的YOLOv8算法并对比YOLOv7、YOLOv6、YOLOv5等算法的结果&#xff0c;能够准确识别图像、视频、实时视频流以及批量文件中的遥感目标。文章详细解释了YOLOv8算法的原理&#xff0…

力扣226 翻转二叉树 Java版本

文章目录 题目描述解题思路代码 题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root…

智慧物业信息管理系统平台及APP建设项目

随着城市化步伐的不断加快&#xff0c;物业管理成为城市建设中不可或缺的一部分。为了更好地满足各方对物业管理的全面需求&#xff0c;智慧物业信息管理系统平台及APP项目&#xff0c;融合了八大子系统&#xff0c;旨在为其提供更全面、高效的物业管理解决方案。 1. 物业信用…

盘点自动化汽车生产线设备 数据采集分析联合各设备

1.机器人自动装配线 机器人自动装配线已成为汽车制造业中的常见场景。这些机器人在汽车组装的各个环节发挥关键作用&#xff0c;从焊接和铆接到零部件组装。它们不仅提高了装配速度&#xff0c;还确保了产品的一致性&#xff0c;降低了废品率。 2.3D打印技术 3D打印技术正在汽车…

Draw.io绘制UML图教程

一、draw.io介绍 1、draw.io简介 draw.io 是一款强大的免费在线图表绘制工具&#xff0c;支持创建流程图、组织结构图、时序图等多种图表类型。它提供丰富的形状库、强大的文本编辑和样式设置功能&#xff0c;使用户能够轻松创建专业级图表。draw.io 具有用户友好的界面&…

UTONMOS开启数智龙年,打造元宇宙游戏圈新名片

新年已过&#xff0c;全国各个城市早已客流涌动、热闹非凡。这种繁华景象不仅存在于现实世界&#xff0c;也被复刻到元宇宙的虚拟空间中。 据介绍&#xff0c;UTONMOS“源起山海-神念无界”元宇宙游戏是以原创IP玄幻神话故事“元宇宙史纪”为蓝本打造的元宇宙游戏空间&#xf…

图文说明Linux云服务器如何更改实例镜像

一、应用场景举例 在学习Linux的vim时&#xff0c;我们难免要对vim进行一些配置&#xff0c;这里我们提供一个vim插件的安装包&#xff1a; curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o./install.sh && bash ./install.sh 但是此安装包…

可变形注意力(Deformable Attention)及其拓展

文章目录 一、补充知识&#xff08;一&#xff09;可变形卷积&#xff08;Deformable Convolution&#xff09;&#xff08;二&#xff09;多头注意力机制 二、可变形注意力模块三、可变形自注意力模块&#xff08;一&#xff09;偏移模块&#xff1a;&#xff08;二&#xff0…

“比特币暴涨讯号显现”!减半牛市来临前还有一次震撼回撤?“52000美元保卫战”已经打响!

虽然比特币在20日一度冲高至近5.3万美元大关&#xff0c;创下自2021年11月来新高&#xff0c;但随后开始回落&#xff0c;在51000美元至52000美元之间反复窄幅波动&#xff0c;甚至在21日晚一度跌至50625美元。比特币的未来走势&#xff0c;已牵动不少投资者的心。 自1月底比特…

华为OD机试真题-来自异国的客人-2023年OD统一考试(C卷)--Python3--开源

题目&#xff1a; 考察内容&#xff1a; 10进制转为任何进制 代码&#xff1a; """ 题目分析&#xff1a;输入&#xff1a; k --物品价值&#xff1b;n 幸运数字&#xff1b;m 进制 输出&#xff1a; 幸运数字的个数 异常&#xff1b;0 eg; 10 2 4思路&…

STM32—触摸键

目录 1 、 电路构成及原理图 2 、编写实现代码 3、代码讲解 4、烧录到开发板调试、验证代码 5、检验效果 此笔记基于朗峰 STM32F103 系列全集成开发板的记录。 1 、 电路构成及原理图 触摸键简单的了解就是一次电容的充放电过程。从原理图可以看出&#xff0c;触摸键 …

代码随想录算法训练营第21天—回溯算法01 | ● 理论基础 ● *77. 组合

理论基础 回溯是一种纯暴力搜索的方法&#xff0c;它和递归相辅相成&#xff0c;通常是执行完递归之后紧接着执行回溯相较于以往使用的for循环暴力搜索&#xff0c;回溯能解决更为复杂的问题&#xff0c;如以下的应用场景应用场景 组合问题 如一个集合{1,2,3,4}&#xff0c;找…

Linux 权限详解

目录 一、权限的概念 二、权限管理 三、文件访问权限的相关设置方法 3.1chmod 3.2chmod ax /home/abc.txt 一、权限的概念 Linux 下有两种用户&#xff1a;超级用户&#xff08; root &#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff…