【Linux】进程间通信(3)_命令管道

hello~ 很高兴见到大家! 这次带来的是Linux系统中关于进程间通信这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙


文章目录

  • 一、命名管道
    • [1.1 介绍](#1.1 介绍)
    • [1.2 使用命名管道](#1.2 使用命名管道)

一、命名管道

1.1 介绍

  1. 命名管道是管道里面除开匿名管道的另一种,它负责处理不具有血缘关系的进程之间的通信。匿名管道的限制就是必须要有血缘关系。
  2. 进程间要实现通信,必须访问同一份共享资源(即打开同一个文件,文件相关的核心数据结构和内核缓冲区只会存在一份)。对于无血缘关系的进程来说,由于不会继承父进程的文件描述符,想要通过文件实现通信,就必须明确知道该文件的路径和名称。
  1. 普通文件的内核缓冲区会自动将数据刷新到磁盘,但命名管道不需要这种自动刷盘的特性(它需要将数据给到另一个进程而不是给磁盘),仅需借助文件的路径和文件名属性让进程找到共享资源,因此衍生出了管道文件这种特殊文件类型 ------ 其磁盘上的实际存储内容始终为空。(路径+文件名 = 唯一inode)
  2. 匿名管道仅保留 "用于进程通信的内核内存块" 这一核心功能,无需文件的路径和名称属性,因此它仅存在于内核空间中,不会在磁盘上留下实体。
  1. 命名管道与匿名管道的核心区别在于存储形态:匿名管道仅存在于内核空间,而命名管道会以管道文件的形式真实存在于磁盘中,管道文件正是命名管道的物理载体
  2. 至于命名管道的5个特点和4个通信情况,这个匿名管道一样。

1.2 使用命名管道

  1. 系统提供了 mkfifo 接口用于创建命名管道。该接口有两种使用方式:一是直接在命令行中执行 mkfifo 命令创建,二是在程序代码中调用 mkfifo() 函数创建;其核心参数包含两个:第一个参数是管道文件的路径(含文件名),第二个参数是该管道文件的访问权限(如 0664,它会受 到umask 权限掩码影响)。
  1. 这样就创建出了名为 pipe 的管道文件,用 ls -l 查看可以看到文件属性以 p 开头,表示这是管道文件。

  2. 我们可以使用 unlink 接口来间接删除文件(文件会由OS删除),字面意思就是去掉文件名和文件路径之间的联系。

  1. 我们就可以使用这个命名管道在不同进程之间进行通信。接下来我们来完成一段Client进程往命名管道里写,Server从命名管道里读的一段代码,实现这两个进程之间的通信。
cpp 复制代码
//Pipe.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>

enum
{
    OK = 0,
    MKFIFO_ERROR,
    OPEN_ERROR
};

#define ForRead 1
#define ForWrite 2

const std::string gcommfile = "./fifo";

class Fifo
{
public:
    Fifo(const std::string &commfile = gcommfile) : _commfile(commfile), _fd(-1), _mode(0666)
    {
    }
    ~Fifo()
    {
    }
    void Build()
    {
        umask(0);

        int n = mkfifo(_commfile.c_str(), _mode);
        if (n < 0)
        {
            std::cerr << "mkfifo error" << strerror(errno) << "errno:"
                      << errno << std::endl;

            exit(MKFIFO_ERROR);
        }
        std::cout << "mkfifo success" << std::endl;
    }

    void Open(int mode)
    {
        if (mode == ForRead)
        {
            _fd = open(_commfile.c_str(), O_RDONLY);
        }
        else if (mode == ForWrite)
        {
            _fd = open(_commfile.c_str(), O_WRONLY);
        }
        else
        {
        }
        if (_fd < 0)
        {
            std::cerr << "open error" << strerror(errno) << "errno:"
                      << errno << std::endl;
            exit(OPEN_ERROR);
        }
        else
        {
            std::cout << "open success" << std::endl;
        }
    }

    //发送信息
    void Send(const std::string& msgin)
    {
        ssize_t n = write(_fd, msgin.c_str(), sizeof(msgin));
        (void)n;
    }
    //接收信息
    int Recv(std::string* msgout)
    {
        char buffer[128];
        ssize_t n = read(_fd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            *msgout = buffer;
            return n;
        }
        else if (n == 0)
        {
            return 0;
        }
        else
        {
            return -1;
        }
    }
    void Delete()
    {
        // unlink可以用来删除文件
        int n = unlink(_commfile.c_str());
        (void)n;
        std::cout << "Unlink:" << _commfile << std::endl;
    }

private:
    std::string _commfile;
    int _fd;
    int _mode;
};
cpp 复制代码
//Client.cc
#include "Pipe.hpp"

int main()
{
    Fifo pipe;
    pipe.Build();
    pipe.Open(ForWrite);
    while (true)
    {
        std::cout << "Please Enter@ ";
        std::string s;
        std::getline(std::cin, s);
        pipe.Send(s);
    }
    return 0;
}
cpp 复制代码
//Server.cc
#include "Pipe.hpp"

int main()
{
    Fifo pipe;
    pipe.Open(ForRead);
    std::string s;
    while (true)
    {
        int n = pipe.Recv(&s);
        if (n > 0) 
            std::cout << "Client say:" << s << std::endl;
        else
            break;
    }
    pipe.Delete();
    return 0;
}
  1. 这里需要特别说明:命名管道如果只单方面以读(r)或写(w)模式打开,进程会阻塞在 open() 调用处,直到有另一个进程打开该管道的另一端(读端对应写端,写端对应读端),程序才会继续执行后续逻辑

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

相关推荐
小比特_蓝光2 小时前
Linux:基本指令
linux·运维·服务器
冉佳驹3 小时前
Linux ——— 多线程编程中的核心概念与技术实现
linux·条件变量·互斥锁·自旋锁·线程的概念·线程的创建、等待、分离·生产者消费模型
Skilce4 小时前
HAProxy
linux·运维·负载均衡
有一个好名字4 小时前
claude code安装
linux·运维·前端
亮子AI4 小时前
【Linux】如何拷贝目录?
linux·运维·服务器
starvapour4 小时前
Ubuntu更换显卡驱动后网络消失的问题
linux·运维·ubuntu
风酥糖4 小时前
在Termux中运行Siyuan笔记服务
android·linux·服务器·笔记
哼?~4 小时前
Linux信号产生
linux
Je1lyfish4 小时前
CMU15-445 (2026 Spring) Project#2 - B+ Tree
linux·数据结构·数据库·c++·sql·spring·oracle