当前位置: 首页 > news >正文

进程间通信 命名管道 ─── Linux第24课

匿名管道利用文件的内核级缓冲区,实现了父子进程之间的通信

而非父子进程的通信是什么样的? 命名管道

其实匿名管道与命名管道的本质是一样的(文件)

目录

命名管道

创建⼀个命名管道

关闭创建的命名管道

匿名管道与命名管道的相同点

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

命名管道的打开规则

实现两个非父子进程之间的命名管道通信

Common.hpp

Sever.hpp

Sever.cc

Client.hpp

Client.cc

makefile

效果:


命名管道

  • 匿名管道应用的⼀个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项⼯作,它经常被称为命名管道。
  • 命名管道是⼀种特殊类型的文件,有名字(与匿名管道不同),不与磁盘(外设)进行数据来往,只让进程互相通信。
  • 命名管道本质: 两个非父子进程基于命名管道的内核级缓冲区进行数据交换。

创建⼀个命名管道

命名管道可以从命令行上创建,命令行方法是使用下⾯这个命令:

mkfifo  filename

示例:

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

 int  mkfifo(const char *pathname , mode_t mode);
int mkfifo_ret =mkfifo( "./MyFifo" , 0600);
if(mkfifo_ret < 0) 
{
     std::cerr<<"mkfile error"<<std::endl;
}

命名文件本质是文件 ,进程中创建可以不带路径只带filename (就是在当前路径下创建),mode_t 就是创建后文件的权限

        命名文件本质是文件 ,进程中创建可以不带路径只带filename (就是在当前路径下创建),mode_t 就是创建后文件的权限

关闭创建的命名管道

        unlink命令

示例:

匿名管道与命名管道的相同点

  • 在匿名管道和命名管道中 ,管道文件的inode和管道内核级缓冲区只存在一份 (Linux文件系统保证文件的数据和属性的唯一性)
  • 他们都只使用文件的内核级缓冲区,不向磁盘(外设)刷新

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

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

命名管道的打开规则

1.如果当前打开操作是为读⽽打开FIFO时 

  •  O_NONBLOCK disable:阻塞直到有相应进程为写⽽打开该FIFO(在open处阻塞)
  •  O_NONBLOCK enable:⽴刻返回成功

2.如果当前打开操作是为写⽽打开FIFO时

  • O_NONBLOCK disable:阻塞直到有相应进程为读⽽打开该FIFO (在open处阻塞)
  • O_NONBLOCK enable:⽴刻返回失败,错误码为ENXIO

实现两个非父子进程之间的命名管道通信

思路:程序server创建命名管道 ,使用命名管道(以读的方式打开命名管道 ,读取命名管道的数据)

           程序client 使用命名管道(以写的方式打开管道 ,向命名管道中写入数据)

           两个程序都知道的信息包含在头文件common.hpp中

Common.hpp

#pragma once
#include<iostream>
#include<string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

const std::string gPipeFile ="./fifo";
const int gmode =0600;
const int gDefaultfd =-1;//初始是-1 成功打开管道后改变
const int gsize =1024;

Sever.hpp

#pragma once
#include"Common.hpp"

class CreatInit
{
public:
    CreatInit()
    {
        umask(0);
        int mkfifo_ret =mkfifo(gPipeFile.c_str() ,gmode);
        if(mkfifo_ret < 0) 
        {
            std::cerr<<"mkfile error"<<std::endl;
        }
    }

    ~CreatInit()
    {
        int unlink_ret =unlink(gPipeFile.c_str());
        if(unlink_ret < 0) 
        {
            std::cerr<<"unlink_ret error"<<std::endl;
        }
    }
private:

};

//建立命名管道
CreatInit CI;


class server
{
public:

    server():_fd(gDefaultfd)
    {}

    ~server()
    {}

    bool OpenPipeForRead()
    {
        // 如果读端打开文件时,写端还没打开,读端对用的open就会阻塞
        _fd =open(gPipeFile.c_str() , O_RDONLY);
        if(_fd < 0) return false;
        return true;

    }

    // std::string *: 输出型参数
    // const std::string &: 输入型参数
    // std::string &: 输入输出型参数
    int ReceviePipe(std::string *out)
    {
        char buf[gsize];
        while(true)
        {
            ssize_t n =read(_fd ,buf ,sizeof(buf)-1);
            if(n > 0)
            {
                buf[n]=0;
                *out =buf;
            }
            return n;
        }
    }
    bool ClosePipe()
    {
        int n =::close(_fd);
        if(n< 0) return false;
        return true;
    }
private:
    int _fd;
};

Sever.cc

#include<iostream>
#include"Server.hpp"
//建立命名管道,以读的方式打开

int main()
{
    server s;
    if(!s.OpenPipeForRead()) 
    {
        std::cerr<<"server openPipe false"<<std::endl;
    }
    std::string message;
    while(true)
    {
        int  n = s.ReceviePipe(&message);
        if(n == 0)
        {
           break;//管道特性返回值为零(读到0)就退出
        }
        else if(n > 0)
        {
            std::cout<<"client Say# "<< message <<std::endl;
        }
        else
        {
            std::cerr<<"sever receice false"<<std::endl;
            return 1;
        }
    }

    std::cout << "client quit, me too!" << std::endl;
    s.ClosePipe();

    return 0;
} 

Client.hpp

#pragma once
#include"Common.hpp"

class client
{
public:

    client():_fd(gDefaultfd)
    {}

    ~client()
    {}

    bool OpenPipeForWrite()
    {
        _fd =open(gPipeFile.c_str() , O_WRONLY);
        if(_fd < 0) return false;
        return true;
    }

    // std::string *: 输出型参数
    // const std::string &: 输入型参数
    // std::string &: 输入输出型参数
    int SendPipe(const std::string& in)
    {
        ssize_t n =::write(_fd ,in.c_str(),in.size());
        //&in sizeof(in)
        if(n > 0) return n;
        else
        {
            std::cerr<<"write error"<<std::endl;
            return -1;
        }
    }
    bool ClosePipe()
    {
        int n =::close(_fd);
        if(n < 0) return false;
        return true;
    }
private:
    int _fd;
};

Client.cc

#include<iostream>
#include"Client.hpp"
int main()
{
    client c;
    if(!c.OpenPipeForWrite())
    {
        std::cerr<<"client openPipe error"<<std::endl;
    }
    std::string message;
    while(true)
    {
        std::getline(std::cin,message);
        c.SendPipe(message);

    }
    c.ClosePipe();
    return 0;
}

makefile

SERVER=server
CLIENT=client
cc=g++
SERVER_SRC=Server.cc
CLIENT_SRC=Client.cc

.PHONY:all
all:$(SERVER) $(CLIENT)

$(SERVER):$(SERVER_SRC)
	$(cc) -o $@ $^  -std=c++11

$(CLIENT):$(CLIENT_SRC)
	$(cc) -o $@ $^ -std=c++11


.PHONY:clean
clean:
	rm -f $(SERVER) $(CLIENT)

效果:

相关文章:

  • 同旺科技USB to SPI 适配器 ---- 指令之间延时功能
  • SpringBoot分布式项目实战:观察者模式的高阶应用与避坑指南
  • JWT应用样例
  • 【Android】Activity 生命周期(详细介绍)
  • Mac 常用命令
  • 《Git江湖录·分支篇》
  • 二叉树进阶
  • 【leetcode刷题日记】lc.560-和为 K 的子数组
  • 深入解析 JVM 内存区域及核心概念
  • 掌握Linux项目自动化构建:从零入门make与Makefile
  • 基于LLM的Agent框架全面比较分析:MGX(MetaGPT X)、AutoGen、OpenHands与秒哒(MiaoDa)
  • [C++面试] span<char>和string_view的差别
  • MySQL数据库入门
  • 【JavaScript】金丹期功法
  • LLM动态Shape实现原理与核心技术
  • 【银河麒麟系统常识】命令:dotnet run(运行)
  • 远程医疗的现状如何?
  • C++ 之 SOCKET 通信详解
  • “自动驾驶背后的数学” 专栏导读
  • 推陈换新系列————java8新特性(编程语言的文艺复兴)
  • 高瓴、景林旗下公司美股持仓揭晓:双双增持中概股
  • 长三角体育节回归“上海时间”,首次发布赛事旅游推荐线路
  • 六省会共建交通枢纽集群,中部离经济“第五极”有多远?
  • 临港新片区将新设5亿元启航基金:专门投向在临港发展的种子期、初创型企业
  • 体坛联播|巴萨提前2轮西甲夺冠,郑钦文不敌高芙止步4强
  • 吉利汽车一季度净利润大增264%,称整合极氪后实现整体效益超5%