Linux/Unix 进程间通信IPC

1 IPC 工具分类

图43-1总结了UNIX系统上各种通信和同步工具,并根据功能将它们分成了三类。

  • 通信:这些工具关注进程之间的数据交换。
  • 同步:这些进程关注进程和线程操作之间的同步。
  • 信号:尽管信号的主要作用并不在此,但在特定场景下仍然可以将它作为一种同步技术。更罕见的是信号还可以作为一种通信技术:信号编号本身是一种形式的信息, 并且可以在实时信号上绑定数据(一个整数或指针)

尽管其中一些工具关注的是同步,但通用术语进程间通信(IPC) 通常指代所有这些工具。

 

2 通信工具

图43-1中列出的各种通信工具允许进程间相互交换数据。(这些工具还可以用来在同一个进程中不同线程之间交换数据,但很少需要这样做,因为线程之间可以通过共享全局变量来交互。

通信工具分为两类:

  • 数据传输工具:区分这些工具的关键因素是写入和读取的概念。为了进行通信,个进程将数据写入到IPC工具中,另一进程从中读取数据。这些工具要求在用户内存和内核内存之间进行两次数据传输:一次传输是在写入的时候从用户内存到内核内存,另一次传输是在读取的时候从内核内存到用户内存。
  • 共享内存:共享内存允许进程通过将数据放到由进程间共享的一块内存中以完成信息的交换。(内核通过将每个进程中的页表条目指向同一个RAM分页来实现这一功能)一个进程可以通过将数据放到共享内存块中使得其他进程读取这些数据。由于通信无需系统调用以及用户内存和内核内存之间的数据传输,因此共享内存的速度非常快。

可以进一步将数据传输工具分成下列类别:

  • 字节流:通过管道、FIFO以及数据报socket交换的数据是一个无分隔符的字节流。每个读取操作可能会从IPC工具中读取任意数量的字节,不管写者写入的块的大小是什么。
  • 消息:通过SystemV消息队列、POSIX消息队列以及数据报socket交换的数据是以分隔符分隔的消息。每个读取操作读取由写者写入的一整条消息,无法只读取部分消息,而把剩余部分留在IPC工具中,也无法在一个读取操作中读取多条消息。
  • 伪终端:伪终端是一种在特殊情况下使用的通信工具,在64章将会介绍有关伪终端的详细信息。

数据传输工具和共享内存之间的差别包括以下几个方面。

  • 尽管一个数据传输工具可能会有多个读取者,但读取操作是具有破坏性的。读取操作会消耗数据,其他进程将无法获取所消耗的数据。在socket中可以使用MSG_PEEK标记来执行非破坏性读取(参见61.3节)。UDP socket 允许将一条消息广播或组播到多个接收者处(参见61.12节)。
  • 读取者和写者进程之间的同步是原子的。如果一个读取者试图从一个当前不包含数据的数据传输工具中读取数据,那么在默认情况下读取操作会被阻塞直至一些进程向该工具写入了数据。

3 同步工具

通过同步可以防止进程执行诸如同时更新一块共享内存或同时更新文件的同一个数据块之类的操作。

UNIX系统提供了下列同步工具。

  • 信号量: 一个信号量是一个由内核维护的整数,其值永远不会小于0。一个进程可以增加或减小一个信号量的值。如果一个进程试图将信号量的值减小到小于0,那么内核会阻塞该操作直至信号量的值增长到允许执行该操作的程度。(或者进程可以要求执行一个非阻塞操作,那么就不会发生阻塞,内核会让该操作立即返回并返回一个标示无法立即执行该操作的错误。)最常用的信号量是二元信号量:一个值只能是0或1的信号量,但处理一类共享资源拥有多个实例的应用程序需要使用最大值等于共享资源数量的信号量。
  • 文件锁: 文件锁是设计用来协调操作同一件的多个进程的动作的一种同步方法。文件锁分为两类:读(共享)锁和写(互斥)锁。Linux通过flock()和fcntl()系统调用来提供文件加锁工具。flock()系统调用提供了一种简单的加锁机制,允许进程将一个共享或互斥锁加到整个文件上。由于功能有限,现在已经很少使用flock()这个加锁工具了。fcntl()系统调用提供了记录加锁,允许进程在同一文件的不同区域上加上多个读锁和写锁。
  • 互斥体和条件变量:这些同步工具通常用于POSIX线程,第30章对此进行了介绍。

在执行进程间同步时通常需要根据功能需求来选择工具。当协调对文件的访问时文件记录加锁通常是最佳的选择,而对于协调对其他共享资源的访问来讲,信号量通常是更佳的选择。

4 IPC工具比较

下面介绍在确定选择何种IPC工具时通常需要考虑的事项。

1. IPC对象标识和打开对象的句柄

要访问一个IPC对象,进程必须要通过某种方式来标识出该对象,一旦将对象“打开”之后,进程必须要使用某种句柄来引用该打开着的对象。表43-1对各种类型的IPC工具的属性进行了总结。

功能

各种IPC工具在功能上是存在差异的,因此在确定使用何种工具时需要考虑这些差异。

  • 数据传输工具提供了读取和写入操作,传输的数据只供一个读者进程消耗。内核会自动处理读者和写者之间的流控以及同步(这样当读者试图从当前为空的工具中读取数据时将会阻塞)。在很多应用程序设计中,这个模型都表现得很好。
  • 其他应用程序设计则更适合采用共享内存的方式。共享内存需要的同步处理(可能还会有流控)会增加共享内存设计的复杂性。在需要维护共享状态(如共享数据结构)的应用程序中,这个模型表现得很好。

关于各种数据传输工具,下 面几点是值得注意的。

  • 一些数据传输工具以字节流的形式传输数据(管道、FIFO 以及流socket),另一些则是面向消息的(消息队列和数据报socket)。到底选择何种方法则需要依赖于应用程序。(应用程序也可以在一个字节流工具上应用面向消息的模型,这可以通过使用分隔字符、固定长度的消息,或对整条消息长度进行编码的消息头来实现,具体可参考44.8节)。
  • 与其他数据传输工具相比,SystemV和POSIX消息队列特有的一个特性是它们能够给消息赋一个数值类型或优先级,这样递送消息的顺序就可以与发送消息的顺序不同了。
  • 管道、FIFO 以及socket是使用文件描述符来实现的。这些IPC工具都支持 I/O 多路复用( select()和poll()系统调用)、信号驱动的I/O、以及Linux特有的epoll API。这些技术的主要优势在于它们允许应用程序同时监控多个文件描述符以判断是否可以在某些文件描述符上执行I/O操作。与之相比,System V消息队列没有使用文件描述符,因此并不支持这些技术。
  • POSIX消息队列提供了一个通知工具,当一条消息进入了一个之前为空的队列中时可以使用它来向进程发送信号或实例化一个新线程。
  • UNIX domain socket提供了一个特性:允许在进程间传递文件描述符。这样一个进程就能够打开一个文件并使之对另一个本来无法访问该文件的进程可用。
  • UDP (Internet domain datagram) socket 允许一个发送者向多个接收者广播或组播一条消息

关于进程同步工具,下面几点是值得注意的。

  • 使用fcnt()加上的记录锁由加锁的进程拥有。内核使用这种所有权属性来检测死锁。如果发生了死锁,那么内核会拒绝其中一个进程的加锁请求,因此会在fcntl()调用中返回一个错误标示出死锁的发生。SystemV和POSIX信号量并没有所有权属性,因此内核不会为信号量进行死锁检测。
  • 当使用fcnt()获得记录锁的进程终止之后会自动释放该记录锁。SystemV信号量提供了一个类似的特性,即“撤销”特性,但这个特性仅在部分场景中可靠(参见47.8节)。POSIX信号量并没有提供类似的特性。

网络通信

在图43-1中给出所有IPC方法中,只有socket允许进程通过网络来通信。socket一般用于两个域中:一个是UNIX domain,它允许位于同一系统上的进程进行通信;另一个是Internetdomain,它允许位于通过TCP/IP网络进行连接的不同主机上的进程进行通信。

可移植性

从可移植性的角度来看,System V IPC要优于POSIX IPC。

但是System V IPC是复杂的,例如:

    • System V IPC工具是无连接的,它们没有提供引用一个打开的IPC对象的句柄(类似于文件描述符)的概念。这意味着内核无法维护当前使用该对象的进程的引用计数,其结果是应用程序需要使用额外的代码来知道何时可以安全地删除一个对象。
    • System V IPC 工具的编程接口 与传统的UNIX I/O模型是不一致的(它们使用整数键值和IPC标识符,而不是路径名和文件描述符),并且这个编程接口也过于复杂了。

相反,内核会为POSIX IPC对象记录打开的引用数,这样就简化了何时删除对象的决策。此外,POSIX IPC提供的接口更加简单并且与传统的UNIX模型也更加一致。

可访问性和持久性

可访问性:权限模型控制着哪些进程能够访问对象。一些IPC工具(管道、匿名内存映射)被标记成只允许相关进程访问。这里“相关”指通过fork()关联的。

术语持久性是指一个IPC工具的生命周期。持久性有三种。

  • 进程持久性:只要存在一个进程持有进程持久的IPC对象,那么该对象的生命周期就不会终止。如果所有进程都关闭了对象,那么与该对象的所有内核资源都会被释放,所有未读取的数据会被销毁。管道、FIFO 以及socket是进程持久的IPC工具。
  • 内核持久性:只有当显式地删除内核持久的IPC对象或系统关闭时,该对象才会销毁。这意味着一个进程可以创建一个对象,向其中写入数据,然后关闭该对象(或终止)。在后面某个时刻,另一个进程可以打开该对象,然后从中读取数据。
  • 文件系统持久性:具备文件系统持久性的IPC对象会在系统重启的时候保持其中的信息,这种对象一直存在直至被显式地删除。唯一种具备文件系统持久性的IPC对象是基于内存映射文件的共享内存。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章