进程间通信2(命名管道)linux

1 命名管道

前面讲到匿名管道,有一个很大的限制,那就是只有具有相同祖先(具有亲缘关系)的进程间才能进行通信,但是如果想实现不同进程间的通信,这个时候命名管道就发挥着巨大作用。

命名管道是一种特殊类型的文件,和匿名管道不一样的是这个命名管道是有文件名的,有路径的,有对应的inode,只是不用向磁盘中进行IO。

多个进程是否可以打开同一个文件呢?这个问题在前面讲文件描述符时,就已经知道是可以的,例如:显示器文件,多个进程都可以打开,并向显示器写入。

1.1 创建命名管道

命令行创建:

bash 复制代码
mkfifo filename

系统调用创建:

cpp 复制代码
int mkfifo(const char *filename,mode_t mode);

参数为:创建管道的文件名,权限。

1.2 匿名管道和命名管道使用不同

匿名管道由pipe函数创建并打开

命名管道由mkfifo函数创建,打开用open

FIFO(命名管道)与pipe(匿名管道)之间唯一区别在它们创建与打开的方式不同,⼀但这些工作完成之后,它们具有相同的语义。

2 用命名管道实现server与client之间的通信

读写端共同包含的部分:

common.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>


const std::string filename="fifo";
const int mode=0666;

const int size=1024;

权限,默认文件名。

namepipe类

默认构造函数

cpp 复制代码
namepipe(const std::string &filename) : _filename(filename),_fd(defaultfd)
    {}

成员_fd表示文件描述符,所以要得等到,打开文件时再确认,这里默认为-1。

初始化创建管道文件

cpp 复制代码
bool InitNamepipe()
    {
        int n = mkfifo(filename.c_str(), mode);
        if (n == 0)
        {
            std::cout << "mkfifo success " << std::endl;
        }
        else
        {
            perror("mkfifo");
            return false;
        }
        return true;
    }

利用mkfifo函数创建一个管道。

这里要注意一个从管道中写入数据,一个向管道中读取数据,所以就要有两种打开方式:

也就是在open函数设置不同的权限

读打开

cpp 复制代码
bool OpenForRead()
    {
        _fd = open(filename.c_str(), O_RDONLY);
        if (_fd < 0)
        {
            perror("open");
            return false;
        }
        else
        {
            std::cout << "open success: " << _fd << std::endl;
        }
        return true;
    }

写打开

cpp 复制代码
 bool OpenForWrite()
    {
        _fd = open(filename.c_str(), O_WRONLY);
        if (_fd < 0)
        {
            perror("open");
            return false;
        }
        else
        {
            std::cout << "open success: " << _fd << std::endl;
        }
        return true;
    }

读数据

cpp 复制代码
 void Write(const std::string &in)
    {
        write(_fd, in.c_str(), in.size());
    }

写数据

cpp 复制代码
bool Read(std::string *out)
    {
        char buffer[size] = {0};
        ssize_t num = read(_fd, buffer, sizeof(buffer) - 1);
        if (num > 0)
        {
            buffer[num] = 0;
            *out = buffer;
        }
        else if (num == 0)
        {
            std::cout << "write out,me too" << std::endl;
            return false;
        }
        else
        {
            return false;
        }
        return true;
    }

读取数据失败或写端关闭返回false,这里out是一个输出型参数,将读出的数据通过out带出。

这里read也是默认阻塞等待写端写入。

回收资源

关闭文件描述符:

cpp 复制代码
void Close()
    {
        if(_fd==defaultfd)
            return;
        else
            close(_fd);
    }

防止没有打开文件,就直接close,所以这里有一个defaultfd的操作。

删除管道文件:

cpp 复制代码
void Remove()
    {
        int m=unlink(_filename.c_str());
        (void)m;
    }

namepipe.hpp

cpp 复制代码
#pragma once
#include "common.hpp"

const int defaultfd = -1;

class namepipe
{
public:
    namepipe(const std::string &filename) : _filename(filename),_fd(defaultfd)
    {}
    bool InitNamepipe()
    {
        int n = mkfifo(filename.c_str(), mode);
        if (n == 0)
        {
            std::cout << "mkfifo success " << std::endl;
        }
        else
        {
            perror("mkfifo");
            return false;
        }
        return true;
    }

    bool OpenForRead()
    {
        _fd = open(filename.c_str(), O_RDONLY);
        if (_fd < 0)
        {
            perror("open");
            return false;
        }
        else
        {
            std::cout << "open success: " << _fd << std::endl;
        }
        return true;
    }

    bool OpenForWrite()
    {
        _fd = open(filename.c_str(), O_WRONLY);
        if (_fd < 0)
        {
            perror("open");
            return false;
        }
        else
        {
            std::cout << "open success: " << _fd << std::endl;
        }
        return true;
    }

    void Write(const std::string &in)
    {
        write(_fd, in.c_str(), in.size());
    }


    bool Read(std::string *out)
    {
        char buffer[size] = {0};
        ssize_t num = read(_fd, buffer, sizeof(buffer) - 1);
        if (num > 0)
        {
            buffer[num] = 0;
            *out = buffer;
        }
        else if (num == 0)
        {
            std::cout << "write out,me too" << std::endl;
            return false;
        }
        else
        {
            return false;
        }
        return true;
    }

    void Close()
    {
        if(_fd==defaultfd)
            return;
        else
            close(_fd);
    }
    
    void Remove()
    {
        int m=unlink(_filename.c_str());
        (void)m;
    }
    ~namepipe()
    {
    }

private:
    std::string _filename;
    int _fd;
};

client

cpp 复制代码
#include"namepipe.hpp"

int main()
{
    namepipe pipe(filename);

    pipe.OpenForWrite();

    while(true)
    {
        std::string in;
        std::getline(std::cin,in);
        pipe.Write(in);
    }

    pipe.Close();
}

client端发送数据。

server

cpp 复制代码
#include"namepipe.hpp"

int main()
{
    namepipe pipe(filename);
    pipe.InitNamepipe();
    pipe.OpenForRead();
    while(true)
    {
        std::string* out;
        if(pipe.Read(out))
            std::cout<<*(out)<<std::endl;
        else
            break;
    }
    pipe.Close();
    pipe.Remove();
    return 0;
}

server端读数据。

结果演示:

在client端输入数据,server就可以看到对应的读取数据。

相关推荐
平行云PVT12 小时前
数字孪生信创云渲染技术解析:从混合信创到全国产化架构
linux·unity·云原生·ue5·图形渲染·webgl·gpu算力
xdscode13 小时前
Linux云服务器安装openclaw,并对接飞书通道
linux·服务器·飞书·openclaw
lswzw13 小时前
win11家庭版 安装 openclaw
服务器
Percep_gan14 小时前
Linux中安装Redis,很详细
linux·运维·redis
七七powerful14 小时前
运维养龙虾--AI 驱动的架构图革命:draw.io MCP 让运维画图效率提升 10 倍,使用codebuddy实战
运维·人工智能·draw.io
枕书14 小时前
实战记录:如何使用 Docker 一键部署长亭 PandaWiki 智能知识库
运维·docker·容器
LegendNoTitle14 小时前
计算机三级等级考试 网络技术 选择题考点详细梳理
服务器·前端·经验分享·笔记·php
2401_8772742414 小时前
从匿名管道到 Master-Slave 进程池:Linux 进程间通信深度实践
linux·服务器·c++
feng_you_ying_li14 小时前
linux之用户的权限详解(4)
linux·运维·服务器
二进制person16 小时前
JavaEE初阶 --网络编程
linux·服务器·网络