【Linux】—— 命名管道详解

news2024/11/16 3:46:08

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

目录

(一)什么是命名管道

(二)命名管道原理理解

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(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

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

 

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

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

相关文章

【cucumber】cucumber-reporting生成测试报告

原始的cucumber report 比较粗糙 我们可以通过cucumber-reporting 插件对报告进去优化 在pom.xml里面添加cuccumber-reporting 插件 <!-- 根据 cucumber json文件 美化测试报告--><dependency><groupId>net.masterthought</groupId><artifactId>…

何为PyTorch?

PyTorch的名字来源于它的功能和设计哲学。"Py"代表Python&#xff0c;因为PyTorch是一个基于Python的深度学习库&#xff0c;它充分利用了Python语言的灵活性和易用性&#xff0c;为开发者提供了简洁而强大的接口。“Torch”则代表其前身—— Torch&#xff0c;这是一…

瓦片地图游戏开发的底层代码

原理&#xff1a; 二维数组存储每个瓦片序号 然后有一个缓冲区存储瓦片图片&#xff0c; 最后连续采样缓冲区&#xff0c;粘贴到屏幕上&#xff0c; 而缓冲区数据随着采样越界再重新更新 #include <graphics.h> #include <stdio.h>// 默认游戏地图 int map[20…

使用vue-pdf插件加载pdf

安装&#xff1a; // 安装这个版本&#xff0c;其它版本会有千奇百怪的错&#xff0c;这个版本和4.0.0都是可以的 cnpm install vue-pdf4.2.0// 安装pdfjs-dist cnpm install pdfjs-dist2.5.207 使用&#xff1a; // 我的css样式是pxToRem&#xff0c;友友们使用可能样式会有…

二叉树题目:二叉树的序列化与反序列化

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的序列化与反序列化 出处&#xff1a;297. 二叉树的序列化与反序列化 难度 8 级 题目描述 要求 序列…

大数据开发之Scala

第 1 章&#xff1a;scala入门 1.1 概述 scala将面向对象和函数式编程结合成一种简洁的高级语言 特点 1、scala和java一样属于jvm语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且scala能够直接调用java的类库 2、scala支持两种编程范式面向对象和函数式…

MySQL索引优化:深入理解索引下推原理与实践

随着MySQL的不断发展和升级&#xff0c;每个版本都为数据库性能和查询优化带来了新的特性。在MySQL 5.6中&#xff0c;引入了一个重要的优化特性——索引下推&#xff08;Index Condition Pushdown&#xff0c;简称ICP&#xff09;。ICP能够在某些查询场景下显著提高查询性能&a…

JVM系列-3.类的生命周期

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

想找一个没有中间商的账户,很简单,昂首资本推给你

各位投资者都知道&#xff0c;交易的成本决定我们是否盈利&#xff0c;那么有没有一个没有中间商的账户呢&#xff1f;当然有了&#xff0c;昂首资本这就推给你。 在交易的时候&#xff0c;银行为投资者提供兑换业务&#xff0c;并从中收取费用。至于经纪商&#xff0c;它是交…

爬虫requests+综合练习详解

Day2 - 1.requests第一血_哔哩哔哩_bilibili requests作用&#xff1a;模拟浏览器发请求 requests流程&#xff1a;指定url -> 发起请求 -> 获取响应数据 -> 持续化存储 爬取搜狗首页的页面数据 import requests# 指定url url https://sogou.com # 发起请求 resp…

GPS位置虚拟软件 AnyGo mac激活版

AnyGo for Mac是一款一键将iPhone的GPS位置更改为任何位置的强大软件&#xff01;使用AnyGo在其iOS或Android设备上改变其GPS位置&#xff0c;并在任何想要的地方显示自己的位置。这对那些需要测试应用程序、游戏或其他依赖于地理位置信息的应用程序的开发人员来说非常有用&…

idea远程服务调试

1. 配置idea远程服务调试 这里以 idea 新 ui 为例&#xff0c;首先点击上面的 debug 旁边的三个小圆点&#xff0c;然后在弹出的框框中选择 “Edit”&#xff0c;如下图所示。 然后进入到打开的界面后&#xff0c;点击左上角的 “” 进行添加&#xff0c;找到 “Remote JVM De…

Java 面向对象 04 构造方法(黑马)

这是以前没有赋值的时候&#xff0c;在&#xff08;&#xff09;里面是空的&#xff1a; 代码&#xff1a; 左边的是调用的空参构造&#xff0c;但是右边没有写空参构造&#xff0c;并不会报错&#xff0c;因为虚拟机会自己给外面一个空参构造的方法&#xff0c;就是这样的&am…

详谈c++智能指针!!!

文章目录 前言一、智能指针的发展历史1.C 98/03 的尝试——std::auto_ptr2.std::unique_ptr3.std::shared_ptr4.std::weak_ptr5.智能指针的大小6.智能指针使用注意事项 二、智能指针的模拟实现三、C11和boost中智能指针的关系 前言 C/C 语言最为人所诟病的特性之一就是存在内存…

《SPSS统计学基础与实证研究应用精解》视频讲解:SPSS数据文件读取

《SPSS统计学基础与实证研究应用精解》4.3 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解4.3节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。本书旨在手把手教会使…

码农维权——案例分析之违法解除劳动合同(一)

目录 一、背景 二、举证责任方&#xff1a;需要公司举证 三、员工可以自证没有严重违反公司规章制度吗&#xff1f; 四、公司解除劳动合同的程序合法吗&#xff1f; 五、写在最后 一、背景 当前互联网行业普遍以”变相裁员“为目的&#xff0c;公司采用各种手段”逼迫“员…

<信息安全>《1 国内主要企业网络安全公司概览(一)》

1 深信服科技股份有限公司 信息内容LOGO成立日期2000年12月25日成立。总部深圳市南山区学苑大道1001号南山智园A1栋是否上市深信服[300454]A股市值265亿主要产品企业级网络安全云计算IT基础设施数据通信物联网员工规模9000人分支机构全球50多个荣誉国家级高新技术企业、中国软…

Python基础第七篇(Python的文件操作)

文章目录 一、文件编码二、文件的读取操作1.操作代码2.读出结果 三、文件的写出操作1.源代码2.读出结果 四、文件的追加操作1.源代码2.读出结果 这篇文章旨在深入浅出地介绍Python在文件操作上的能力&#xff0c;包括文件的编码、读取和写入等基本操作。内容丰富、易于理解&…

Macos flatter(用于快速LLL)本地编译安装(解决安装过程各种疑难杂症)

flatter是一个开源项目&#xff0c;能大大提高LLL的速度&#xff0c;项目提供的安装文档适用于Ubuntu&#xff0c;但是在macos上安装&#xff0c;总会遇到各种各样的问题&#xff0c;这里记录下所踩坑&#xff0c;帮助大家快速在macos上安装flatter。 文章目录 1.安装依赖库&am…

Unity学习-逐帧图集动画制作

首先在文件部分创建一个Sprite Library Asset 然后点击创建出来的文件 点下面的加号添加对应的图 添加完成之后点一下Apply 然后新建一个物体 添加这三个组件 其中SpriteLibrary里面 把你刚刚创建的图集文件拉过来 Sprite Resolver选择对应的动作和图片 然后开始制作动画 An…