关键词:pipe、fifo、stream socket、datagram socket、message queue、Share Memory、memory mapping、signal、semaphore。mutex、condition variable等等。
本章是后面章节的简要介绍,包括管道和FIFO;System V和POSIX IPC的消息队列、信号量、共享内存。
1. IPC工具分类
UNIX系统上各种通信和同步工具,分成三类:
- 通信:关注进程之间的数据交换。
- 同步:关注进程和线程操作之间的同步。
- 信号:尽管信号的主要作用并不在此,但在特定场景下仍然可以将它作为一种同步技术。
2. 通信工具
通信工具可以分为两类:
数据传输工具:区分这些工具的关键因素是写入和读取的概念。为了进行通信,一个进程将数据写入到IPC工具中,另一个进程从中读取数据。
这些工具要求在用户内存和内核内存之间进行两次数据传输:一次传输是在写入的时候从用户内存到内核内存,另一次传输是在读取的时候从内核内存到用户内存。
共享内存:共享内存允许进程通过将数据放到有进程共享的一块内存中已完成信息的交换。内核通过将每个进程中的页表条目指向同一个RAM分页来实现这一功能。一个进程可以通过将数据放到共享内存块中使得其他进程读取这些数据。
共享内存相比进程间通信优点是:数据不经过内核,少一次拷贝;而且不用陷入退出内核。因此速度非常快。
2.1 数据传输
字节流:管道、FIFO、以及流socket交换的数据是一个无分隔符的字节流。
消息:通过System V消息队列、POSIX消息队列以及数据报socket交换的数据是以分隔符分隔的消息。
伪终端:特殊情况下使用的通信工具。
2.2 共享内存
三种形式:System V共享内存、POSIX共享内存、内存映射。
使用共享内存注意点:
- 同步:尽管共享内存通信速度更快,但是需要对在共享内存上发生的操作进行同步。
- 安全:放入共享内存中的数据对所有共享这块内存的进程可见。
3. 同步工具
UNIX系统提供了下列同步工具:
信号量:一个信号量是一个由内核维护的整数,其实永远不会小于0。一个进程可以增加或减小一个信号量的值。如果一个进程试图将信号量的值减小到小于0,那么内核会阻塞该操作直至信号量的值增长到允许执行该操作的程度。
文件锁:文件锁是设计用来协调操作同一个文件的多个进程的动作的一种同步方法。它可以用来协调对其它共享资源的访问。文件锁分为两类:读(共享)锁和写(互斥)锁。Linux通过flock()和fcntl()系统调用来提供文件加锁工具。
互斥体和条件变量:互斥量帮助线程同步对共享资源的使用;条件变量允许线程相互通知共享变量的状态发生了变化。
4. IPC工具比较
IPC对象标识和打开对象的句柄
功能
数据传输和共享内存之间差异:
- 数据传输工具提供了读取和写入操作,传输的数据只供一个读者进程消耗。内核会自动处理读者和写者之间的流控以及同步,这样当读者试图从当前为空的工具中读取数据时将会阻塞。
- 其他应用程序设计更适合采用共享内存方式,一个进程通过共享内存能够使数据对共享同一内存区域的所有进程可见。但是同步处理会增加共享内存设计的复杂性。在需要维护共享状态的应用程序中,这个模型表现的很好。
各种数据传输工具比较:
- 一些传输工具是以字节流形式传输数据(管道、fifo、流socket),另一些则是面向消息的(消息队列和数据报socket)。
- System V和POSIX消息队列特有的一个特性是他们能够给消息赋一个数值类型或优先级,这样递送消息的顺序就可以与发送消息的顺序不同了。
- 管道、FIFO以及socket是使用文件描述符来实现的。这些IPC工具都支持以下一组I/O模型:I/O多路复用、信号驱动的I/O、以及Linux特有的epoll API。
- POSIX消息队列提供了一个通知工具,当一条消息进入了一个之前为空的队列中时可以使用它来向进程发送信号或实例化一个新线程。
- UNIX domain socket提供了一个特性允许在进程间传递文件描述符。
- UDP socket允许一个发送者向多个接收者广播或组播一条消息。
关于进程同步工具:
- 使用fcntl()加上的记录锁由加锁的进程拥有。
- 当使用fcntl()获得记录锁的进程终止之后会自动释放该记录锁。
网络通信
socket一般用于两个域中:一个是UNIX domain,它允许位于同一系统上的进程进行通信;另一个是Internet domain,它允许位于通过TCP/IP网络进行连接的不同主机上的进程进行通信。
可移植性
System V IPC可移植性要优于POSIX IPC。
System V IPC设计问题
- System V IPC工具是无连接的,没有提供引用一个打开的IPC对象的句柄的概念。System V IPC对象打开仅仅是描述进程获取一个引用该对象的句柄的简便方式。内核不会记录进程已经“打开”的该对象。这意味着内核无法维护当前使用该对象的进程的引用计数,应用程序需要额外代码管理删除工作。
- System V IPC工具的编程接口和传统UNIX I/O模型不一致。
POSIX IPC对象记录打开的引用数,简化了何时删除对象的策略;POSIX IPC提供的接口更加简单且与传统的UNIX模型也更加一致。
可访问性
权限模型控制着哪些进程能够访问对象:
- 对于一些IPC工具(FIFO和socket),对象名位于文件系统中,可访问性是根据相关的文件权限掩码来确定的。System V IPC对象不位于文件系统中,但每个对象拥有一个相关的权限掩码,其语义与文件的权限掩码类似。
- 一些IPC工具(管道、匿名内存映射)被标记成只允许相关进程访问。“相关”指通过fork()关联的,其中一个进程创建该对象,然后fork(),fork()调用的结果就是子进程会继承应用该对象的一个句柄,这样两个进程就能够共享对象了。
- POSIX未命名信号量的可访问性是通过包含该信号量的共享内存区域的可访问性来确定的。
- 为给一个文件加锁,进程必须拥有一个引用该文件的文件描述符。
- 对Internet domain socket的访问没有限制。
持久性
持久性指一个IPC工具的生命周期:
- 进程持久性:只要存在一个进程持有进程持久的IPC对象,那么该对象的生命周期就不会终止。如果所有进程都关闭了对象,那么该对象的所有内核资源都会被释放,所有未读取的数据都会被销毁。管道、FIFO以及socket是进程持久的IPC工具。
- 内和持久性:只有显式地删除内核持久的IPC对象或系统关闭时,该对象才会销毁。这种对象的生命周期与是否有进程打开该对象无关。具有内核持久性的工具包括System V IPC和POSIX IPC。
- 文件系统持久性:具备文件系统持久性的IPC对象会在系统重启的时候保持其中的信息,这种对象一直存在直至被显式地删除。基于内存映射文件的共享内存是唯一一个。
5. 总结
Linux提供通信工具包括:管道、FIFO、socket、消息队列、共享内存。
Linux提供的同步工具包括:信号量和文件锁。