进程间通信

进程的地址空间相互隔离,这是安全的代价——想交换数据,就必须借助操作系统提供的通道,这就是进程间通信(IPC)。线程因共享内存而不需要 IPC,多进程协作则全靠它。

为什么需要 IPC

典型场景:浏览器主进程与渲染进程分工;生产者把数据交给消费者;多个服务进程汇总统计结果。不同 IPC 在"速度、是否跨机器、能否携带结构化数据"上差异巨大,选错代价不小。

管道(Pipe)

管道是最古老的 IPC:数据像水流一样单向流过内核缓冲区,一端写、一端读。匿名管道只能用于有亲缘关系的进程(父子进程),典型应用就是 shell 命令拼接:

# 把前一个命令的输出作为后一个命令的输入
cat access.log | grep "ERROR" | wc -l
# 输出:42

无关进程之间用命名管道(FIFO):通过文件系统中的一个名字建立联系,先创建,再各自以读写方式打开使用。

消息队列与共享内存

消息队列:内核维护一个消息链表,进程按类型"投递/取走"消息,读走即删除、自带边界。相比管道,双方不必严格同步读写节奏。

共享内存:把同一块物理内存映射到多个进程的地址空间,读写直接命中内存,是速度最快的 IPC;缺点是需要自己解决互斥与同步。

// 片段:需包含头文件并处理返回值后才能运行
int shmid = shmget(KEY, 1024, IPC_CREAT);  // 申请 1024 字节共享内存
char *ptr = shmat(shmid, NULL, 0);         // 映射到本进程地址空间
strcpy(ptr, "hello");                      // 直接写入
shmdt(ptr);                                // 解除映射
// 输出:无;另一进程 shmat 同一 KEY 后即可读到 "hello"

两者常配合使用:共享内存负责"高速搬数据",信号量负责"不让多个进程同时写"。

信号、信号量与 Socket

  • 信号(signal):异步通知机制,不携带业务数据。如 Ctrl+C 给进程发 SIGINT、kill -9 发 SIGKILL。
  • 信号量(semaphore):本身不传数据,只是一个计数器,用于互斥与同步(如限制同时访问某个资源的进程数)。
  • Socket:既能本地通信(Unix Domain Socket),也能跨主机通信(TCP/UDP)——它是唯一能"跨机器"的 IPC,网络通信本质上也是一种 IPC。

IPC 对比速查

方式速度跨机器数据形态典型场景
管道字节流shell 命令拼接
消息队列结构化消息解耦、异步
共享内存最快任意数据高性能数据交换
信号几乎无事件通知
Socket字节流网络服务

小结:选 IPC 的口诀是——"要快用共享内存,要解耦用消息队列,要通知用信号,要跨机器用 Socket"。再补一句"共享内存必须配合信号量或锁使用",这条知识点就闭环了。

笔记加载中…