一、进程间通信(IPC)基础概述

1. IPC 发展与主流分类

Linux IPC 能力继承自 UNIX 两大分支,形成了完整的通信体系:

  • UNIX 原生 IPC:无名管道、命名管道、信号
  • System V IPC:System V 消息队列、信号量集、共享内存
  • Posix IPC:Posix 消息队列、信号量集、共享内存
  • BSD 套接字(Socket):突破单机限制,实现跨主机进程通信

2. Linux 6 种核心 IPC 方式速览

通信方式 核心特点 核心适用场景
无名管道 / 命名管道 管道式字节流传输,先进先出 无名管道:亲缘进程一对一通信;命名管道:无亲缘进程一对一通信
信号 软件层模拟中断机制,异步通知 通知进程事件发生,异常 / 终止进程处理
消息队列 消息链表式存储,带权限读写控制 多进程间结构化消息传递,克服管道信号的缺点
共享内存 多进程访问同一块内存空间 多进程间大数据量高速通信,需配合同步机制
信号量集 进程 / 线程间同步与互斥手段 保护临界资源,实现进程间执行时序控制
套接字 Socket 跨主机网络通信 不同机器间的进程通信,网络编程核心

二、无名管道(Pipe)

1. 核心概述

无名管道是 Linux 最基础的 IPC 方式,本质是内核中一段固定大小的缓冲区,类比为带读写两端的水管,仅能用于具有亲缘关系的进程间通信(父子、兄弟进程),通常用于主进程向子进程下达指令、控制子进程运行。

2. 关键特性

  1. 通信范围限制:仅能在具有亲缘关系的进程间使用。
  2. 通信模式:半双工通信,拥有固定的读端和写端,同一时间只能单向传输数据。
  3. 文件属性:特殊的伪文件,无文件名、无文件节点,仅存在于内存中,应用层不可见;可通过read/write文件 IO 函数操作。
  4. 阻塞特性
    • 读阻塞:管道无数据时,读进程会被阻塞,直到管道内有数据可读。
    • 写阻塞:管道缓冲区被填满时,写进程会被阻塞,直到管道内有空闲空间可写。
  5. 数据结构:队列结构,遵循先进先出(FIFO) 规则,数据被读取后立即从管道中移除。
  6. 生命周期:随进程存在,所有持有管道文件描述符的进程退出后,管道被内核释放。

3. 标准通信流程

  1. 父进程调用pipe()创建无名管道,获取读端和写端的文件描述符。
  2. 父进程调用fork()创建子进程,子进程复制父进程的管道文件描述符,确保操作同一个管道。
  3. 通信双方关闭无用的读写端(单向通信:发送方关闭读端,接收方关闭写端),通过管道完成数据读写。
  4. 通信结束,双方关闭剩余的文件描述符,销毁管道。

4. 核心 API

// 头文件
#include <unistd.h>
// 函数原型
int pipe(int pipefd[2]);
  • 核心功能:创建一个无名管道,通过参数返回读写两端的文件描述符。
  • 参数说明int pipefd[2] 为数组首地址,其中pipefd[0]是管道的读端pipefd[1]是管道的写端
  • 返回值:成功返回 0;失败返回 - 1,并设置 errno。

三、命名管道(FIFO)

1. 核心概述

命名管道又称 FIFO 文件,是无名管道的升级版本,突破了亲缘进程的通信限制,可实现任意两个无亲缘关系的进程间通信。它在文件系统中拥有专属的管道文件和 inode 节点,但数据仍存储在内存中,不占用磁盘空间。

  • 命令行创建:mkfifo 管道文件名
  • 代码创建:mkfifo()函数

2. 关键特性(高频考点)

  1. 通信范围:可用于本机内任意两个进程间通信,支持无亲缘关系进程。
  2. 文件属性:在文件系统中存在 FIFO 管道文件,文件大小始终为 0;数据仅存储在内存中,关闭管道后数据丢失,不占用磁盘空间。
  3. 操作方式:仅能通过文件 IO 函数(open/read/write/close)操作,无法使用标准 IO 操作普通文件的方式直接读写。
  4. 阻塞特性:与无名管道完全一致,具备读阻塞、写阻塞特性。
  5. 数据结构:队列结构,遵循先进先出(FIFO)规则,数据被读取后立即移除。
  6. 打开规则:管道必须同时以读方式和写方式打开,才能正常完成 open 操作;单独以只读 O_RDONLY 或只写 O_WRONLY 打开时,进程会阻塞,直到另一端以对应模式打开。
    • O_RDONLY:只读模式打开,作为管道读端
    • O_WRONLY:只写模式打开,作为管道写端
    • O_RDWR:读写模式打开,不会触发打开阻塞

3. 核心 API

3.1 创建管道文件 mkfifo ()
// 头文件
#include <sys/types.h>
#include <sys/stat.h>
// 函数原型
int mkfifo(const char *pathname, mode_t mode);
  • 核心功能:创建命名管道文件,并设置文件权限。
  • 参数说明
    • pathname:待创建的管道文件路径 + 文件名,无路径则默认当前目录。
    • mode:管道文件权限,最终权限 = mode & ~umask,通常填八进制0666
  • 返回值:成功返回 0;失败返回 - 1,管道已存在时也会报错。
3.2 删除管道 / 文件
// 头文件
#include <unistd.h>
int unlink(const char *pathname); // 仅删除文件,管道文件专用

// 头文件
#include <stdio.h>
int remove(const char *pathname); // 可删除文件/空文件夹,功能等价于rm命令
  • 核心功能:删除指定路径的管道文件 / 普通文件 / 空目录。
  • 返回值:成功返回 0;失败返回 - 1。

4. 关键使用注意事项

  1. 管道文件仅作为进程间通信的句柄,数据始终存储在内存中,不会写入磁盘。
  2. 管道内数据的生命周期:所有打开管道的进程都关闭后,管道内的数据会被清空;仅删除管道文件,正在通信的进程仍可正常读写管道内数据。
  3. 实现两个终端双向自由聊天,需要创建两个独立的命名管道,分别负责两个方向的数据传输。
  4. 管道文件不可重复创建,已存在时调用 mkfifo 会报错,代码中需做错误兼容。

5. 无名管道与命名管道的异同对比

表格

对比维度 无名管道(Pipe) 命名管道(FIFO)
相同点 1. 数据均存储在内存中,不占用磁盘空间;2. 均通过文件 IO 函数 read/write 操作;3. 均遵循 FIFO 先进先出队列结构;4. 均具备读阻塞、写阻塞特性;5. 均为半双工通信模式 同左
通信范围 仅支持亲缘关系进程间通信 支持本机内任意进程间通信,包括无亲缘进程
文件属性 无文件名、无文件 inode 节点,仅存在于内存 有专属管道文件和 inode 节点,存在于文件系统中,文件大小始终为 0
创建方式 仅能通过 pipe () 函数创建 可通过 mkfifo () 函数或 mkfifo 命令创建
生命周期 随创建管道的进程退出而销毁 管道文件持久化存在于文件系统,除非主动删除

四、标准流管道

1. 核心概述

标准流管道封装了无名管道的创建、打开、进程替换、重定向全流程,将管道操作与标准 IO 绑定,通过文件流指针操作管道,简化了代码开发。核心由popen()pclose()两个函数实现,必须使用标准 IO 函数(fread/fwrite/fgets/fputs)操作,不能使用 read/write 文件 IO

2. 核心 API

2.1 创建并打开标准流管道 popen ()
// 头文件
#include <stdio.h>
// 函数原型
FILE *popen(const char *command, const char *type);
  • 核心功能:创建子进程执行 shell 命令,同时创建无名管道建立与子进程的通信,返回管道的标准 IO 流指针。
  • 参数说明
    • command:子进程中要执行的 shell 命令,与终端执行命令一致。
    • type:管道操作模式,仅支持两种:
      • "r":返回的流指针指向管道读端,可读取子进程命令的标准输出。
      • "w":返回的流指针指向管道写端,写入的数据会作为子进程命令的标准输入。
  • 返回值:成功返回 FILE * 文件流指针;失败返回 NULL。
2.2 关闭标准流管道 pclose ()
// 头文件
#include <stdio.h>
// 函数原型
int pclose(FILE *stream);
  • 核心功能:关闭管道流指针,等待子进程执行结束,回收相关资源。
  • 参数说明stream为 popen () 返回的文件流指针。
  • 返回值:成功返回子进程的退出状态;失败返回 - 1。

3. 关键使用规则

  1. popen () 与 pclose () 必须配套使用,不可用 fclose () 关闭管道流,否则会造成资源泄漏。
  2. 仅能使用标准 IO 函数操作管道流,禁止使用文件 IO 的 read/write 函数。
  3. popen () 会自动创建子进程、管道、重定向标准输入 / 输出,无需手动处理 fork 和管道创建逻辑。

4. 补充:文件描述符重定向 dup2 ()

// 头文件
#include <unistd.h>
// 函数原型
int dup2(int oldfd, int newfd);
  • 核心功能:将 newfd 文件描述符重定向到 oldfd,重定向后,对 newfd 的所有读写操作都会作用于 oldfd 对应的文件。
  • 参数说明oldfd为被复制的源文件描述符;newfd为待重定向的目标文件描述符。
  • 返回值:成功返回新的文件描述符;失败返回 - 1。
  • 典型用法:将标准输出 1 重定向到文件,实现 printf 内容写入文件;将标准输入 / 输出重定向到管道,实现进程间数据交互。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐