APUE笔记:Advanced IPC(十七)
本文记录《UNIX环境高级编程》第3版 第17章 Advanced IPC 的一些知识点。
UNIX Domain Sockets
UNIX域套接字用于与运行在同一台机器上的进程进行通信。尽管因特网域套接字也能用于相同的目的,但UNIX域套接字的效率更高。UNIX域套接字只进行数据复制,它们无需执行协议处理,无需添加或移除网络头部,无需计算校验和,无需生成序列号,也无需发送确认信息。
UNIX域套接字既提供流式接口,也提供数据报接口。不过,UNIX域数据报服务是可靠的——消息既不会丢失,也不会乱序交付。UNIX域套接字就像是套接字和管道的混合体。你既可以将面向网络的套接字接口用于它们,也可以使用
socketpair 函数创建一对未命名的、已连接的UNIX域套接字。
1 |
|
尽管该接口具有足够的通用性,允许将 socketpair
用于其他域,但操作系统通常仅支持UNIX域。
一对相连的UNIX域套接字的作用类似于全双工管道:两端都可以进行读写操作(见图17.1)。将其称为“文件描述符管道(fd-pipes)”,以区别于普通的半双工管道。

尽管socketpair函数创建的套接字彼此相连,但各个套接字并没有名称。这意味着它们无法被不相关的进程寻址。
在16.3.4节中,我们了解了如何将一个地址绑定到因特网域套接字。和因特网域套接字一样,UNIX域套接字也可以被命名并用于提供服务。不过,UNIX域套接字所使用的地址格式与因特网域套接字所使用的不同。
UNIX域套接字的地址由 sockaddr_un 结构表示。在Linux
3.2.0和Solaris 10系统中,sockaddr_un
结构在<sys/un.h>头文件中定义如下:
1 | struct sockaddr_un { |
然而,在FreeBSD 8.0和Mac OS X 10.6.8系统中,sockaddr_un
结构的定义为:
1 | struct sockaddr_un { |
sockaddr_un 结构的 sun_path
成员包含一个路径名。当我们将一个地址绑定到UNIX域套接字时,系统会创建一个类型为
S_IFSOCK
且名称相同的文件。该文件的存在仅作为向客户端公布套接字名称的一种方式。应用程序无法打开此文件,也不能用它进行通信。
如果在尝试绑定相同地址时该文件已存在,绑定请求将会失败。当关闭套接字时,此文件不会被自动删除,因此需要确保在应用程序退出前将其解除链接。
Passing File Descriptors
在进程间传递打开的文件描述符是一种强大的技术。它能催生出多种客户端-服务器应用程序的设计方式。
这一技术允许一个进程(通常是服务器)完成打开文件所需的所有操作(包括将网络名称转换为网络地址、拨号调制解调器、协商文件锁等细节),然后只需向调用进程返回一个可用于所有I/O函数的描述符。打开文件或设备所涉及的所有细节都对客户端隐藏。
我们必须更明确地说明从一个进程向另一个进程“传递打开的文件描述符”的确切含义。回想一下图3.8,该图展示了两个打开了同一个文件的进程。尽管它们共享同一个v节点,但每个进程都有自己的文件表项。
当我们从一个进程向另一个进程传递打开的文件描述符时,我们希望发送进程和接收进程共享同一个文件表项。图17.11展示了这种理想的结构。

从技术上讲,我们是将一个指向打开文件表项的指针从一个进程传递到另一个进程。这个指针会被分配给接收进程中第一个可用的描述符。(说我们在传递一个打开的描述符,容易让人误以为接收进程中的描述符编号与发送进程中的相同,而这通常是不正确的。)两个进程共享一个打开的文件表,这与
fork 之后发生的情况完全一致(回想一下图8.2)。
通常,当一个描述符从一个进程传递到另一个进程时,发送进程在传递描述符后会关闭该描述符。发送方关闭描述符并不会真正关闭文件或设备,因为对于接收进程而言,该描述符仍被视为处于打开状态(即使接收方尚未明确接收该描述符)。
Open Servers
通过文件描述符传递,我们现在开发一个打开服务器——这是一个由进程执行以打开一个或多个文件的程序。不过,该服务器不会将文件内容发送回调用进程,而是发送回一个打开的文件描述符。因此,这个打开服务器可以处理任何类型的文件(如设备或套接字),而不仅仅是普通文件。客户端和服务器使用进程间通信(IPC)交换最少的信息:客户端发送的文件名和打开模式,以及服务器返回的描述符。文件内容不会通过进程间通信进行交换。
将服务器设计为一个独立的可执行程序有几个优点(既可以是由客户端执行的程序,也可以是守护进程服务器):
- 任何客户端都能轻松联系到服务器,这类似于客户端调用库函数。 我们不会在应用程序中硬编码特定服务,而是设计一种可供他人复用的通用工具。
- 如果需要更换服务器,只会影响一个程序。相反,更新库函数可能需要所有调用该函数的程序都进行更新(即使用链接编辑器重新链接)。共享库可以简化这种更新(见7.7节)。
- 服务器可以是一个设置用户ID的程序,为其提供客户端所没有的额外权限。请注意,库函数(或共享库函数)无法提供这种功能。