【Linux】 管道扩展 — 开始使用命名管道

在这里插入图片描述

送给大家一句话:
人生有六个字,前面三个是不害怕,后面三个是不后悔。 -- 董卿
🔆🔆🔆🔆🔆🔆🔆🔆

命名管道的功能实现

  • 1 命名管道的原理
  • 2 代码实现
    • 2.1 系统调用
    • 2.2 命名管道的封装
    • 2.3 开始使用
  • 3回归概念
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 命名管道的原理

命名管道时进程间通信的一种,那么原理也就是类似的:先让不同的进程看到同一份(操作系统)资源(“一段内存”)。

匿名管道是通过父子进程的继承关系来满足:父子进程可以看到同一段内存!这段内存会在子进程创建时的拷贝一份,所以并不需要名字,只需要通过pipefd[0] pipefd[1]来记录其读写端的文件描述符,然后在父子进程中关闭对应的文件描述符,达到单方向通信的需求!
在这里插入图片描述
根据匿名管道的底层,两个毫不相干的进程就无法通过匿名管道的方式来进行通信

那么两个毫不相干的进程如何才能看的同一片内存,才能共享一个文件缓冲区呢?当然就通过文件的路径(唯一性)来打开!

当两个进程打开同一个文件时,他们共享该文件的内核缓冲区。为了我们的通信效率,肯定不能把缓冲区的数据刷新到硬盘中。所以这个文件必须是一个特殊的文件,只用于通信的需求!!!

这个文件就是命名管道!!!

2 代码实现

2.1 系统调用

匿名管道的创建是通过系统调用:pipe(int pipefd[2]) 来建立,同样命名管道的创建也有对应的指令:mkfifo

MKFIFO(1)                                                                 User Commands                                                                MKFIFO(1)

NAME
       mkfifo - make FIFOs (named pipes)

SYNOPSIS
       mkfifo [OPTION]... NAME...

DESCRIPTION
       Create named pipes (FIFOs) with the given NAMEs.

       Mandatory arguments to long options are mandatory for short options too.

       -m, --mode=MODE
              set file permission bits to MODE, not a=rw - umask

       -Z     set the SELinux security context to default type

       --context[=CTX]
              like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX

       --help display this help and exit

       --version
              output version information and exit

我们使用一下来看看:
在这里插入图片描述
这个文件类型是p不同于-(普通文件)和d(目录),p表示管道文件,显然它是有名字的!
我们来尝试通信一下:
在这里插入图片描述
此时两个不同的进程就可以进行通信!!!
我们在让两个进程保持一直通信的状态,这样读端可以一直获取数据!
在这里插入图片描述

当我们突然关闭右侧读端时,左边的写端就直接退出来了!这是因为当读端退出了,操作系统会自动释放写端进程,操作系统不会做无用功(不会在一个没有读取的管道文件了一直写入)

当然这样的通信也就只能用来演示,我们先要通过命名管道来使我们创建的两个毫不相干的进程完成通信工作,接下来我们就来实现!

2.2 命名管道的封装

首先我们来认识一下创建管道的系统调用:

MKFIFO(3)                                                           Linux Programmer's Manual                                                          MKFIFO(3)

NAME
       mkfifo, mkfifoat - make a FIFO special file (a named pipe)

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>

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

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <sys/stat.h>

       int mkfifoat(int dirfd, const char *pathname, mode_t mode);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       mkfifoat():
           Since glibc 2.10:
               _POSIX_C_SOURCE >= 200809L
           Before glibc 2.10:
               _ATFILE_SOURCE

int mkfifo(const char *pathname, mode_t mode);
第一个参数const char *pathname 是要建立的管道文件的路径与文件名,第二个参数mode_t mode 表示文件权限

返回值的意义:

ETURN VALUE
On success mkfifo() and mkfifoat() return 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).
创建成功返回 0 失败返回 -1!

通过这些我可以先搭建一个基础类,可以创建管道文件!

  • 成员

    1. 管道文件名 const std::string _fifo_path
    2. 文件描述符 _fd 默认为-1
    3. 操作者类型 _id 1 /2
  • 构造函数 --> 创建管道 CreateNamePipe(const std::string &path )
    使用函数 int mkfifo(const char *pathname, mode_t mode); 文件名为 path(需要确定下来 保证引用该头文件的都可以获取) 权限为0666
    为 0 正常建立 , 为 -1 建立失败 测试创建是否成功

  • 析构函数 --> 删除管道 RemoveNamedPipe(const std::string &path )
    使用unlink(path.c_str()) 删除管道

#pragma once

#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstdio>
#include <unistd.h>
//文件名
const std::string path = "./myfifo";
//管道类
class NamedPipe
{
public:
    NamedPipe(const std::string fifo_path)
        : _fifo_path(fifo_path)
    {
        int n = mkfifo(_fifo_path.c_str(), 0666);
        if(n != 0)
        {
            perror("mkfifo");
        }

    }
    ~NamedPipe()
    {
        sleep(10);
        unlink(_fifo_path.c_str());
    }

private:
    const std::string _fifo_path;
    int _fd;
    int _id;
};

这样就可以通过类的实例化对象来建立管道!!!

接下来我们进行打开文件函数的书写:

  1. 首先,命名管道是文件,打开文件需要open接口,管理管道由操作者来控制。使用者只能使用不能管理管道的创建与关闭

  2. 表明身份的宏定义:----- 权限不同

    • greater 1 创建者 :只有创建者才可以建立删除管道
    • user 2 使用者 :只需要初始化其管道,不需要再建立
  3. 打开管道 OpenForRead / OpenForWrite —> OpenNamedPipe()
    打开管道的本质就是打开文件 _fd = open()
    注意打开的方式与操作者的类型紧密相关 所以进行飞封装!通过内部传参来保证权限的正确

private:
    bool OpenNamedPipe(int mode)
    {
        //sleep(2);
        std::cout << "OpenNamedPipe : " ;
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
        {
            std::cout << "false" << std::endl;
            return false;
        }
        std::cout << "true" << std::endl;
        return true;
    }
public:
// 打开文件
    int OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    int OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }

打开文件之后就是进行读取或者写入,我们在写一下相应的函数:

  • 读取 ReadNamedPipe(std::string *out)
    设置缓冲区
    从管道里读取 向缓冲区写入数据
    命名管道对于读端而言 , 如果我们打开文件,但是写端还没有,就会阻塞在open调用中,等待写端进入

  • 写入 WriteNamedPipe(const std::string& in)
    向文件描述符里面进行写入

 	// 读取文件
    int ReadNamedPipe(std::string *out)
    {
        char buffer[128];
        int n = read(_fd, buffer, sizeof(buffer));
        buffer[n] = 0;
        *out = buffer;
        return n;
    }
    // 写入文件
    int WriteNamedPipe(const std::string &in)
    {
        int n = write(_fd, in.c_str(), in.size());

        return n;
    }

这样我们的封装就完成了,NamedPipe具有以下功能:

  1. 通过文件路径和操作者权限建立实例化对象
  2. 按照需求调用:OpenForRead() / OpenForWrite()打开文件
  3. 进行写入和读取WriteNamedPipe / ReadNamedPipe

2.3 开始使用

模拟客户端和服务器的通信过程:客户端写入数据,服务器读取数据

client.cc
#include"namedPipe.hpp"

int main()
{
    NamedPipe fifo(path , user);
    if(fifo.OpenForWrite())
    {
        std::cout << "client open named pipe done" << std::endl;
        while(true)
        {
            std::cout << "Please Enter>" ;
            std::string in ;
            getline(std::cin , in);
            fifo.WriteNamedPipe(in);
        }
    }
    return 0;
}
server.cc
#include"namedPipe.hpp"

int main()
{
    NamedPipe fifo(path , greater);
    //服务端进行读取
    if(fifo.OpenForRead())
    {
        std::cout << "server open named pipe done" << std::endl;
        sleep(3);
        while(true)
        {
            std::string out ;
            int n = fifo.ReadNamedPipe(&out);
            if(n > 0)
            {
                std::cout << "client say >" << out << std::endl;
            }
            else if (n == 0)
            {
                std::cout << "client quit , server too!" << out << std::endl;
                break;
            }
            else
            {
                perror("Write");
                break;
            }
        }
    }

    return 0;
}
来看效果:

在这里插入图片描述
这个效果很好玩呢!!!

注意:

  1. 对于读端来说,如果我们打开文件,但是写端还没有进入,那么就会阻塞在open()调用中!直到写端打开—进程同步!!!
  2. 当读端退出时,写端再次写入数据时会直接退出!操作系统不会做无用功!!!(直接把broken pipe坏的管道 进行杀掉!会发送对应的13号信号SIGPIPE
  3. 管道别写满 && read fd 不读且没有关闭 :
    管道被写满,写进程会被阻塞,写条件不具备-- wait 等待条件具备(读取走一部分数据才能继续写)
  4. 如果管道内部是空的 && write fd没有关闭:
    读取条件不具备,读取进程会被阻塞 – wait 等待条件具备(写入了数据)

3回归概念

总结一下,命名管道的通信原理依然是:让两个不同的进程看到同一份资源(通过文件路径)

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

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

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

相关文章

ROS2从入门到精通2-1:launch多节点启动与脚本配置

目录 0 专栏介绍1 ROS2的启动脚本优化2 ROS2多节点启动案例2.1 C架构2.2 Python架构 3 其他格式的启动文件3.1 .yaml启动3.2 .xml启动 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实际项目的…

博客星球大冒险:用Spring Boot和JWT打造你的数字王国

揭秘如何在Spring Boot中无缝集成JWT&#xff0c;为你的应用打造一个高度可扩展且安全的认证系统。从添加依赖到创建JWT过滤器&#xff0c;再到实现令牌的有效性管理和刷新机制&#xff0c;每一步都精心设计&#xff0c;确保你的乐园能够迎接成千上万的游客&#xff01; 文章目…

AltiumDesigner/AD添加数据库连接

1.首先确保本机电脑有无对应的数据库驱动&#xff0c;例如我这边要添加MySQL的数据&#xff0c;则需要首先下载MySQL数据驱动&#xff1a;MySQL :: Download MySQL Connector/ODBC (Archived Versions) 2.运行“odbcad32.exe”&#xff0c;如下图添加对应的数据库配置&#xf…

基于深度学习的模糊认知图方法

1 文章信息 文章题目为“Deep Fuzzy Cognitive Maps for Interpretable Multivariate Time Series Prediction”&#xff0c;该文于2019年发表于“IEEE TRANSACTIONS ON FUZZY SYSTEMS”。文章提出了深度模糊认知图&#xff08;FCM&#xff09;用于多变量时间序列预测&#xff…

一个良好的嵌入式系统框架(基于FreeRTOS)

目录 Unix操作系统里的优先级嵌入式系统里的优先级 Unix操作系统里的优先级 在《Unix传奇》中有这样一句话&#xff0c;用户态的进程/线程是三等公民、root线程是二等公民、硬件中断是一等公民。 在操作系统中&#xff0c;"用户态"和"内核态"是两种不同的…

BERT模型学习(1)

BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;由谷歌在2018年推出&#xff0c;迅速成为自然语言处理&#xff08;NLP&#xff09;领域的一个突破性成果。 基本概念 在深入了解BERT之前&#xff0c;需要先简单了解一下自然语言处理&a…

五分钟“手撕”链表

为了提高大家的学习效率&#xff0c;我把代码放开头&#xff0c;供查阅。 目录 一、链表的实现代码 二、什么是链表 三、链表的分类 四、链表的常见操作 插入 删除 五、Java自带的LinkedList 两个构造方法 一些常用方法 六、LinkedList的遍历 七、ArrayList和Linke…

达梦数据库写文件的方式探索

0x01 前沿 这篇文章整体算是《达梦数据库手工注入笔记》的续集&#xff0c;达梦作为国内优秀的信创数据库&#xff0c;在关基单位中拥有越来越大的用户使用量。 通过SQL注入来写文件一直以来都是SQL注入漏洞深入利用的一种方式&#xff0c;对于不同的数据库通常写文件的方式也是…

探索无限可能性——微软 Visio 2021 改变您的思维方式

在当今信息化时代&#xff0c;信息流动和数据处理已经成为各行各业的关键。微软 Visio 2021 作为领先的流程图和图表软件&#xff0c;帮助用户以直观、动态的方式呈现信息和数据&#xff0c;从而提高工作效率&#xff0c;优化业务流程。本文将介绍 Visio 2021 的特色功能及其在…

【管理咨询宝藏119】翰威特组织架构设计优化方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏119】翰威特组织架构设计优化方案 【格式】PDF版本 【关键词】人力资源、组织设计、组织架构 【核心观点】 - 城镇化建设和居民可支配收入的增长…

Python实现定时任务的方式

大家好&#xff0c;在当今数字化的时代&#xff0c;定时任务的需求在各种应用场景中频繁出现。无论是数据的定时更新、周期性的任务执行&#xff0c;还是特定时间点的操作触发&#xff0c;Python 都为我们提供了强大而灵活的手段来实现这些定时任务。当我们深入探索 Python 的世…

代理 模式

一、什么是代理模式 代理模式指代理控制对其他对象的访问&#xff0c;也就是代理对象控制对原对象的引⽤。在某些情况下&#xff0c;⼀个对象不适合或者不能直接被引⽤访问&#xff0c;⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 二、为什么使用代理模式 模式作…

MySQL各种锁

目录 1. 从粒度上区分锁 1.1 全局锁&#xff08;第一粒度&#xff09; 1.2 表级锁&#xff08;第二粒度&#xff09; 1.3 行锁&#xff08;第三最小粒度&#xff09; 2 从模式上区分锁 2.1 什么是乐观锁 2.2 什么是悲观锁 2.3 意向共享锁和意向排他锁 2.4 临键锁和记录…

【Python】 深入理解Python中的UnicodeDecodeError及其解决方案

基本原理 在Python编程中&#xff0c;我们经常需要处理各种类型的数据&#xff0c;尤其是文本数据。文本数据在计算机中通常以字节的形式存在&#xff0c;而字节需要被解码成我们能够理解的字符。这个过程涉及到编码和解码的概念。 编码是将字符转换为字节的过程&#xff0c;…

23 vue3面试重难点复习:响应式原理、特点、8大生命钩子、data数据定义、组件、全家桶

vue作为用的最为广泛的当前热门框架&#xff0c;总结如下重难点核心知识&#xff1a; 1.vue特点是什么&#xff1f; 1.1优点 渐进式 vue本身只提供数据响应式&#xff0c;需要全局缓存用 vuex&#xff0c;需要路由用 vue-router 组件化 封装组件&#xff0c;利于复用 响应式数…

k8s——Pod进阶(资源限制和探针)

一、资源限制 1.1 资源限制的定义 当定义Pod时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是CPU和内存大小&#xff0c;以及其他类型的资源。 当为Pod中的容器指定了request资源时&#xff0c;调度器就使用该信息来决定将Pod调度到哪个节点上。当还为容器…

汇凯金业:量化交易有风险吗

量化交易是一种通过复杂的数学模型和算法在金融市场中进行高频和自动化交易的方式。尽管量化交易在提高市场效率、减少人为错误等方面具有诸多优点&#xff0c;但它也同样存在着不少风险。以下列举了一些主要的风险因素&#xff1a; 1. 模型风险 模型缺陷&#xff1a;量化交易…

网络协议。

一、流程案例 接下来揭秘我要说的大事情&#xff0c;“双十一”。这和我们要讲的网络协议有什么关系呢&#xff1f; 在经济学领域&#xff0c;有个伦纳德里德&#xff08;Leonard E. Read&#xff09;创作的《铅笔的故事》。这个故事通过一个铅笔的诞生过程&#xff0c;来讲述…

数据安全之翼:天空卫士在汽车数据安全领域的卓越领航

近期&#xff0c;中国汽车网络安全与数据安全产业的积极倡导者谈思实验室发布首份《汽车网络与数据安全行业全景图》&#xff0c;天空卫士入选&#xff0c;并且位列榜首。 天空卫士在汽车数据安全领域有丰富的实践经验&#xff0c;曾为多家汽车行业用户提供数据安全产品与服务&…

LeetCode - 贪心(Greedy)算法集合(Python)[分配问题|区间问题]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139242199 贪心算法&#xff0c;是在每一步选择中&#xff0c;都采取当前状态下&#xff0c;最好或最优&#xff08;即最有利&#xff09;的选择&…