Unix 进程间通信

总结Unix下进程间通信的几种方式及其特点

前言

进程同步和进程通信的区别

关于这个问题,网络和很多书籍上把这两者混为一谈,对于这两者的区别,说法也有很多种。

其中一种说法是:

进程互斥、同步与通信的关系:进程竞争资源时要实施互斥,互斥是一种特殊的同步,实质上需要解决好进程同步问题,进程同步是一种进程通信,由此看来,进程互斥、同步都可以看做进程的通信

个人比较认同是以下这种说法:

  • 进程同步:控制多个进程按一定顺序执行
  • 进程通信:进程间传输信息

进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。

在进程同步中介绍的信号量也属于进程通信的一种方式,但是属于低级别的进程通信,因为它传输的信息非常小。

消息传递与共享内存

消息传递提供一种机制以允许进程不必通过共享地址空间来实现通信和同步,消息传递工具提供至少两种操作:发送(消息)和接收(消息)。由进程发送的消息可以是定长的或变长的。

操作系统提供用于通信的通道,进程通过读写这个通道进行通信。

Unix中基于消息传递实现的IPC有:管道、FIFO、消息队列以及网络间的Socket通信

共享内存的进程间通信需要通信进程建立共享内存区域。通常,一块共享内存区域驻留在生成共享内存段进程的地址空间。其他希望使用这个共享内存段进行通信的进程必须将此放到它们自己的地址空间上。

管道

管道用于有亲缘关系的两个进程(父子进程)进行通信,写进程在管道的尾端写入数据,读进程在管道的首端读出数据。管道提供了简单的流控制机制,进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。

通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。

管道存在以下这些局限性:

  1. 历史上,它们是半双工的(即数据只能在一个方向上流动)。现在,某些系统提供全双工管道,但是为了最佳的可移植性,我们决不应预先假定系统支持全双工管道
  2. 管道只能在具有公共祖先的两个进程之间使用。

在Unix中管道是通过调用pipe函数创建的。对于一个从子进程到父进程的管道,父进程关闭fd[1],子进程关闭fd[0]。

FIFO

FIFO有时被称为命名管道。未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的创建了它们的祖先进程。但是,通过FIFO,不相关的进程也能交换数据。

在Unix中使用mkfifo或者mkfifoat创建FIFO,使用open打开,使用正常文件的I/O函数对FIFO进行读写操作实现进程间通信的目的。

使用FIFO实现两进程间通信的例子:

消息队列

消息队列是消息的链接表,存储在内核中,由消息队列标识符标识。

在Unix中使用msgget用于创建一个新队列或打开一个现有队列、msgsnd将新消息添加到队列尾端。mmsgrcv用于从队列中取消息。我们并不定要以先进先出次序取消息,也可以按消息的类型字段取消息。
每个消息包含一个正的长整型类型的字段、一个非负的长度以及实际数据字节数(对应于长度),所有这些都在将消息添加到队列时,传送给 msgsna。

消息队列相比于其他方式有很多优点:它提供有格式的字节流,减少了开发人员的工作量;消息具有类型(system V)或优先级(posix)

Socket

Socket 是网络进程间通信的一种,我们将描述套接字网络进程间通信接口,进程用该接口能够和其他进程通信,无论它们是在同一台计算机上还是在不同的计算机上。实际上,这正是套接字接口的设计目标之一。

Socket自身携带同步机制,不需要额外的方式来辅助实现同步。

在Unix中使用socketbindconnectlistenaccept等一系列系统调用建立连接并进行通信。

信号量

信号量是一个计数器,用于多进程提供对共享数据对象的访问。

为了获得共享资源,进程需要执行下列操作。

  1. 测试控制该资源的信号量
  2. 若此信号量的值为正,则进程可以使用该资源。在这种情况下,进程会将信号量值减1,表示它使用了一个资源单位。
  3. 否则,若此信号量的值为0,则进程进入休眠状态,直至信号量值大于0。进程被唤醒后,它返回至步骤(1)。

当进程不再使用由一个信号量控制的共享资源时,该信号量值增1。如果有进程正在休眠等待此信号量,则唤醒它们。

为了正确地实现信号量,信号量值的测试及减1操作应当是原子操作。为此,信号量通常是在内核中实现的。

因为只能传递简单的数据,所以信号量常常只是用来进行进程间的同步。

共享内存

共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。

不过使用共享内存的时候要注意保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全。

总结

方式 优点 缺点
管道 创建简单 容量有限、速度慢、只能用于父子进程通讯、半双工通信方式
FIFO 克服了管道没有名字的限制、允许无亲缘关系进程间的通信 速度慢
消息队列 承载信息量比较多、能够承载有格式的字节流 容量受到系统限制
Socket 能够实现网络间进程的通信 实现比较复杂
信号量 能够比较好地解决同步问题 传递的信息较少,只能用于同步
共享内存 速度快 要解决线程安全的问题

参考