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

相关推荐
悠悠121384 分钟前
告别Zabbix?我用Netdata只花10分钟就搞定了50台服务器的秒级监控(保姆级实战)
运维·服务器·zabbix
天庭鸡腿哥18 分钟前
大小只有4K的软件,可让系统瞬间丝滑!
运维·服务器·windows·microsoft·everything
虚伪的空想家22 分钟前
华为昇腾Atlas 800 A2物理服务器开启VT-d模式
运维·服务器·ubuntu·kvm·vt-d·直通
学渣6765628 分钟前
服务器端口映射
运维·服务器
红袜子i29 分钟前
【问题】实验室服务器恢复记录,一个主板挂两张显卡,
运维·服务器
S***q19230 分钟前
DevOps在云中的云计算
运维·云计算·devops
h***01542 小时前
Docker启动安装nacos(详情讲解,全网最细)
运维·docker·容器
z***94842 小时前
Linux下安装Nginx服务及systemctl方式管理nginx详情
linux·运维·nginx
默恋~微凉2 小时前
Nginx(十一)——反向代理与负载均衡
运维·nginx·负载均衡
凉晓风2 小时前
Linux上TCP通信异常排查工具命令
linux·运维·tcp/ip