进程间通信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就可以看到对应的读取数据。

相关推荐
有没有没有重复的名字几秒前
进程间通信
运维·服务器
qwfys20022 分钟前
一次网络问题排查
linux·kylin·network·v10
孤邑29 分钟前
【Linux】DBUS基础
linux·运维·服务器
loong_XL30 分钟前
fastmcp MCPConfig多服务器使用案例;sse、stdio、streamable-http使用
服务器·agent·mcp
孙克旭_31 分钟前
day037-openssh服务与http协议
linux·运维·网络·网络协议·http
99乘法口诀万物皆可变1 小时前
C#设计模式之AbstractFactory_抽象工厂_对象创建新模式-练习制作PANL(一)
服务器·javascript·c#·html
周六放风筝1 小时前
生成https免费证书并绑定到nginx
linux·nginx·https
量化投资和人工智能1 小时前
【K8S】详解Labels 和 Annotations
运维·docker·云原生·容器·kubernetes
小艺E1 小时前
硬核算力时代:裸金属服务器如何重塑企业级云基建?
运维·服务器
deeper_wind1 小时前
Rsync+sersync实现数据实时同步(小白的“升级打怪”成长之路)
linux·运维·网络