在 Linux 系统编程与运维面试中,进程间通信(IPC)是出现频率极高的一类考点。其中,管道(pipe)、命名管道(FIFO) 和 socketpair 这三者因为外形相似、都能传递字节流,经常被面试官放在一起对比。很多候选人能说出“管道用于亲缘进程,FIFO 用于任意进程”,但一旦被追问“socketpair 和管道到底差在哪”“什么时候必须用 socketpair”,回答就开始模糊。本文从内核实现、使用限制和实战场景三个维度,把这三者彻底讲清楚。
一、先给结论:三者的本质定位
| 特性 | 管道 pipe | 命名管道 FIFO | socketpair |
|---|---|---|---|
| 通信方向 | 半双工(单工) | 半双工(单工) | 全双工 |
| 进程关系 | 必须有亲缘关系 | 任意进程 | 必须有亲缘关系 |
| 是否有名字 | 无 | 有(文件系统路径) | 无 |
| 底层实现 | pipefs 匿名 inode | pipefs 命名 inode | Unix domain socket |
| 是否支持多路复用 | 否 | 否 | 是(select/poll/epoll) |
| 能否传递文件描述符 | 否 | 否 | 是(SCM_RIGHTS) |
| 典型 API | pipe() |
mkfifo() + open() |
socketpair() |
一句话概括:管道和 FIFO 本质是同一个东西,区别只在于有没有名字;socketpair 则是“成对的 Unix 域套接字”,能力上是前两者的超集。
二、管道 pipe:最轻量的亲缘通信
pipe() 创建一个匿名管道,返回两个文件描述符:fd[0] 读端、fd[1] 写端。内核会为它分配一个 pipe_inode_info 结构,挂载在 pipefs 上,用户态看不到任何路径。
它的核心限制有三个:
- 只能用于有亲缘关系的进程。因为匿名管道没有名字,其他进程无法通过路径找到它,只能靠
fork()继承文件描述符。 - 半双工。数据只能从写端流向读端。如果父子进程要双向通信,必须创建两根管道,分别负责两个方向。
- 不支持多路复用和辅助数据。你没法用
epoll监听管道,也没法通过它传文件描述符。
经典用法是 shell 里的 ls | grep txt,以及父子进程间传递简单命令或结果。如果只是单向传数据、进程又是父子关系,管道是开销最小的选择——它不经过 socket 层,内核路径最短。
三、命名管道 FIFO:让无关进程也能连上
FIFO 通过 mkfifo() 在文件系统中创建一个特殊文件,之后任何进程只要 open() 这个路径,就能读写。它解决了管道的“匿名”问题,因此可以用于任意两个进程,甚至跨会话、跨用户(受权限控制)。
但要注意几个面试常问的坑:
- 打开阻塞:以只读方式
open()一个 FIFO 会阻塞,直到有进程以写方式打开;反之亦然。除非使用O_NONBLOCK。 - 依然是半双工。FIFO 没有改变管道的内核本质,双向通信仍需两个 FIFO。
- 数据是字节流,没有消息边界。写入端写 100 字节,读端可能分多次读到,不能假设一次 read 对应一次 write。
FIFO 的典型场景是:一个常驻服务进程创建 FIFO,客户端进程往里面写请求;或者日志采集程序通过 FIFO 接收其他进程的输出。它比 socket 简单,比管道灵活,适合“无关进程、单向、低频率”的通信。
四、socketpair:被低估的全能选手
socketpair(AF_UNIX, SOCK_STREAM, 0, sv) 会创建一对已连接的 Unix 域套接字,sv[0] 和 sv[1] 互为对端,双方都可以读也可以写,天然全双工。
它和管道的核心差异在于:
- 全双工:一根 socketpair 就能双向通信,不需要创建两根。
- 支持多路复用:socketpair 返回的是真正的 socket fd,可以用
select、poll、epoll监听,这在高并发事件循环里非常关键。 - 支持辅助数据:可以通过
sendmsg+SCM_RIGHTS传递文件描述符,这是管道和 FIFO 完全做不到的。 - 支持消息边界:如果使用
SOCK_SEQPACKET或SOCK_DGRAM,还能保留消息边界,而管道是纯字节流。
限制是它同样只能用于亲缘进程,因为 socketpair 没有文件系统路径。不过父子进程 fork 后各自持有一个 fd,就能双向通信。
实战中 socketpair 的经典用法包括:
- 主进程与 worker 进程之间的控制通道:主进程用 epoll 监听 socketpair 的一端,worker 通过另一端上报状态、请求任务。
- 传递文件描述符:比如一个进程打开日志文件或网络连接,通过 socketpair 把 fd 传给另一个进程处理。
- 信号处理的替代方案:在自管道(self-pipe)技巧中,用 socketpair 比 pipe 更好,因为可以同时读写且支持 epoll。
五、实战选择:一张决策清单
面试时如果被问到“你会怎么选”,可以按下面的顺序判断:
- 需要双向通信吗? 需要 → 优先 socketpair;不需要 → 继续。
- 进程之间有亲缘关系吗? 有 → 管道足够;没有 → FIFO。
- 需要 epoll/select 监听吗? 需要 → socketpair(管道虽可被 poll,但能力受限,且无法传 fd)。
- 需要传递文件描述符或凭证吗? 需要 → 只能 socketpair。
- 只是父子间传简单数据、追求极低开销? → 管道。
- 无关进程、低频单向通信? → FIFO。
补充一个容易忽略的点:性能上管道通常略优于 socketpair,因为少了 socket 层的协议处理。但在现代内核中差距很小,除非是极端高频场景,否则不应以性能为由放弃 socketpair 的功能优势。
六、面试高频追问
- 管道写满会怎样? 写端阻塞,直到读端消费。管道缓冲区默认 64KB(可调)。
- 读端关闭后写会怎样? 进程收到
SIGPIPE,默认终止;忽略信号则write返回EPIPE。 - socketpair 能跨主机吗? 不能,它是
AF_UNIX,仅限本机。跨主机要用 TCP socket。 - FIFO 和普通文件的区别? FIFO 数据不落盘,读走即消失;普通文件持久化。
总结
管道、FIFO、socketpair 三者同源但定位不同:管道是亲缘进程间最轻量的单向通道,FIFO 把管道的能力扩展到任意进程,socketpair 则在亲缘进程间提供了全双工、可多路复用、可传 fd 的完整能力。面试中只要抓住“方向、关系、是否可监听、是否可传 fd”这四个维度,就能给出清晰且有说服力的选择理由。
未经允许不得转载:任鹏个人博客 » Linux 面试题:管道、命名管道和 socketpair 的区别与实战选择

