首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >在虚拟宇宙中低语——进程间通信,Linux命名管道的前世今生

在虚拟宇宙中低语——进程间通信,Linux命名管道的前世今生

作者头像
用户11379153
发布2025-11-05 16:32:59
发布2025-11-05 16:32:59
760
举报
在这里插入图片描述
在这里插入图片描述

🌌 序章

在操作系统的寂静星辰之间,进程如宇宙中的恒星各自运转,孤独却不懈。而在某一刻,它们渴望对话,渴望将彼此的思想穿越内存的风暴,投递至彼此心中。于是,进程间通信(IPC)如星际信使般诞生。而在众多信使中,有一种古老而优雅的方式被称为:命名管道(Named Pipe,亦称FIFO)。

这便是我们今天的主角——在系统的寂静深处低语的“命名管道”。

🌠 一、命名管道的宿命与哲学

命名管道,是一种半双工通信机制,允许两个不具亲缘关系的进程通过内核中一个具名的管道文件进行通信。

正如信使需要地址,命名管道也需栖身于文件系统中,等待两端的进程将心语倾诉或倾听。

匿名管道(pipe)不同,命名管道拥有一个路径名,存在于文件系统中,通常通过命令 mkfifo 或系统调用mkfifo()创建。即便通信双方毫无血缘(非父子进程),只要知晓这路径,便能互通心声。

那么如何给 匿名管道 起名字呢?

结合文件系统,给匿名管道这个纯纯的内存文件分配 inode,将文件名与之构建联系,关键点在于不给它分配 Data block,因为它是一个纯纯的内存文件,是不需要将数据刷盘到磁盘中的 可以将命名管道理解为 “挂名” 后的匿名管道,把匿名管道加入文件系统中,但仅仅是挂个名而已,目的就是为了让其他进程也能看到这个文件(文件系统中的文件可以被所有进程看到)

因为没有 Data block,所以命名管道这个特殊文件大小为 0

1.1、创建及简单使用

命令管道的创建依赖于函数 mkfifo,函数原型如下

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

关于 mkfifo 函数

在这里插入图片描述
在这里插入图片描述
  • 对于参数1,既可以传递绝对路径 /home/xxx/namePipeCode/fifo,也可以传递相对路径 ./fifo,当然绝对路径更灵活,但也更长
  • 对于参数2,mode_t 其实就是对 unsigned int 的封装,等价于 uint32_t,而 mode 就是创建命名管道时的初始权限,实际权限需要经过 umask 掩码计算

不难发现,mkfifo mkdir 非常像,其实 mkfifo 可以直接在命令行中运行

创建一个名为fifo的命名管道文件

代码语言:javascript
复制
mkfifo fifo
在这里插入图片描述
在这里插入图片描述

p是管道文件,大小为0

在这里插入图片描述
在这里插入图片描述

这个管道文件也非常特殊:大小为 0,从侧面说明 管道文件就是一个纯纯的内存级文件,有自己的上限,出现在文件系统中,只是单纯挂个名而已

可以直接在命令行中使用命名管道:

  • echo 可以进行数据写入,可以重定向至 fifo
  • cat 可以进行数据读取,同样也可以重定向于 fifo

打开两个终端窗口(两个进程),即可进行通信

在这里插入图片描述
在这里插入图片描述

命名管道遵循管道的四种场景,因此在写端未写入数据时,读端会阻塞 当然也可以通过程序实现两个独立进程 IPC

思路:创建 服务端 server 客户端 client 两个独立的进程,服务端 server 创建并以 的方式打开管道文件,客户端 client 的方式打开管道文件,打开后俩进程可以进程通信,通信结束后,由客户端关闭 写端(服务端 读端 读取到 0 后也关闭并删除命令管道文件

注意:

  • 当管道文件不存在时,文件会打开失败,因此为了确保正常通信,需要先运行服务端 server 创建管道文件
  • 服务端启动后,因为是读端,所以会阻塞等待 客户端(写端)写入数据
  • 客户端写入数据时,因为 ‘\n’ 也被读取了,所以要去除此字符
  • 通信结束后,需要服务端主动删除管道文件
代码语言:javascript
复制
unlink 命令管道文件名	//删除管道文件

为了让服务端和客户端能享有同一个文件名,可以创建一个公共头文件 common.h,其中存储 命名管道文件名及默认权限等公有信息

公共资源 common.h

代码语言:javascript
复制
#pragma once

#include <iostream>
#include <string>

std::string fifo_name = "./fifo";   //管道名
uint32_t mode = 0666;   //权限

服务端(写端) server.cc 提供文件拷贝服务

代码语言:javascript
复制
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"

using namespace std;

int main()
{
   
   
    // 服务端
    // 1、打开文件
    int wfd = open(fifo_name.c_str(), O_WRONLY);
    if (wfd < 0)
    {
   
   
        cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;
        exit(0);
    }

    // 2、打开源文件
    FILE *fp = fopen("file.txt", "r");
    if (fp == NULL)
    {
   
   
        cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;
        exit(0);
    }

    // 3、读取源文件数据
    char buff[1024];
    int n = fread(buff, sizeof(char), sizeof(buff), fp);

    //IPC区域
    // 4、写入源文件至命名管道
    write(wfd, buff, strlen(buff));
    cout << "服务端已向管道写入: " << n << "字节的数据" << endl;
    //IPC区域

    fclose(fp);
    fp = nullptr;
    close(wfd);
    return 0;
}

客户端(读端) client.cc 从服务端中拷贝文件(下载)

代码语言:javascript
复制
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"

using namespace std;

int main()
{
   
   
    // 客户端
    // 1、创建命名管道文件
    int ret = mkfifo(fifo_name.c_str(), mode);
    if (ret < 0)
    {
   
   
        cerr << "mkfifo fail! errno: " << errno << " | " << strerror(errno) << endl;
        exit(0
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🌌 序章
  • 🌠 一、命名管道的宿命与哲学
    • 1.1、创建及简单使用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档