【Linux】—— 命名管道详解

命名管道是一种在操作系统中用于进程间通信的机制,它允许不同的进程之间通过管道进行数据交换。与匿名管道相比,命名管道具有更多的灵活性和功能。在本博客中,我们将深入探讨命名管道的概念、用途以及如何在编程中使用它们。

目录

(一)什么是命名管道

(二)命名管道原理理解

2.1 创建一个命名管道

2.2 命名管道原理

(三)代码示例

3.1命名管道的打开规则

3.2 实现server&client通信 

 (四)小结


(一)什么是命名管道

命名管道是一种具有独特标识符(通常是文件路径)的管道。它允许不同进程通过该标识符进行通信。与无名管道不同,命名管道存在于文件系统中,使得它们能够跨越进程和甚至计算机边界进行通信。

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

(二)命名管道原理理解

2.1 创建一个命名管道

1️⃣命名管道可以从命令行上创建,命令行方法是使用下面这个命令

mkfifo filename
  • 我们不了解的话可以通过man手册去对这个函数进行学习: 

 示例演示:

【解释说明】 

  • 我们可以先手动的创建一个管道文件,给它起一个 fifo 的文件名,创建好之后我们就看到了当前目录下存在了一个文件叫做 fifo ;
  • 其次我们可以发现它的文件类型前面以P开头,当大家看到P开头的,会能想到什么?在之前我给大家在讲我们Linux基础命令的时候说过一个话题叫做文件类型:以 - 开头普通文件、以D开头为目录文件、以L开头为链接文件L开头的叫做软链接、这里以P开头叫做管道文件,这时候在磁盘上存在了一个管道文件

  接下来,我准备向文件中写入数据:

【解释说明】  

  • 在我们的理解中把它写到文件当中,此时就相当于当我一敲回车,echo对应的这个东西就会变成进程;
  • 然后,执行我们向显示器当中打印,经过重定向,它最终不向显示器文件打印,而向管道文件中打印,所以底层作为重定向是没问题的;
  • 紧接着我们就尝试去写了,但当前呢它卡在这里的,什么都没做,我们再看一下当前这个管道文件里,当前显示的是零,好像没有写入啊;
  • 这是因为管道文件有种特殊特性,虽然在磁盘当中创建了这个 fifo,但它仅仅是一种符号,那么对于这种符号呢,将来你向这个文件里写入的消息,并没有或者并不会刷新落实到磁盘上,而是只帮我们在这里直接 echo,然后写入管道文件当中,但是管道文件当前是内存级的,所以你的大小没有变。

接下来,我们在尝试一下输出重定向操作:

 【解释说明】  

  • 对于 cat 指令默认就是从显示器当中进行读取,你输什么它给你打印什么;
  • cat fifo 可以直接从管道文件当中输出重对象,将曾经他向管道里写的东西写到它里面;
  • 另外cat也是一条命令,它一旦启动就会变成进程,所以这是一个进程,这俩进程没有任何关系,然后我们这个hello world 的消息最终就打印了出来,这种通讯方式我们就称之为叫做命名管道。

 接下来,我再改一下代码逻辑,再给大家分析一波:

【解释说明】

  • 在进行显示的时候,我让它每隔一秒进行向我们显示器当中打印一条hello world;
  • 但我不想往显示器打印,我想让往管道打印,此时上述那一堆就对应的一个进程,向我们对应的管道文件当中写入;
  • 那么对于 fifo 此时呢,我们就看到本来应该显示到这个显示器文件这个终端的字符串,最终经过管道被重定向到了右端。

2️⃣ 命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode);

2.2 命名管道原理

【解释说明】 

  • 现在假设左边框框里,它是我们对应的操作系统内部的一大堆的数据结构,和它对应的磁盘组成;
  • 如果磁盘里有一个文件,现在有一个问题,如果我有一个进程要打开它,这是个进程,打开一个文件,有自己的文件描述符表,然后012定义的是标准输入标准输出标准错误这;
  • 此时,对应的要打开我们对应的这个文件,怎么打开呢?我曾经讲过一个文件如果不打开,它就在磁盘上静静的躺着,就有了我们之前讲的分区,然后格式化,然后有文件系统等;
  • 紧接着,对应的这个磁盘文件,它里面还可以去包含文件相关的属性inode,还有它对应的缓冲区等等,一旦它被打开了,首先要在操作系统内为该文件创建对应的struct file对象,然后这个struct file对象它有对应的自己的缓冲区;
  • 在结构体当中,有指针指向它的缓冲区,实际上它里面严格上讲应该是指向它inode,然后进一步指向缓冲区

【解释说明】  

  •  我们让父进程创建子进程的时候,子进程会继承父进程的文件描述符表;
  • 所以对于子进程,它直接继承父进程的文件描表之后,它会把这个值拷下来,拷下来之后,他俩就指向同一个文件了;

 上诉是第一种情况,假设如果又来了一个进程,他也要有自己的文件描述符表,紧接着有一个小问题?

  • 如果这个进程此时也打开磁盘中的这个文件了,还用不用再在操作系统内部给他创建一个对应的struct file 结构体,然后给他创建一个缓冲区,然后这样去搞呢?

【解释说明】 

  •  其实大可不必;
  • 因为在正常的情况下,对应的文件的属性的值呢数字是一样的,所以操作系统内根本就不需要维护对应的两个一样的结构体;

【解释说明】  

  •  所以对于我们来讲,实际上当你想打开这个文件时,当新进程想打开其他文件时,新进程先做的第一件事情是在所有已经打开的文件列表里去找这个文件是否已经被打开了,没有被打开则创建,如果已经打开了;
  • 直接把对应的我们struct file对象的地址填入到对应的下标里,而对应的这个struct file对象里面包含一个叫做ref,我们称之为引用计数;
  • 这个引用计数指向的时候,默认是1,再有进程打开它就变成2了,当你关闭这个文件时,它就把这个ref进行减减操作,由2再变成对应的1,当你再关闭它才由1变成0,然后操作系统才释放它。

【注意事项】

不过如果这个文件是一个普通文件,将来你写的时候数据要定期刷新到磁盘里面,可是今天我们的目的是想让这两个进程,通过对应的缓冲区让他们俩之间通信,好让他们俩直接通信,那么我们的文件呢就不应该把这个数据刷到磁盘上,所以未来呢,我们如果创建了一个文件,这种文件必须有一个特性,就叫做是内存级

 【解释说明】  

  •  也就是说对于这种文件只需要把数据有一个进程写进来,另一个进程再从缓冲区中读,此时不需要做这个刷盘动作;
  • 如果做了刷盘操作:一导致速度慢了,二也没必要,所以我们就由此诞生了一种文件,这种文件就叫做管道文件,或者叫做命名管道文件,它就是一个文件,只不过这个文件呢,没有所谓的data block,它就是在磁盘当中的一种

(三)代码示例

3.1命名管道的打开规则

如果当前打开操作是为而打开FIFO时

  • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
  • O_NONBLOCK enable:立刻返回成功

如果当前打开操作是为而打开FIFO时

  • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
  • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
     

3.2 实现server&client通信
 

【client.cc】

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "common.hpp"

int main()
{
    //1. 不需创建管道文件,我只需要打开对应的文件即可!
    int wfd = open(fifoname.c_str(),O_WRONLY);
    if(wfd < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        return 1;
    }

    //正常通信
    char buffer[NUM];
    while(true){
        system("stty raw");
        int c = getchar();
        system("stty -raw");

        ssize_t n = write(wfd, (char*)&c, sizeof(char));
        assert(n >= 0);
        (void)n;
    }

    //关闭不要的fd
    close(wfd);
    return 0;
}

【server.cc】

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

int main()
{
    // 1. 创建管道文件
    umask(0); //这个设置并不影响系统的默认配置,只会影响当前进程
    int n = mkfifo(fifoname.c_str(),mode);
    if(n != 0)
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 1;
    }
    std::cout << "create fifo file success" << std::endl;

    // 2.让服务端直接开启管道文件
    int rfd = open(fifoname.c_str(), O_RDONLY);
    if(rfd < 0 )
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }
    std::cout << "open fifo success" << std::endl;

    // 3.正常通信
    char buffer[NUM];
    while (true)
    {
        buffer[0] = 0;
        ssize_t n = read(rfd,buffer,sizeof(buffer));
        if(n > 0){
            buffer[n] = 0;
            printf("%c", buffer[0]);
            fflush(stdout);
        }
        else if(n == 0){
            std::cout << "client quit, me too" << std::endl;
            break;
        }
        else {
            std::cout << errno << " : " << strerror(errno) << std::endl;
            break;
        }
    }
    
    // 4.关闭不要的fd
    close(rfd);

    unlink(fifoname.c_str());
    return 0;
}

【common.hpp】

#pragma once

#include <iostream>
#include <string>

#define NUM 1024

const std::string fifoname = "./fifo";
uint32_t mode = 0666; 

 (四)小结

以上便是关于命名管道的全部知识内容了,接下来简单小结命名管道以及匿名管道和命名管道之间的区别!!

 命名管道:

  1. 基本概念: 命名管道是一种通过文件系统路径标识的通信管道,允许不同进程之间进行通信。

  2. 创建方式: 在Unix/Linux系统中,可以使用 mkfifo 函数创建命名管道;在Windows系统中,可以使用 CreateNamedPipe 函数。

  3. 通信方向: 命名管道同样是单向的,但可以被用于任意两个进程之间的通信,甚至是不同计算机之间。

  4. 生命周期: 命名管道的生命周期不受限于创建它的进程,可以长时间存在于文件系统中。

  5. 灵活性: 命名管道相对于匿名管道更灵活,适用于更复杂的进程通信场景,包括跨越进程和计算机的通信。

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

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

本文内容便讲解结束,感谢大家的观看和支持!! 

 

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

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

相关文章

多线程(看这一篇就够了,超详细,满满的干货)

多线程 一.认识线程&#xff08;Thread&#xff09;1. 1) 线程是什么1. 2) 为啥要有线程1.3) 进程和线程的区别标题1.4) Java的线程和操作系统线程的关系 二.创建线程方法1:继承Thread类方法2:实现Runnable接口方法3:匿名内部类创建Thread子类对象标题方法4:匿名内部类创建Runn…

139:leafle加载here地图(v3软件多种形式)

第139个 点击查看专栏目录 本示例介绍如何在vue+leaflet中添加HERE地图(v3版本的软件),并且含多种的表现形式。包括地图类型,文字标记的设置、语言的选择、PPI的设定。 v3版本和v2版本有很大的区别,关键是引用方法上,请参考文章尾部的API链接。 直接复制下面的 vue+leaf…

SpringCloud之Nacos的学习、快速上手

1、什么是Nacos Nacos是阿里的一个开源产品&#xff0c;是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案&#xff0c;用来实现配置中心和服务注册中心。 Nacos 快速开始 2、安装运行nacos nacos下载地址 下载地址: https://github.com/alibaba/nacos/rel…

冒泡排序-BubbleSort

1、基本思路 从数组的左边开始&#xff0c;比较两个元素的大小&#xff0c;当左边大于右边时&#xff0c;更换左右元素位置&#xff0c;否则不改变&#xff1b;接着向右移动一步&#xff0c;比较第二个元素和第三个元素的大小&#xff0c;重复上述操作&#xff0c;直到最后一个…

VMware workstation安装FreeBSD14.0虚拟机并配置网络

VMware workstation安装FreeBSD14.0虚拟机并配置网络 FreeBSD是类UNIX操作系统&#xff0c;FreeBSD带有多个软件包&#xff0c;并覆盖了广阔的应用领域&#xff0c;且都是免费和易于安装的。该文档适用于在VMware workstation平台安装FreeBSD14.0虚拟机。 1.安装准备 1.1安装…

Spring+SprinMVC+MyBatis配置方式简易模板

SpringSprinMVCMyBatis配置方式简易模板代码Demo GitHub访问 ssm-tpl-cfg 一、SQL数据准备 创建数据库test&#xff0c;执行下方SQL创建表ssm-tpl-cfg /*Navicat Premium Data TransferSource Server : 127.0.0.1Source Server Type : MySQLSource Server Versio…

QCustomPlot 曲线数据结构与存取

对了&#xff0c;我开通了微信公众号&#xff0c;计划是两边会同步更新&#xff0c;并逐步的会将博客上的文章同步至公众号中。感兴趣的朋友可以搜索“里先森sements”来关注&#xff0c;欢迎来玩~&#xff01; 通常&#xff0c;我们对QCustomPlot中的曲线数据无外乎增、删、改…

xshell配置隧道转移规则

钢铁知识库&#xff0c;一个学习python爬虫、数据分析的知识库。人生苦短&#xff0c;快用python。 xshell是什么 通俗点说就是一款强大ssh远程软件&#xff0c;可以方便运维人员对服务器进行管理操作&#xff0c;功能很多朋友们自行探索&#xff0c;今天只聊其中一个功能点那…

【RHCSA服务搭建实验】之apache

虚拟web主机类型 一、基于端口 1.vim /etc/httpd/conf.d/vhost2.conf ---- — 改变http服务默认访问路径 <directory /testweb1>allowoverride none 表示不允许覆盖其他配置require all granted 表示允许所有请求 </directory> <virtualhost 0.0.0.0:…

分布式系统中为什么需要使用消息队列

本文转载自 linkedkeeper.com 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能&#xff0c;成为异步RPC的主要手段之一。 当今市面上有很多主流的消息中间件&#xff0c;如老牌的ActiveMQ、RabbitMQ&#…

【Docker】使用Docker安装Nginx及部署前后端分离项目应用

一、Nginx介绍 Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。它是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点开发的&#xff0c;公开版本1.19.6发布于2020年12月15日。其将源代码以类BSD许可证的形式发布&#xff0c;因它…

RLC如何通过改变频率实现输出稳压

当开关频率工作在容性区域时&#xff0c;容抗抵消完感抗还有剩余&#xff0c;所以容抗感抗可以近似为一个容抗Cr,但加上频率的改变&#xff0c;容抗又可以近似为一个可调电阻 那又改如何控制频率&#xff0c;保持输出稳压&#xff1f; 当输入与输出电压不变时&#xff0c;Rac变…

尝试解决githubclone失败问题

BV1qV4y1m7PB 根据这个视频 似乎是我的linux的github似乎下好了 我没有配置好 比如我的ssh-key 现在根据视频试试 首先需要跳转到ssh的文件夹&#xff1a; cd ~/.ssh 然后生成一个ssh-key&#xff1a; ssh-keygen -t rsa -C "<github资料里的邮箱>" 然后…

Python(18)--文件输入/输出 Ⅱ

​ 大家好&#xff01;我是码银&#x1f970; 欢迎关注&#x1f970;&#xff1a; CSDN&#xff1a;码银 公众号&#xff1a;码银学编程 前言 前一篇文章&#xff08;python(17)–文件的输入/输出-CSDN博客&#xff09;介绍了如何操作文本文件和二进制文件&#xff0c;以及对应…

跨站点请求伪造攻击 - Cross Site Request Forgery (CSRF)

什么是CSRF 最好理解CSRF攻击的方式是看一个具体的例子。 假设你的银行网站提供一个表单,允许当前登录用户将钱转账到另一个银行账户。例如,转账表单可能如下所示: <form method="post"action="/transfer"> <

SpringBoot 更新业务场景下,如何区分null是清空属性值 还是null为vo属性默认值?

先看歧义现象 值为null 未传递此属性 所以此时如何区分null 时传递进来的的null&#xff0c;还是属性的默认值null? 引入方案 引入过滤器&#xff0c;中间截获requestBodyData并保存到HttpServletRequest&#xff0c;业务层从HttpServletRequest 获取到requestBodyData辅…

PCIe 5.0硬件

一、PCIe 5.0概述 (1)什么是PCIe 5.0 第五代快速周边组件互连称为PCI Express 5.0(Peripheral Component Interconnect Express 5.0),也称为第五代PCIe、PCIe 5、PCI v5或简称为PCIe 5.0。PCIe技术于2003年首次推出,现已成为使用点对点访问总线将高速组件连接到主板的标准接…

基于springboot的一个IT人才招聘网站系统源码+数据库+部署文档,公司可以发布岗位需求,求职者查找岗位并递交简历等

介绍 实现一个IT人才招聘系统&#xff0c;公司可以发布岗位需求&#xff0c;求职者查找岗位并递交简历等 启动 1. 主要技术版本 技术名称版本SpringBoot2.5.0MySQL8.0Redis6.2.0 2. 本地启动部署 2.1 数据库数据源部署 src/main/resources/application.yaml 配置文件&am…

Camera理论知识和基本原理(1)

1. 前言 本篇文章为Camera系列文章的第一篇&#xff0c;主要阐述Camera摄像头的基础理论知识&#xff0c;解决Camera硬件或Camera软件开发的一些困惑。该系列文章主要围绕Android操作系统进行&#xff0c;并涉及Android系统上Camera的协议、实现和应用。 2. Basic Concepts …

Java SE入门及基础(24)

目录 方法带参&#xff08;续第23篇文章&#xff09; 3. 对象数组 案例场景 练习 4. 引用数据类型作为方法的参数 案例场景 分析 代码实现 5. 数组作为方法的参数 案例场景 Java SE文章参考:Java SE入门及基础知识合集-CSDN博客 方法带参&#xff08;续第23篇文章&am…