Linux 进程间通信 —— 匿名管道和命名管道

一、管道通信概述

管道是 Linux 最基础的进程间通信(IPC)方式,分为匿名管道(pipe)命名管道(FIFO/mkfifo) ,核心都是基于内核缓冲区实现数据传输,均为半双工(数据单向流动)。

二、匿名管道(pipe)

1. 核心特点

  • 无文件系统实体,仅存在于内核中;
  • 仅支持亲缘进程(父子 / 兄弟进程)间通信;
  • 生命周期随进程:进程退出后管道自动销毁;
  • 无需手动创建文件,直接通过系统调用分配内核缓冲区。

2. 函数原型

复制代码
#include <unistd.h>
int pipe(int pipefd[2]);

3. 参数与返回值

  • pipefd[2]:输出型参数,保存管道的两个文件描述符:
    • pipefd[0]:读端,仅用于读取数据;
    • pipefd[1]:写端,仅用于写入数据;
  • 返回值:成功返回 0,失败返回 -1(设置 errno)。

4. 注意事项

  • 必须先创建管道,再 fork() 子进程(保证子进程继承管道描述符);
  • 读端 / 写端需成对关闭,否则可能导致 read() 阻塞;
  • read() 读取空管道时会阻塞,写端关闭后 read() 返回 0。

三、命名管道(FIFO/mkfifo)

1. 核心特点

  • 有文件系统实体(ls -l 显示权限位开头为 p);
  • 支持任意进程(无亲缘关系)间通信;
  • 生命周期独立:进程退出后管道文件仍存在,需手动删除;
  • 数据存储于内核缓冲区,文件仅作为 "访问标识"。

2. mkfifo () 函数详解

函数原型
复制代码
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
参数
  • pathname:管道文件路径(如 ./myfifo);
  • mode:文件权限,必须为八进制(如 0664,最终权限 = mode & ~umask)。
返回值
返回值 场景 errno 标识 处理方式
0 管道首次创建成功 正常使用
-1 管道文件已存在 EEXIST (17) 复用管道,无需报错
-1 真失败(权限 / 路径错误) EACCES/ENOENT 等 perror() 打印错误并退出
关键错误判断代码
复制代码
#include <errno.h>
int ret = mkfifo("./myfifo", 0664);
if (ret == -1) {
    if (errno == EEXIST) {
        printf("管道已存在,直接复用\n");
    } else {
        perror("mkfifo 创建失败");
        return -1;
    }
}

3. 常见踩坑点

  1. 读写端搞反 :读进程必须用 O_RDONLY,写进程必须用 O_WRONLY
  2. read 长度错误 :❌ read(fd, buf, strlen(buf))(未初始化数组长度随机),✅ read(fd, buf, sizeof(buf)-1)
  3. open 阻塞特性 :默认 open() 会阻塞,直到读写端配对,非阻塞需加 O_NONBLOCK
  4. 管道文件残留 :通信完成后用**unlink()**删除,避免重复创建误判。

四、unlink()函数详解

1、函数核心定位

unlink() 是 Linux 系统调用,核心作用是删除文件系统中的文件链接(目录项),对于命名管道(FIFO)而言,是清理管道文件的核心函数。

2、函数原型与头文件

复制代码
#include <unistd.h>

int unlink(const char *pathname);
2.1. 参数
  • pathname:要删除的文件路径(如 ./myfifo/tmp/fifo),支持相对 / 绝对路径。
2.2. 返回值
返回值 含义
0 成功删除文件链接
-1 失败,设置 errno 表示原因(如文件不存在、权限不足、路径错误等)

3、核心作用

3.1. 对普通文件:删除目录项,释放磁盘空间
  • 普通文件被 unlink() 后,若没有进程打开该文件,文件会立即被删除,磁盘空间释放;
  • 若有进程正在打开该文件,目录项先被删除(ls 看不到),但文件内容直到所有进程关闭该文件后才会释放。
3.2. 对命名管道(FIFO):删除管道文件标识
  • 命名管道是 "特殊文件",unlink() 会直接删除文件系统中的管道文件(ls -l 看不到);
  • 管道的内核缓冲区不受影响:若有进程正在通过该管道通信,unlink() 后通信仍可正常进行,直到所有进程关闭管道描述符,内核缓冲区才会释放。

4、在命名管道通信中的必用场景

4.1. 为什么必须用 unlink() 清理命名管道?
  • 命名管道文件不会随进程退出自动删除,若不清理,下次运行 mkfifo() 会返回 -1 + errno=EEXIST
  • 残留的管道文件可能被其他进程误用,导致通信异常。

5、常见错误码(errno)

errno 值 宏定义 含义 解决方案
2 ENOENT 文件不存在 检查路径是否正确,避免重复删除
13 EACCES 权限不足(无目录写权限) 提升目录权限(如 chmod 775 目录
17 EEXIST 路径是目录(非文件) 确认参数是文件路径,而非目录
20 ENOTDIR 路径中包含非目录节点 修正路径,确保父目录存在且是目录

6、注意事项

6.1. 避免重复删除
  • 删除前可先检查文件是否存在(用 access() 函数),避免 unlink() 失败:

    复制代码
    #include <unistd.h>
    if (access(myfifo, F_OK) == 0) { // 检查文件是否存在
        unlink(myfifo);
    }
6.2. 命名管道通信中,unlink() 不影响正在进行的通信
  • 示例:进程 A 和进程 B 已打开 ./myfifo 通信,此时调用 unlink("./myfifo")
    • ls 看不到 myfifo 文件;
    • 进程 A/B 仍可正常读写管道,直到双方关闭文件描述符,管道内核缓冲区才释放。
6.3. 与 rm 命令的关系
  • rm 文件名 本质就是调用 unlink() 函数实现的;
  • 区别:rm 是用户态命令,unlink() 是内核态系统调用。

7、核心总结(结合管道通信)

(1)unlink() 是清理命名管道的必备函数,解决管道文件残留问题;

(2)调用时机:通信完成、关闭文件描述符后立即调用;

(3)核心特性:删除文件目录项,不影响正在打开该文件的进程的操作;

(4)错误处理:调用后检查返回值,用 perror() 定位删除失败原因。

知识点 关联逻辑
命名管道创建(mkfifo) mkfifo() 创建文件,unlink() 清理文件
管道生命周期 命名管道需 unlink() 手动销毁,匿名管道自动销毁
管道残留问题 unlink() 是解决管道残留的唯一方式

五、匿名管道 vs 命名管道 对比表

特性 匿名管道(pipe) 命名管道(mkfifo/FIFO)
进程关系 仅亲缘进程(父子 / 兄弟) 任意进程(无亲缘关系)
文件系统实体 无(仅内核缓冲区) 有(可见的管道文件)
创建方式 pipe() 系统调用 mkfifo() 系统调用
生命周期 进程退出后自动销毁 需手动 unlink()/rm 删除
阻塞特性 read()/write() 阻塞 open() 阻塞(需读写端配对)
核心使用场景 父子进程简单通信 无亲缘进程间通信
数据流向 半双工,单向流动 半双工,单向流动

六、核心总结

  1. 管道通信核心是内核缓冲区,匿名 / 命名仅为 "访问方式" 不同;
  2. 匿名管道:先 pipe()fork(),仅亲缘进程用,自动销毁;
  3. 命名管道:mkfifo() 创建文件,任意进程用,返回 -1 必看 errno
  4. 通用规则:半双工通信,读 / 写端需配对,read 长度用 sizeof(缓冲区)
相关推荐
71ber2 小时前
RHCSE 实战笔记:Keepalived 企业级高可用集群深度解析
linux·服务器·keepalived
一个人旅程~2 小时前
everything的快速搜索怎么达成?
linux·windows·电脑
xmlhcxr2 小时前
LVS(Linux virual server)
linux·运维·lvs
天上飞的粉红小猪2 小时前
数据链路层
linux·服务器·网络
2023自学中4 小时前
笔记本电脑 连接 手机WIFI,开发板网线连接笔记本,开发板 和 虚拟机 同时上网
linux·单片机·嵌入式硬件·tcp/ip
funnycoffee12310 小时前
linux系统DNS修改命令
linux·运维·服务器·linux dns
小哈里11 小时前
【工具】Linux远程开发核心工具,Git命令缩写与SSH常用命令
linux·git·ssh·工具·远程开发
夏乌_Wx11 小时前
深入理解x86内存寻址:从8086实模式到IA-32段页式映射&Linux内核实现
linux
czxyvX11 小时前
012-Linux简易Shell编写
linux