跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

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

综述由AI生成Linux 命名管道利用文件系统标识实现任意进程间通信,解决了匿名管道仅限血缘进程的局限。其本质是内核缓冲区配合文件入口,支持半双工字节流传输。创建需 mkfifo 指定路径,打开时读端写端阻塞同步机制是关键。代码示例展示了服务端监听与客户端发送的交互流程,并总结了 SIGPIPE 处理、文件残留等常见坑点,适合入门跨进程通信开发。

神经兮兮发布于 2026/3/23更新于 2026/5/34 浏览
Linux 命名管道(FIFO)通信:原理与跨进程实战

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

在 Linux 进程间通信(IPC)中,管道是最基础的方式之一。但平时接触的'匿名管道'有个致命缺陷——只能用于有血缘关系的进程(如父子进程),无法实现无关联进程间的通信。

命名管道(简称 FIFO) 正是为解决这个问题而生。它给管道赋予了一个'文件名',通过文件系统作为媒介,让任意两个进程(无论是否有血缘关系)都能通过这个'命名'实现数据交互。

一、先搞懂:命名管道(FIFO)是什么?

1. 命名管道的本质

命名管道和匿名管道的核心通信逻辑完全一致——都是基于'字节流'的半双工通信(同一时刻只能单向传输数据),底层都是内核中的缓冲区。

两者的核心区别在于:命名管道有一个可见的文件系统入口(一个以 .fifo 或 .pipe 为后缀的文件),这个文件只是一个'标识',不存储实际数据(数据仍在内存缓冲区中);而匿名管道没有文件入口,只能通过父子进程继承文件描述符来通信。

简单来说:匿名管道是'隐式的、血缘专属',命名管道是'显式的、通用的',只要知道 FIFO 文件的路径,任意进程都能与之通信。

2. 命名管道的核心特点

  • 跨进程通信:无血缘关系的进程(如两个独立的程序),只要知道 FIFO 文件路径,就能通过它通信;
  • 半双工通信:数据只能单向流动,若要实现双向通信,需要创建两个 FIFO(一个用于 A→B,一个用于 B→A);
  • 基于文件标识:FIFO 文件存在于文件系统中,进程通过打开这个文件,获取读写文件描述符,进而实现通信;
  • 阻塞特性:默认情况下,打开 FIFO 的读端会阻塞,直到有进程打开写端;打开写端也会阻塞,直到有进程打开读端(可通过 O_NONBLOCK 设置为非阻塞);
  • 生命周期:FIFO 文件不会随进程退出而自动删除,需要手动删除(rm 命令或 unlink 函数),否则会一直存在于文件系统中。

3. 命名管道与匿名管道的对比

为了更清晰区分,做一个简单对比表,避免混淆:

对比维度匿名管道(pipe)命名管道(FIFO)
通信范围有血缘关系的进程(父子、兄弟)任意进程(无血缘关系也可)
标识方式无文件标识,通过文件描述符继承有文件系统入口(FIFO 文件)
创建方式通过 pipe() 系统调用创建通过 mkfifo() 系统调用或 mkfifo 命令创建
生命周期随进程退出而销毁(文件描述符关闭)随 FIFO 文件删除而销毁,进程退出不影响文件
使用场景父子进程间简单通信(如 shell 管道)无关联进程间通信(如两个独立程序交互)

二、命名管道的创建方式

命名管道有两种创建方式:命令行创建和代码创建,本质都是在文件系统中生成一个 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;
}

2.3 命名管道的打开规则

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

  • O_RDONLY:以只读方式打开(读端),默认阻塞,直到有进程以 O_WRONLY 方式打开;
  • O_WRONLY:以只写方式打开(写端),默认阻塞,直到有进程以 O_RDONLY 方式打开;
  • O_RDWR:以读写方式打开,不阻塞,直接打开(同时具备读和写权限,可实现单向通信的'自我循环')

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

三、实操实现:手搓命名管道通信

下面我们通过一个简单的 C++ 示例来实现服务端和客户端的交互。

  • server.cpp:作为服务端,监听管道,接收客户端消息并打印;
  • client.cpp:作为客户端,向管道发送消息,实现双向交互。

3.1 前置准备(Makefile && comm.h)

Makefile:

all: client server
client: client.cpp
	g++ -o $@ $^ -std=c++11
server: server.cpp
	g++ -o $@ $^ -std=c++11
.PHONY: clean
clean:
	rm -f client server

comm.h:

#pragma once
#include <string>
const std::string fifoname = "fifo";

3.2 服务端程序(server.cpp)

#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. 打开管道文件
    // 注意:read 端打开会阻塞,直到有 write 端连接
    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;
}

3.3 客户端程序(client.cpp)

#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() {
    // 写端打开会阻塞,直到有 read 端连接
    int wfd = open(fifoname.c_str(), O_WRONLY);
    if (wfd < 0) {
        perror("open");
        return 1;
    }

    std::string outstring;
    while (true) {
        std::cout << "Please Enter@ ";
        std::cin >> outstring;
        write(wfd, outstring.c_str(), outstring.size());
        // 注意:不需要额外写 '\0',write 按字节数写入
    }

    close(wfd);
    return 0;
}

3.4 编译运行

确保 Makefile 和源文件在同一目录下,执行 make 即可编译。然后分别在两个终端窗口启动 server 和 client 进行测试。

四、命名管道的实际应用场景

命名管道因其'跨进程、简单易用'的特点,在 Linux 开发中应用广泛,常见场景包括:

  • 后台服务与前台程序通信:如后台守护进程(如日志服务),通过 FIFO 接收前台程序发送的日志指令,实现日志的动态控制;
  • 多个独立程序的数据交互:如两个不同的服务程序(如数据库服务和缓存服务),通过 FIFO 传递数据,无需复杂的 IPC 机制;
  • shell 脚本间通信:在 shell 脚本中,通过 mkfifo 命令创建 FIFO,实现两个脚本之间的数据传输(比匿名管道更灵活);
  • 简单的客户端/服务器模型:基于 FIFO 实现简单的 C/S 架构,客户端向 FIFO 写入请求,服务器从 FIFO 读取请求并处理,返回结果。

五、常见坑点总结(必看)

  • 忘记处理阻塞:打开 FIFO 时未设置 O_NONBLOCK,导致进程一直阻塞,误以为程序卡死;
  • 忽略 SIGPIPE 信号:读端关闭后,写端继续写入,导致进程崩溃;
  • FIFO 文件残留:未用 unlink() 删除 FIFO,下次创建时报错;
  • 路径不一致:读端和写端使用的 FIFO 路径不同,导致无法通信;
  • 权限问题:mkfifo() 设置的权限过低(如 0600),导致其他进程无法打开 FIFO,建议设置为 0666;
  • 数据错乱:未处理字节流无边界问题,读端读取的数据不完整或拼接错误。

六、总结

命名管道(FIFO)是 Linux 跨进程通信的基础方式,核心是'通过文件系统标识,实现任意进程间的字节流通信'。它继承了匿名管道的简单性,又解决了匿名管道'只能用于血缘进程'的缺陷,是入门 IPC 开发的必学知识点。

本文通过'原理→API→实操→避坑'的流程,手把手教你实现命名管道的单方向和双向通信,代码可直接复用。重点掌握 FIFO 的阻塞特性、SIGPIPE 信号处理和文件删除问题,就能避免绝大多数踩坑场景。

实际开发中,若需要更高效、更复杂的通信(如消息优先级、双向通信的更优实现),可后续学习消息队列、共享内存等 IPC 机制,但命名管道的核心思想(字节流、文件标识),是理解所有 IPC 机制的基础。

目录

  1. Linux 命名管道(FIFO)通信:原理与跨进程实战
  2. 一、先搞懂:命名管道(FIFO)是什么?
  3. 1. 命名管道的本质
  4. 2. 命名管道的核心特点
  5. 3. 命名管道与匿名管道的对比
  6. 二、命名管道的创建方式
  7. 2.1 命令行创建(mkfifo 命令)
  8. 创建名为 myfifo 的命名管道
  9. 查看管道文件(类型标识为 p)
  10. 2.2 代码创建(mkfifo 函数)
  11. 2.3 命名管道的打开规则
  12. 三、实操实现:手搓命名管道通信
  13. 3.1 前置准备(Makefile && comm.h)
  14. 3.2 服务端程序(server.cpp)
  15. 3.3 客户端程序(client.cpp)
  16. 3.4 编译运行
  17. 四、命名管道的实际应用场景
  18. 五、常见坑点总结(必看)
  19. 六、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Python 30 分钟构建简易记事本应用
  • macOS 外接显示器配置:分辨率、多屏与色彩校准
  • 数据结构:二叉树核心概念与特性
  • SmolVLA 高算力适配:TensorRT 加速可行性分析与 ONNX 导出实操
  • MySQL 数据库基础操作:创建、管理与备份恢复
  • OpenClaw 架构原理与落地实战:AI Agent 执行网关底层逻辑
  • Codex 接入 Kimi K2 与 GLM-4.6 环境配置指南 (Windows/macOS/Ubuntu)
  • OpenClaw 飞书机器人搭建指南
  • 电商 AI 绘画:产品提示词精准撰写实战指南
  • OpenClaw 与企业即时通讯平台集成架构研究
  • HTML5 页面 XSS 攻击的 AI 识别与防御策略
  • Spring Web MVC 入门指南:从概念到实践
  • 华为手机鸿蒙系统安装 Google Play 方案对比及操作教程
  • Claude Code 本地化部署教程:基于 Ollama 实现离线开发
  • C++ 红黑树的概念、规则与实现
  • Python 自动化办公:使用 xlwt 写入 Excel
  • C++ 关联式容器详解:Set、Map 与底层原理
  • 剑桥大学博士论文:检索增强生成(RAG)如何提升视觉问答
  • 自然语言处理在医疗领域的应用与实战
  • JavaScript Response 对象详解与使用指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • 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