进程间通信(IPC):管道通信全解析

一、管道通信的核心背景

Linux 系统中进程地址空间相互隔离,管道通过内核提供的 "伪文件"(队列结构)实现进程间数据传递,是最经典的单机 IPC 方式之一,具备实现简单、轻量高效的特点。

二、无名管道(Unnamed Pipe)

1. 核心定义

无名管道(也叫匿名管道)是仅支持有亲缘关系进程(父子 / 兄弟进程)通信的半双工管道,无文件名称,文件系统不可见,生命周期随进程结束而销毁。

2. 核心特性

特性 说明
通信范围 仅限有亲缘关系的进程(如 fork 创建的父子进程)
工作模式 半双工(数据单向传输),实际开发中通常当作单工使用
文件属性 特殊的伪文件,不支持lseek()(文件 IO)/fseek()(标准 IO)定位操作
IO 方式 首选文件 IO(open/read/write/close),也可使用标准 IO(fgets/fread 等,需注意缓冲区)
核心限制 数据传输上限约 64KB,超出会触发阻塞

3. 关键函数

c

运行

复制代码
#include <unistd.h>
int pipe(int pipefd[2]);
  • 功能:创建并打开一个无名管道,在内核中分配队列资源;
  • 参数pipefd[0] 固定为管道读端,pipefd[1] 固定为管道写端;
  • 返回值 :成功返回 0,失败返回 - 1(可通过perror()查看错误原因)。

4. 编程流程

plaintext

复制代码
创建管道(pipe()) → fork创建子进程 → 关闭无用端(父子进程分工读/写) → 读写管道 → 关闭管道(close())

5. 核心行为(必记)

  1. 写阻塞:读端存在时,向管道写入数据超过 64KB,写操作会阻塞(写进程速度过快);
  2. 读阻塞:写端存在时,读取空管道,读操作会阻塞(读进程速度过快);
  3. 管道破裂 :读端关闭后仍向管道写数据,内核会向写进程发送SIGPIPE信号,导致写进程退出;
  4. 通信结束:写端关闭后,读进程读取空管道会返回 0(可作为通信结束的标志)。

6. 极简示例代码

c

运行

复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {
    int pipefd[2];
    // 1. 创建无名管道
    if (pipe(pipefd) == -1) {
        perror("pipe create failed");
        return 1;
    }

    // 2. fork创建子进程(亲缘进程)
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        return 1;
    }

    // 子进程:写管道
    if (pid == 0) {
        close(pipefd[0]); // 关闭读端(仅写)
        char buf[] = "Hello from child process (pipe)";
        write(pipefd[1], buf, strlen(buf));
        close(pipefd[1]); // 关闭写端
        return 0;
    }

    // 父进程:读管道
    close(pipefd[1]); // 关闭写端(仅读)
    char recv_buf[1024] = {0};
    read(pipefd[0], recv_buf, sizeof(recv_buf));
    printf("Parent read: %s\n", recv_buf);
    close(pipefd[0]); // 关闭读端
    wait(NULL); // 等待子进程退出

    return 0;
}

三、有名管道(FIFO)

1. 核心定义

有名管道(FIFO)解决了无名管道 "仅亲缘进程通信" 的限制,支持任意单机进程通信,底层队列结构与无名管道一致,最大区别是 "文件系统可见"(有明确的路径和名称)。

2. 核心特性

  • 继承无名管道的所有特性(半双工、无定位、64KB 上限等);
  • 额外特性:管道以文件形式存在(ls -l可见,文件类型为 p),若一端未打开,另一端调用open()时会阻塞;
  • 生命周期:文件存在于文件系统,需手动删除(unlink()/rm),否则会残留。

3. 关键函数

c

运行

复制代码
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
  • 功能:在指定路径创建有名管道文件;
  • 参数pathname为管道路径 + 名称(如./my_fifo),mode为 8 进制文件权限(如 0664);
  • 返回值:成功返回 0,失败返回 - 1(若文件已存在,错误码为 EEXIST)。

4. 编程流程

plaintext

复制代码
创建有名管道(mkfifo()) → 进程A/B打开管道(open()) → 进程间读写管道 → 关闭管道(close()) → 删除管道文件(unlink())

5. 极简示例代码

写进程(fifo_write.c)

c

运行

复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>

#define FIFO_PATH "./my_fifo"

int main() {
    // 1. 创建有名管道
    if (mkfifo(FIFO_PATH, 0664) == -1) {
        perror("mkfifo failed");
        return 1;
    }

    // 2. 打开管道(写模式,阻塞等待读端打开)
    int fd = open(FIFO_PATH, O_WRONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    // 3. 写数据
    char buf[] = "Hello from FIFO write process";
    write(fd, buf, strlen(buf));
    printf("Write data: %s\n", buf);

    // 4. 关闭并删除管道
    close(fd);
    unlink(FIFO_PATH);

    return 0;
}
读进程(fifo_read.c)

c

运行

复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>

#define FIFO_PATH "./my_fifo"

int main() {
    // 1. 打开管道(读模式,阻塞等待写端打开)
    int fd = open(FIFO_PATH, O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    // 2. 读数据
    char recv_buf[1024] = {0};
    read(fd, recv_buf, sizeof(recv_buf));
    printf("Read data: %s\n", recv_buf);

    // 3. 关闭管道
    close(fd);

    return 0;
}

6. 运行方式

bash

运行

复制代码
# 编译
gcc fifo_write.c -o fifo_write
gcc fifo_read.c -o fifo_read

# 终端1运行读进程(阻塞等待写端)
./fifo_read

# 终端2运行写进程(完成后自动删除管道)
./fifo_write

四、无名管道 vs 有名管道 核心对比

维度 无名管道 有名管道
通信范围 仅亲缘进程 任意单机进程
文件可见性 不可见(仅内核中存在) 可见(文件系统中有路径 / 名称)
创建方式 pipe() mkfifo()
生命周期 随进程退出销毁 需手动删除,否则残留
open 阻塞 无(创建时已打开) 一端未打开时,另一端 open 阻塞
核心特性 半双工、64KB 上限、无定位 继承无名管道所有特性

五、管道通信实践建议

  1. 场景选择
    • 亲缘进程单机通信 → 无名管道(无需手动清理,轻量);
    • 任意单机进程通信 → 有名管道(灵活,需注意文件残留);
  2. 注意事项
    • 避免管道破裂:读端关闭前,写端不要持续写数据;
    • 数据完整性:管道传输字节流,需自定义分隔符(如\n)标识数据边界;
    • 性能限制:64KB 上限决定管道不适合大数据传输,大数据场景优先选共享内存。
相关推荐
无奈笑天下3 小时前
银河麒麟V10虚拟机安装vmtools报错:/bin/bash解释器错误, 权限不够
linux·运维·服务器·开发语言·经验分享·bash
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [fs]kernfs
linux·笔记·学习
代码游侠3 小时前
学习笔记——IO多路复用技术
linux·运维·数据库·笔记·网络协议·学习
比奇堡派星星3 小时前
Linux Hotplug 机制详解
linux·开发语言·驱动开发
忙里偷闲学python3 小时前
docker
运维·docker·容器
云飞云共享云桌面3 小时前
河北某机器人工厂8个研发设计共享一台SolidWorks云主机
运维·服务器·网络·数据库·算法·性能优化·机器人
m0_485614674 小时前
Linux-容器基础2
linux·运维·服务器
于齐龙4 小时前
2025年12月19日 - 操作系统
运维·服务器
QT 小鲜肉4 小时前
【Linux命令大全】001.文件管理之mattrib命令(实操篇)
linux·运维·服务器·chrome·笔记