Linux - 进程间通信(3)

news/2025/2/3 5:46:32 标签: linux, 运维, 服务器

目录

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.niftyadmin.cn/n/5840508.html

相关文章

MATLAB中open函数用法

目录 语法 说明 示例 打开文件 打开不在路径中的文件 创建处理扩展名的函数 open函数的功能是在合适的应用程序中打开文件。 语法 open name A open(name) 说明 open name 在适当的应用程序中打开指定的文件或变量。 可以通过 openxxx 的形式&#xff08;其中 xxx 为…

解决国内服务器 npm install 卡住的问题

在使用国内云服务器时&#xff0c;经常会遇到 npm install 命令执行卡住的情况。本文将分享一个典型案例以及常见的解决方案。 问题描述 在执行以下命令时&#xff1a; mkdir test-npm cd test-npm npm init -y npm install lodash --verbose安装过程会卡在这个状态&#xf…

Flutter Raw Image Provider

一般情况下&#xff0c;考虑网络传输效率&#xff0c;会采用算法来压缩这个数据&#xff0c;故而你会看到有各种各样的图像压缩算法和文件格式。 你可能会问什么情况下会有需要直接去加载一张图的原始rgba数据&#xff1f; 这里举个简单例子&#xff1a;分块加载图片。将图片…

【华为OD-E卷 - 磁盘容量排序 100分(python、java、c++、js、c)】

【华为OD-E卷 - 磁盘容量排序 100分&#xff08;python、java、c、js、c&#xff09;】 题目 磁盘的容量单位常用的有M&#xff0c;G&#xff0c;T这三个等级&#xff0c; 它们之间的换算关系为1T 1024G&#xff0c;1G 1024M&#xff0c; 现在给定n块磁盘的容量&#xff0c…

UE5 蓝图学习计划 - Day 11:材质与特效

在游戏开发中&#xff0c;材质&#xff08;Material&#xff09;与特效&#xff08;VFX&#xff09; 是提升视觉体验的关键元素。Unreal Engine 5 提供了强大的 材质系统 和 粒子系统&#xff08;Niagara&#xff09;&#xff0c;让开发者可以通过蓝图控制 动态材质、光效变化、…

算法随笔_36: 复写零

上一篇:算法随笔_35: 每日温度-CSDN博客 题目描述如下: 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改…

UE5 蓝图学习计划 - Day 9:数组与跨蓝图通信

在游戏开发中&#xff0c;数据存储与传递 是构建复杂系统的重要基础。UE5 蓝图提供了 数组&#xff08;Array&#xff09; 来存储多个数据项&#xff0c;并允许 跨蓝图通信&#xff08;Blueprint Communication&#xff09; 让不同的蓝图共享和传递数据。本篇将学习如何使用数组…

Resnet 改进:尝试在不同位置加入Transform模块

目录 1. TransformerBlock 2. resnet 3. 替换部分卷积层 4. 在特定位置插入Transformer模块 5. 使用Transformer全局特征提取器 6. 其他 Tips:融入模块后的网络经过测试,可以直接使用,设置好输入和输出的图片维度即可 1. TransformerBlock TransformerBlock是Transfo…