Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

进程间通信

复习

  • 多级反馈队列:不知道任务长短时,靠反馈自动调整优先级
  • 多核调度:任务分到多个 CPU 后还要处理负载和缓存亲和
  • 线程与并发:一个进程内可以有多条共享内存的执行线

TL;DR

  • 进程之间内存隔离,互相看不到对方的数据
  • 要交换数据,得借助内核提供的专门通道
  • 常见方式:管道、消息、信号、共享内存
  • 各有取舍:共享内存最快,但要自己处理同步

正文

  上一章说,同一进程里的线程共享内存,交换数据很方便。可进程与进程之间是隔离的——这也是它们的优点:一个进程出错,不会直接改坏另一个进程的数据。

  但隔离也带来麻烦:两个进程想交换数据,怎么办?

为什么不能直接读写对方内存

  每个进程都有自己独立的地址空间(后面会专门讲)。进程 A 眼里的地址 1000,和进程 B 眼里的地址 1000,很可能对应完全不同的物理内存。A 也没法凭一个地址,去访问 B 的数据。

  所以,进程间通信(IPC,Inter-Process Communication)必须借助内核:由内核提供一条双方都能用的通道,数据经由这条通道传递。

几种常见方式

  内核提供的通道有好几种,各有各的脾气:

  • 管道(pipe):一条单向的字节流,像一根水管。一端往里写,另一端从里读。适合把“前一个程序的输出”接到“后一个程序的输入”。
  • 消息队列:一条条有边界的“消息”。每发一条,对方就收到一条,能保留消息之间的分界。
  • 信号(signal):一个简短的通知,用来“打断”对方,比如告诉它“该结束了”。它只传递很少的信息。
  • 共享内存:由内核划出一块内存,让两个进程都能访问。数据不走内核中转,速度最快;但正因为大家都能直接读写,必须另外配一套同步机制,否则会互相踩脚。

  前三种都要经过内核搬运或转发;共享内存则让双方直接读写同一块地方,省去了搬运。

怎么选

方式传递什么速度同步难度
管道字节流一般
消息队列有边界的消息一般
信号简短通知
共享内存任意数据最快

  一句话:想省事就用管道或消息,想最快就用共享内存,但要额外操心同步。 怎么在这两者之间取平衡,正是接下来几章要展开的。

  而“共享内存要同步”这件事,其实不只发生在进程之间——同一进程里的多个线程共享内存,也会撞上同样的麻烦。下一章,我们从最简单的一个例子看起:两个线程同时给一个数加一,为什么会加错?

思考题

  既然共享内存最快,为什么不让所有进程通信都用共享内存?多出来的是哪部分工作?

小结

知识点

  • 进程地址空间互相隔离,无法直接访问对方数据
  • 进程间通信需要内核提供通道
  • 管道、消息、信号、共享内存各有取舍
  • 共享内存最快,但需要额外的同步

参考资料

  1. Wikipedia(zh):进程间通信:IPC 的各种方式
  2. Wikipedia(zh):管道:管道
  3. Wikipedia(zh):共享内存:共享内存

思考题答案(仅供参考)

  因为共享内存虽然快,却把“协调”这件事全丢给了使用者:多个进程同时读写同一块内存,必须自己保证它们不会互相覆盖、不会读到写了一半的数据。这就需要锁、信号量等一整套同步机制,写起来容易出错。管道和消息由内核替你把数据搬好、把边界划清,省心得多。所以选哪种,要看更在意速度还是更在意省事——工具没有绝对的好坏,只有是否合适。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪