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

相关推荐
RisunJan4 小时前
Linux命令-rlogin(远程登录)
linux·运维
深圳恒讯4 小时前
菲律宾云服务器与传统VPS的架构差异
运维·服务器·架构
蓝天下的守望者4 小时前
svt_apb_if里的宏定义问题
运维·服务器·数据库
小林ixn4 小时前
从“玩具工具”到“跨语言利器”:MCP 协议实战解析
运维·服务器·网络
小顿的企业观察5 小时前
中企出海战略规划,正在从“走出去”转向“走进去”
大数据·运维·人工智能·产品运营·制造
数智化转型推荐官6 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互6 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
2603_954708316 小时前
全维度容错设计,打造微电网安全运行屏障
服务器·网络·数据库·人工智能·分布式·安全
其实防守也摸鱼6 小时前
蓝队相关知识学习(1)---常用堡垒机和服务器
服务器·功能测试·学习·网络安全·网络攻击模型·攻防对抗·蓝队
大飞记Python7 小时前
Linux命令速查手册(测试开发4年实战总结,附PDF)
linux·网络·pdf