木下~Linux 系统编程笔记之管道核心知识点
·
一、进程间通信(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. 关键特性
- 通信范围限制:仅能在具有亲缘关系的进程间使用。
- 通信模式:半双工通信,拥有固定的读端和写端,同一时间只能单向传输数据。
- 文件属性:特殊的伪文件,无文件名、无文件节点,仅存在于内存中,应用层不可见;可通过
read/write文件 IO 函数操作。 - 阻塞特性:
- 读阻塞:管道无数据时,读进程会被阻塞,直到管道内有数据可读。
- 写阻塞:管道缓冲区被填满时,写进程会被阻塞,直到管道内有空闲空间可写。
- 数据结构:队列结构,遵循先进先出(FIFO) 规则,数据被读取后立即从管道中移除。
- 生命周期:随进程存在,所有持有管道文件描述符的进程退出后,管道被内核释放。
3. 标准通信流程
- 父进程调用
pipe()创建无名管道,获取读端和写端的文件描述符。 - 父进程调用
fork()创建子进程,子进程复制父进程的管道文件描述符,确保操作同一个管道。 - 通信双方关闭无用的读写端(单向通信:发送方关闭读端,接收方关闭写端),通过管道完成数据读写。
- 通信结束,双方关闭剩余的文件描述符,销毁管道。
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. 关键特性(高频考点)
- 通信范围:可用于本机内任意两个进程间通信,支持无亲缘关系进程。
- 文件属性:在文件系统中存在 FIFO 管道文件,文件大小始终为 0;数据仅存储在内存中,关闭管道后数据丢失,不占用磁盘空间。
- 操作方式:仅能通过文件 IO 函数(
open/read/write/close)操作,无法使用标准 IO 操作普通文件的方式直接读写。 - 阻塞特性:与无名管道完全一致,具备读阻塞、写阻塞特性。
- 数据结构:队列结构,遵循先进先出(FIFO)规则,数据被读取后立即移除。
- 打开规则:管道必须同时以读方式和写方式打开,才能正常完成 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. 关键使用注意事项
- 管道文件仅作为进程间通信的句柄,数据始终存储在内存中,不会写入磁盘。
- 管道内数据的生命周期:所有打开管道的进程都关闭后,管道内的数据会被清空;仅删除管道文件,正在通信的进程仍可正常读写管道内数据。
- 实现两个终端双向自由聊天,需要创建两个独立的命名管道,分别负责两个方向的数据传输。
- 管道文件不可重复创建,已存在时调用 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. 关键使用规则
- popen () 与 pclose () 必须配套使用,不可用 fclose () 关闭管道流,否则会造成资源泄漏。
- 仅能使用标准 IO 函数操作管道流,禁止使用文件 IO 的 read/write 函数。
- popen () 会自动创建子进程、管道、重定向标准输入 / 输出,无需手动处理 fork 和管道创建逻辑。
4. 补充:文件描述符重定向 dup2 ()
// 头文件
#include <unistd.h>
// 函数原型
int dup2(int oldfd, int newfd);
- 核心功能:将 newfd 文件描述符重定向到 oldfd,重定向后,对 newfd 的所有读写操作都会作用于 oldfd 对应的文件。
- 参数说明:
oldfd为被复制的源文件描述符;newfd为待重定向的目标文件描述符。 - 返回值:成功返回新的文件描述符;失败返回 - 1。
- 典型用法:将标准输出 1 重定向到文件,实现 printf 内容写入文件;将标准输入 / 输出重定向到管道,实现进程间数据交互。
更多推荐



所有评论(0)