进程间通信(IPC,Inter-Process Communication)是后端面试的高频考点。因为进程内存隔离,要交换数据就得走专门的机制。这篇文章把四种核心 IPC 方式一次讲清楚。

管道(Pipe)

最简单的 IPC,本质是内核维护的一块缓冲区,单向流动(半双工)。

三个关键概念:

内核维护: 管道这块内存不是程序自己分配的,是调用 pipe() 系统调用后,操作系统在内核空间里开辟的。程序没法直接访问,只能通过 read()/write() 系统调用来读写。

缓冲区: 管道不是实时传输,有一块中间存储区。写进去的数据先放在这里,读端什么时候来取都行。这块缓冲区有大小限制(Linux 默认 64KB),写满了写端会阻塞。

单向流动: 数据只能从写端流向读端,不能反向。创建管道时得到两个文件描述符:fd[0](读端)和 fd[1](写端)。双向通信需要创建两个管道。

分类:

  • 匿名管道: 只能用于有亲缘关系的进程(父子进程)
  • 命名管道(FIFO): 有文件路径,任意进程都能打开

适合场景: 简单的父子进程通信、流式数据传输。shell 里的 | 就是匿名管道。


消息队列(Message Queue)

内核维护的消息链表,进程可以往里放消息、取消息。

和管道的核心区别:

管道 消息队列
消息边界 无(字节流,可能粘包) 有(每条消息独立)
读取方式 只能顺序读 可按消息类型选择性读取
生命周期 依附于进程 内核对象,进程退出后仍存在

安全隐患: 消息队列是内核级共享资源,如果创建时权限设得太宽松(如 0666),同机器上任意进程都能读写。防范方式:收紧权限(0600)、消息体加签名验证、或换用 Unix Domain Socket。

适合场景: 需要消息分类、异步解耦、进程重启后继续处理的场景。


共享内存(Shared Memory)

最快的 IPC 方式。两个进程把同一块物理内存映射到各自的虚拟地址空间,直接读写,不需要数据拷贝。

  • 速度最快(零拷贝,不经过内核)
  • 本身没有同步机制,需要配合信号量或互斥锁防止并发冲突
  • 适合大数据量传输

类比: 两个工人共用一块白板,谁都能写,但要约定好谁在写的时候另一个不能动。


信号量(Semaphore)

严格来说不是用来传数据的,而是用来做同步和互斥的。

本质是一个计数器,支持两个原子操作:

  • P 操作(wait): 计数器 -1,如果 < 0 就阻塞
  • V 操作(signal): 计数器 +1,如果有等待的进程就唤醒

二值信号量(值只有 0/1) 就是互斥锁,用来保护临界区。计数信号量 用来控制并发数量。

常见搭配: 共享内存 + 信号量,共享内存负责传数据,信号量负责协调访问顺序。


其他 IPC 方式

方式 特点
信号(Signal) 异步通知,只能传信号编号,不能传数据(如 kill -9)
Socket 可跨机器,本地也能用(Unix Domain Socket),最通用
内存映射文件(mmap) 类似共享内存,但基于文件,进程重启数据不丢

面试答题思路

问”进程间通信有哪些方式”,答完列举后,面试官通常会追问:

  • 管道和消息队列的区别? → 有无消息边界、能否随机读取、生命周期
  • 共享内存为什么最快? → 零拷贝,直接操作内存,不经过内核
  • 信号量和互斥锁的区别? → 信号量可以跨进程,互斥锁通常在同一进程内的线程间用
  • 消息队列有安全问题吗? → 有,权限控制不严格时其他进程可以读写,需要收紧权限或加消息认证

理解 IPC 的本质,再去看分布式系统里的消息队列(Kafka、RocketMQ)、共享缓存(Redis)等,会发现思路是一脉相承的——都是在解决”不同执行单元之间如何安全高效地交换数据”这个问题。