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

Linux - 进程间通信(3)

目录

3、解决遗留BUG -- 边关闭信道边回收进程

1)解决方案

2)两种方法相比较

4、命名管道

1)理解命名管道

2)创建命名管道

a. 命令行指令

b. 系统调用方法

3)代码实现命名管道

构建类进行封装命名管道:

构造和析构:

读取管道、写入管道:

server.cc (读端):

client.cc(写端):

效果:

4)疑点解决

写端未来,读端open调用阻塞

读端关闭,写端继续写入

5)完整代码

namedPipe.hpp:

server.cc:

client.cc:


3、解决遗留BUG -- 边关闭信道边回收进程

1)解决方案

我们仍然想用上述方法进行管道和子进程的回收

-- 则需要解决子进程所继承的父进程遗留的多余wfd,我们在每次创建子进程时,遍历所有之前的信道,关闭掉wfd即可,就不会出现,多个wfd指向一个管道

2)两种方法相比较

退一个回收一个:

先全部退出,再进行等待回收:

4、命名管道

1)理解命名管道

命名:该管道有名字,因为该文件有路径,有路径必有文件名

管道:依旧是一个内存级的基于文件进行通信的通信方案

属性、操作、文件内核缓冲区同一个文件的都是差不多的,因此不用再创建一份,操作系统不做浪费时间和空间的事情

我们怎么保证两个毫不相关的进程打开了同一个文件呢??
每一个文件,都有文件路径(唯一性)

2)创建命名管道

a. 命令行指令

一个进程(echo)向命名管道里面输入数据

一个进程(cat)向命名管道里面读取数据

这样就实现了两个毫不相关的进行之间的通信

创建了三个窗口,一个一直向管道输入,一个一直读取,一个手动检测管道大小

但是我们可以看到管道文件(myfifo)的大小一直显示0

因为 FIFO0 文件虽存在于文件系统中,但其内容都存放在内存里,不会将通信数据刷新到磁盘中,所以在磁盘上显示的文件大小始终为0

b. 系统调用方法

使用mkfifo即可创建管道文件

使用unlink即可删除一个管道文件(当然,rm也可以删除)

3)代码实现命名管道

创建两个.cc文件分别模拟两个进程,一个进行发送,一个进行读取

通过一个CreateNamedPipe和一个RemoveNamedPipe就可以实现对管道生命周期的管理

当然,我们管理管道的声明周期时,肯定是将创建和删除交给同一个文件去做比较好,因为它清楚什么时候去删除合适

这里我们让发送的那方去管理管道的生命周期

构建类进行封装命名管道:

我们需要创建管道的路径(共同路径)、创建管道的身份、管道的文件描述符

class NamedPipe
{
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};
构造和析构:
class NamedPipe
{
public:
    NamedPipe(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }

    ~NamedPipe()
    {
        sleep(5);

        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater remove named pipe" << std::endl;
        }
        if(_fd != DefaultFd) close(_fd);
    }

private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};
读取管道、写入管道:
class NamedPipe
{
private:
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if(_fd < 0) return false;
        return true;
    }

public:
    NamedPipe(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }

    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }

    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }

    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0; // '\0'
            *out = buffer;
        }
        return n;
    }

    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size());
    }

    ~NamedPipe()
    {
        sleep(5);

        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater remove named pipe" << std::endl;
        }
        if(_fd != DefaultFd) close(_fd);
    }

private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};
server.cc (读端):
#include "namedPipe.hpp"

// server -- read : 管理命名管道的整个生命周期
int main()
{
    NamedPipe fifo(comm_path, Creater);

    // 对于读端而言,如果我们打开了文件,但是写还没有来,我们会阻塞在open调中,直到对方打开
    // --> 一种变向的进程同步
    if (fifo.OpenForRead())
    {
        std::cout << "Server open named pipe done" << std::endl; // 为了检测阻塞

        sleep(3);
        while (true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if (n > 0) // 正常接收
            {
                std::cout << "Client Say > " << message << std::endl;
            }
            else if(n == 0) // 即写端关闭
            {
                std::cout << "Client quit, Server too!" << std::endl;
                break;
            }
            else 
            {
                std::cout << "fifo.ReadNamedPipe Error!" << std::endl;
                break;
            }
        }
    }

    return 0;
}
client.cc(写端):
#include "namedPipe.hpp"

// client -- write
int main()
{
    NamedPipe fifo(comm_path, User); // 以非创建身份实例化

    if(fifo.OpenForWrite())
    {
        std::cout << "client open named pipe done" << std::endl;
        
        while(true)
        {
            std::cout << "Please Enter > ";
            std::string message;
            std::getline(std::cin, message);
            fifo.WriteNamedPipe(message);
        }
    }

    return 0;
}
效果:

实现了两个进程(无父子关系)之间的通信

4)疑点解决

写端未来,读端open调用阻塞

读端关闭,写端继续写入

5)完整代码

namedPipe.hpp:
#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string comm_path = "./myfifo";

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

class NamedPipe
{
private:
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if(_fd < 0) return false;
        return true;
    }

public:
    NamedPipe(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }

    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }

    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }

    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0; // '\0'
            *out = buffer;
        }
        return n;
    }

    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size());
    }

    ~NamedPipe()
    {
        sleep(5);

        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater remove named pipe" << std::endl;
        }
        if(_fd != DefaultFd) close(_fd);
    }

private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};
server.cc:
#include "namedPipe.hpp"

// server -- read : 管理命名管道的整个生命周期
int main()
{
    NamedPipe fifo(comm_path, Creater);

    // 对于读端而言,如果我们打开了文件,但是写还没有来,我们会阻塞在open调中,直到对方打开
    // --> 一种变向的进程同步
    if (fifo.OpenForRead())
    {
        std::cout << "Server open named pipe done" << std::endl;

        sleep(3);
        while (true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if (n > 0)
            {
                std::cout << "Client Say > " << message << std::endl;
            }
            else if(n == 0)
            {
                std::cout << "Client quit, Server too!" << std::endl;
                break;
            }
            else
            {
                std::cout << "fifo.ReadNamedPipe Error!" << std::endl;
                break;
            }
        }
    }

    return 0;
}
client.cc:
#include "namedPipe.hpp"

// client -- write
int main()
{
    NamedPipe fifo(comm_path, User);

    if(fifo.OpenForWrite())
    {
        std::cout << "client open named pipe done" << std::endl;
        
        while(true)
        {
            std::cout << "Please Enter > ";
            std::string message;
            std::getline(std::cin, message);
            fifo.WriteNamedPipe(message);
        }
    }

    return 0;
}

http://www.kler.cn/a/529939.html

相关文章:

  • 力扣第149场双周赛
  • 13 尺寸结构模块(size.rs)
  • 【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)
  • C++【深入底层,手撕vector】
  • UbuntuWindows双系统安装
  • AIGC技术中常提到的 “嵌入转换到同一个向量空间中”该如何理解
  • IOC三种实现方式的区别
  • Brooks MX Marathon Expressrm User Manual MX集成系统平台
  • 建表注意事项(2):表约束,主键自增,序列[oracle]
  • Lesson 127 A famous actress
  • 一维数组0-1背包问题理论基础
  • w189电商平台的设计与实现
  • 尝试ai生成figma设计
  • 系统URL整合系列视频二(界面原型)
  • 独立开发经验谈:如何借助 AI 辅助产品 UI 设计
  • C++中的拷贝构造器(Copy Constructor)
  • 稀疏进化训练:机器学习优化算法中的高效解决方案
  • 蓝桥杯备考:枚举算法之铺地毯
  • R语言绘制有向无环图(DAG)
  • 普通用户(非root) 安装libreoffice
  • Python的那些事第九篇:从单继承到多继承的奇妙之旅
  • 【leetcode详解】T598 区间加法
  • 手机Python爬虫教程:利用手机学习Python爬虫的终极指南_python可以在手机上写爬虫吗
  • 人机交互系统实验三 多通道用户界面
  • C++模板编程——可变参函数模板之折叠表达式
  • 使用 DeepSeek-R1 与 AnythingLLM 搭建本地知识库