跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

Linux 进程间通信:命名管道(FIFO)原理与实战

Linux 命名管道(FIFO)通过文件系统路径实现无亲缘关系进程间的通信。核心特性包括半双工、基于文件操作及生命周期随内核。创建方式支持命令行 mkfifo 和代码 mkfifo 函数。打开时需遵循读写端同步规则,避免阻塞死锁。实战案例涵盖文件拷贝与 Server-Client 模型,需注意手动删除管道文件及权限设置。相比匿名管道,其优势在于突破亲缘限制,适用于日志收集等场景。

ApiHolic发布于 2026/3/30更新于 2026/7/930 浏览
Linux 进程间通信:命名管道(FIFO)原理与实战

前言

在 Linux IPC(进程间通信)体系中,匿名管道解决了亲缘进程(父子、兄弟进程)间的通信问题,但存在一个核心限制——仅支持具有共同祖先的进程通信。而命名管道(FIFO)作为匿名管道的扩展,通过文件系统中的'命名'标识,打破了亲缘关系的束缚,实现了无关联进程间的双向数据传输。本文将从命名管道的核心概念、创建方式、通信原理和实战案例出发,带你吃透这一实用的跨进程通信技术。

一. 命名管道核心概念:什么是 FIFO?

1.1 命名管道的定义

命名管道(Named Pipe),又称 FIFO(First In First Out),是一种特殊的文件系统对象(类型为 p),其核心本质与匿名管道一致——内核中的一块缓冲区,但通过文件路径作为标识,让任意进程都能通过该路径访问管道,实现跨进程通信。

与匿名管道相比,命名管道的核心差异是'命名':

  • 匿名管道:无文件路径,仅通过 pipe() 创建的文件描述符在亲缘进程间共享;
  • 命名管道:有明确的文件路径(如 /tmp/myfifo),任意进程可通过 open() 打开该路径,实现通信。

1.2 命名管道的核心特性

  • 跨进程通信:支持无亲缘关系的进程(如两个独立的应用程序)通信,突破匿名管道的亲缘限制;
  • 半双工通信:数据单向流动,如需双向通信需创建两个命名管道;
  • 基于文件操作:遵循 Linux'一切皆文件'思想,通过 open()/read()/write()/close() 等标准文件接口操作;
  • 生命周期随内核:命名管道创建后,即使创建进程退出,管道文件仍存在于文件系统中,需手动删除(unlink() 或 rm 命令);
  • 同步与互斥:内核自动保证管道操作的同步(如读阻塞、写阻塞)和互斥(同一时间仅允许一个进程写)。

1.3 命名管道和匿名管道的区别与联系

  • 匿名管道由 pipe 函数创建并打开。
  • 命名管道由 mkfifo 函数创建,打开用 open。
  • FIFO(命名管道)与 pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的语义。
特性匿名管道命名管道 (FIFO)
定义一种半双工通信通道,通常用于具有亲缘关系的进程之间(如父子进程)。一种特殊的文件(存在于文件系统中),允许无亲缘关系的进程间通信。
创建方式通过 pipe() 系统调用创建,返回两个文件描述符(读端和写端)。通过 mkfifo() 或 mknod() 创建,在文件系统中具有路径名。
标识无名,仅通过文件描述符引用。有名,通过文件系统中的路径名标识。
进程关系仅适用于有共同祖先(如父子进程)的进程。适用于任意进程,无论是否有亲缘关系。
通信方向单向(半双工),数据只能从一个方向流动。通常也是单向(半双工),但可通过打开两个管道实现双向通信。
持久性随进程存在而存在,所有相关进程关闭管道后自动销毁。随文件系统存在,可显式删除(unlink),即使没有进程打开也不会消失。
打开方式无需显式打开,创建时直接获得文件描述符。必须像普通文件一样用 open() 打开,使用路径名。
数据模型字节流,无消息边界。字节流,无消息边界。
阻塞行为默认阻塞:读空管道或写满管道会使进程阻塞(可设置非阻塞)。默认阻塞:读空 FIFO 或写满 FIFO 会使进程阻塞(可设置非阻塞)。
典型应用场景父子进程间的简单通信,如命令管道 `` 在 shell 中的使用。

联系:

  1. 通信机制:两者都是操作系统提供的进程间通信(IPC)方式,基于内核缓冲区实现数据传输。
  2. 数据特性:都提供可靠的字节流服务,数据写入和读取的顺序一致,无消息边界。
  3. 行为相似:默认情况下,读写操作具有相似的阻塞语义(读空阻塞、写满阻塞),并支持非阻塞标志。
  4. 单向性:本质上都是单向通信管道(半双工),若要双向通信需创建两个管道。
  5. 原子性:在 PIPE_BUF 限制内,写入操作具有原子性(多个进程同时写时数据不会交错)。

二. 命名管道的创建方式

命名管道有两种创建方式:命令行创建和代码创建,本质都是在文件系统中生成一个 FIFO 类型的文件。

2.1 命令行创建(mkfifo 命令)

直接通过 mkfifo 命令创建命名管道,语法简单,适合快速测试:

# 创建名为 myfifo 的命名管道
mkfifo myfifo
# 查看管道文件(类型标识为 p)
ls -l myfifo

创建的管道文件最前面的标识是 p 开头,验证了它不是一个普通文件。另外当我们直接终止读端的时候,这个时候的情况就相当于读端关闭,写端还在一起写,所以操作系统直接杀死了我们的进程。

2.2 代码创建(mkfifo 函数)

通过 mkfifo() 系统调用在代码中创建命名管道,需指定管道路径和权限,原型如下:

#include <sys/stat.h>
#include <sys/types.h>
// 参数:pathname-管道路径;mode-权限(如 0644)
int mkfifo(const char* pathname, mode_t mode);

代码示例(创建命名管道)

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#define FIFO_PATH "./myfifo"

int main(){
    // 创建命名管道,权限 0644(所有者读 + 写,其他读)
    int ret = mkfifo(FIFO_PATH, 0644);
    if(ret == -1){
        // 若管道已存在,errno 为 EEXIST,可忽略该错误
        if(errno != EEXIST){
            perror("mkfifo error");
            return 1;
        }
        printf("命名管道已存在\n");
    } else {
        printf("命名管道创建成功\n");
    }
    return 0;
}

三. 命名管道的打开规则(关键!)

命名管道的打开(open())行为与普通文件不同,核心是'读端与写端的同步'——仅当管道的读端和写端都被打开后,通信才能正常进行,具体规则如下:

打开方式行为描述
读方式打开(O_RDONLY)- 若管道无写端打开:阻塞,直到有进程以写方式打开该管道;
- 若指定 O_NONBLOCK:不阻塞,直接返回成功
写方式打开(O_WRONLY)- 若管道无读端打开:阻塞,直到有进程以读方式打开该管道;
- 若指定 O_NONBLOCK:不阻塞,返回失败(errno=ENXIO)
读写方式打开(O_RDWR)不阻塞,直接打开(同时具备读和写权限,可实现单向通信的'自我循环')

注意:实际开发中,建议读端以 O_RDONLY 打开,写端以 O_WRONLY 打开,避免使用 O_RDWR(可能导致通信逻辑混乱)。

四. 命名管道实战案例

下面通过两个独立的程序(writer.c 写端、reader.c 读端)演示命名管道的跨进程通信,实现'写端输入数据,读端接收并打印'的功能。

4.1 案例 1:命名管道实现文件拷贝

通过两个程序配合,实现文件拷贝功能:

  • file_writer.c:读取本地文件,将内容写入命名管道;
  • file_reader.c:从命名管道读取内容,写入目标文件。
4.1.1 写端程序(file_writer.c)
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define FIFO_PATH "./tp"
#define BUF_SIZE 1024

// 错误处理宏
#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)

int main(){
    // 1. 创建命名管道
    int ret = mkfifo(FIFO_PATH, 0644);
    if(ret == -1 && errno != EEXIST){
        ERR_EXIT("mkfifo error");
    }
    // 2. 打开本地文件(待拷贝的源文件 abc)
    int infd = open("abc", O_RDONLY);
    if(infd == -1){
        ERR_EXIT("open source file error");
    }
    // 3. 以写方式打开命名管道(阻塞,直到读端打开)
    int outfd = open(FIFO_PATH, O_WRONLY);
    if(outfd == -1){
        ERR_EXIT("open fifo error");
    }
    // 4. 读取源文件内容,写入管道
    char buf[BUF_SIZE];
    int n;
    while((n = read(infd, buf, BUF_SIZE)) > 0){
        // 写入管道(保证数据完整写入)
        if(write(outfd, buf, n) != n){
            ERR_EXIT("write to fifo error");
        }
    }
    // 5. 关闭文件描述符
    close(infd);
    close(outfd);
    printf("文件内容写入管道完成\n");
    return 0;
}
4.1.2 读端程序(file_reader.c)
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define FIFO_PATH "./tp"
#define BUF_SIZE 1024
#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)

int main(){
    // 1. 以读方式打开命名管道(阻塞,直到写端打开)
    int infd = open(FIFO_PATH, O_RDONLY);
    if(infd == -1){
        ERR_EXIT("open fifo error");
    }
    // 2. 创建目标文件(拷贝后的文件 abc.bak)
    int outfd = open("abc.bak", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if(outfd == -1){
        ERR_EXIT("open target file error");
    }
    // 3. 从管道读取内容,写入目标文件
    char buf[BUF_SIZE];
    int n;
    while((n = read(infd, buf, BUF_SIZE)) > 0){
        if(write(outfd, buf, n) != n){
            ERR_EXIT("write to target file error");
        }
    }
    // 4. 关闭文件描述符,删除命名管道
    close(infd);
    close(outfd);
    unlink(FIFO_PATH); // 手动删除管道文件
    printf("文件拷贝完成\n");
    return 0;
}
4.1.3 编译与运行
# 编译两个程序
gcc file_writer.c -o writer
gcc file_reader.c -o reader
# 终端 1:运行读端(阻塞等待写端)
./reader
# 终端 2:运行写端(开始拷贝)
./writer

运行结果:

  • 写端读取 abc 文件内容,写入命名管道 tp;
  • 读端从 tp 读取内容,写入 abc.bak;
  • 拷贝完成后,读端删除管道文件,两个程序退出。

4.2 案例 2:命名管道实现 Server-Client 通信

实现一个简单的 C/S 模型:

  • server.c:作为服务端,监听管道,接收客户端消息并打印;
  • client.c:作为客户端,向管道发送消息,实现双向交互。
4.2.1 前置准备(Makefile && comm.h)

需要配置 Makefile 进行编译,并定义公共头文件 comm.h 包含管道名称常量。

4.2.2 服务端程序(server.c)
#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "comm.h"

int main(){
    // 1. 创建管道文件
    umask(0); // 重置文件创建掩码,确保权限生效
    int n = mkfifo(fifoname.c_str(), 0666);
    if(n < 0){
        perror("mkfifo");
        return 1;
    }
    // 2. 打开管道文件(阻塞等待客户端连接)
    int rfd = open(fifoname.c_str(), O_RDONLY);
    if(rfd < 0){
        perror("open");
        return 2;
    }
    char inbuffer[1024];
    // 3. 进行通信,循环接收客户端消息
    while(true){
        ssize_t n = read(rfd, inbuffer, sizeof(inbuffer)-1);
        if(n > 0){
            inbuffer[n] = 0;
            std::cout << "client say# " << inbuffer << std::endl;
        } else if(n == 0){ // 写端关闭了
            break;
        } else {
            perror("read");
            break;
        }
    }
    // 4. 关闭文件描述符
    close(rfd);
    // 5. 删除管道文件
    unlink(fifoname.c_str());
    return 0;
}
4.2.3 客户端程序(client.c)
#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.h"

int main(){
    // 1. 以写方式打开管道(阻塞,直到服务端打开读端)
    int wfd = open(fifoname.c_str(), O_WRONLY);
    if(wfd < 0){
        perror("open");
        return 1;
    }
    // 2. 循环输入并发送消息给服务端
    std::string outstring;
    while(true){
        std::cout << "Please Enter@ ";
        std::cin >> outstring;
        write(wfd, outstring.c_str(), outstring.size());
        // 不需要写 \0
    }
    // 3. 关闭文件描述符
    close(wfd);
    return 0;
}
4.2.4 编译与运行
# 编译程序
make
# 终端 1:启动服务端(阻塞等待客户端)
./server
# 终端 2:启动客户端(输入消息交互)
./client

注意:必须先打开服务端再打开客户端,否则会出现阻塞。

五. 命名管道使用避坑指南和总结

  • 必须手动删除管道文件:命名管道创建后会残留于文件系统,若不删除,下次创建会报错(errno=EEXIST),建议在通信结束后用 unlink() 删除;
  • 避免单进程同时读写:虽然可通过 O_RDWR 打开管道实现单进程读写,但会破坏半双工特性,容易导致数据混乱;
  • 处理阻塞场景:读端未打开时写端会阻塞,写端未打开时读端会阻塞,若需非阻塞操作,可在 open() 时添加 O_NONBLOCK 标志;
  • 数据完整性保证:当写入数据量 ≤PIPE_BUF(默认 4096 字节)时,内核保证写入原子性;超过则不保证,需在应用层处理分包;
  • 权限设置合理:创建管道时权限需开放给通信进程(如 0664 允许同组进程访问),避免因权限不足导致 open() 失败。

总结:命名管道(FIFO)是匿名管道的重要扩展,其核心价值在于突破了亲缘进程的限制,通过文件系统路径实现任意进程间的通信,且沿用了 Linux 标准的文件操作接口,上手成本低。

  • 命名管道是带文件路径的内核缓冲区,支持跨进程通信
  • 需通过 mkfifo() 或 mkfifo 命令创建,open() 时需遵循读/写端同步规则;
  • 实战中可实现文件拷贝、C/S 通信等场景,配合 read()/write() 即可完成数据传输;
  • 与匿名管道相比,命名管道的核心优势是通信范围无限制,生命周期随内核。

目录

  1. 前言
  2. 一. 命名管道核心概念:什么是 FIFO?
  3. 1.1 命名管道的定义
  4. 1.2 命名管道的核心特性
  5. 1.3 命名管道和匿名管道的区别与联系
  6. 二. 命名管道的创建方式
  7. 2.1 命令行创建(mkfifo 命令)
  8. 创建名为 myfifo 的命名管道
  9. 查看管道文件(类型标识为 p)
  10. 2.2 代码创建(mkfifo 函数)
  11. 三. 命名管道的打开规则(关键!)
  12. 四. 命名管道实战案例
  13. 4.1 案例 1:命名管道实现文件拷贝
  14. 4.1.1 写端程序(file_writer.c)
  15. 4.1.2 读端程序(file_reader.c)
  16. 4.1.3 编译与运行
  17. 编译两个程序
  18. 终端 1:运行读端(阻塞等待写端)
  19. 终端 2:运行写端(开始拷贝)
  20. 4.2 案例 2:命名管道实现 Server-Client 通信
  21. 4.2.1 前置准备(Makefile && comm.h)
  22. 4.2.2 服务端程序(server.c)
  23. 4.2.3 客户端程序(client.c)
  24. 4.2.4 编译与运行
  25. 编译程序
  26. 终端 1:启动服务端(阻塞等待客户端)
  27. 终端 2:启动客户端(输入消息交互)
  28. 五. 命名管道使用避坑指南和总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 中 del 语句与垃圾回收机制深度解析
  • 大模型实战:深入解析 LLaMA 核心算子 RMSNorm 开发
  • C++ 二叉搜索树(BST)详解与实现
  • 基于 ECharts 与 Three.js 的碳排放可视化大屏实现
  • 前端可访问性:构建包容性的 Web 体验
前端交互体验中的消息提示组件设计
  • Python 从零基础入门到精通学习指南
  • 前端骨架屏实现详解:Vue React Angular 及小程序多端实践
  • AI 提示词管理工具 AiShort
  • 前端岗位需求下降 9.89%,AI 时代前端开发的未来在哪里
  • 前端岗位面试高频原题与参考答案汇总
  • ES6 基础语法:变量声明与解构
  • 前端动画库选型指南:CSS、Framer Motion、GSAP 及 React Spring
  • 前端大数据导出优化:解决 Chrome 内存崩溃的实战方案
  • 前端 CI/CD 流程与自动化部署实践
  • 鸿蒙卡片开发实战:音乐播放器卡片构建指南
  • 用 AI 接管产品工作流:需求到上线的全流程实战
  • 前端表单验证策略:防止无效数据提交
  • 5 个 nano banana 提示词资源网站及用法指南
  • HTML、CSS、JavaScript 三者关系详解
  • 相关免费在线工具

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

    • HTML转Markdown

      将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

    • JSON 压缩

      通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

    • JSON美化和格式化

      将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online