Unix 域套接字与描述符的传递
程序员文章站
2022-07-12 16:49:23
...
在Unix 域套接字概述一节中介绍了什么是 Unix 及相关函数,本文将继续介绍 Unix 域套接字在进程间传递描述符的应用。
在进程间传递打开的描述符时通常会采用如下两种方法:
(1)fork 调用返回后,子进程自动共享父进程的所有打开的描述符。
(2)exec 调用执行后,所有描述符通常保持打开状态不变。
第一种方法中,进程先打开一个描述符,再调用 fork,之后父进程关闭这个描述符,子进程则处理该描述符。这样一个打开的描述符就从父进程传递到子进程。不过有时候可能想让子进程打开一个描述符并把他传递给父进程。
使用 Unix 域套接字,可以从一个进程向任一其他进程传递打开的描述符,而无需这两个进程之间存在亲缘关系。这种技术要求首先在这两个进程之间创建一个 Unix 域套接字,然后使用 sendmsg 跨这个套接字发送一个特殊消息。该消息由内核来专门处理,会把打开的描述符从发送进程传递到接收进程。
使用 Unix 域套接字在两个进程之间传递描述符涉及的步骤如下。
(1)创建一个字节流或数据报的 Unix 域套接字。如果目标是让子进程将打开的描述符传递回父进程,则父进程可以预先调用 socketpair 函数创建一个可用于在父子进程之间交换描述符的流管道。如果进程间没有亲缘关系,则服务器进程必须先创建一个 Unix 域字节流套接字(也可以是 Unix 域数据报套接字,不过这没什么好处,而且数据报还存在被丢弃的可能性),然后 bind 一个路径名到该套接字,以允许客户进程 connect 到套接字,发送一个打开某个描述符的请求。
(2)发送进程通过调用返回描述符的任一 Unix 函数(如 open、pipe、mkfifo、socket 和 accept,可以在进程之间传递的描述符不限类型)打开一个描述符。
(3)发送进程创建一个 msghdr 结构,其中含有待传递的描述符。POSIX 规定描述符作为辅助数据(msghdr 结构的 msg_control 成员,见辅助数据)发送。发送进程调用 sendmsg 跨来自步骤 1 的 Unix 域套接字发送该描述符。至此,称这个描述符“在飞行中(in flight)”。即使发送进程在调用 sendmsg 之后但在接收进程调用 recvmsg 之前关闭了该描述符,对于接收进程它仍然保持打开状态。发送一个描述符会使该描述符的引用计数加一。
(4)接收进程调用 recvmsg 在来自步骤 1 的 Unix 域套接字上接收这个描述符。这个描述符在接收进程中的描述符号不同于它在发送进程中的描述符号是正常的,因为传递一个描述符并不是传递一个描述符号,而是涉及在接收进程中创建一个新的描述符,该描述符和发送进程中飞行前的那个描述符指向内核中相同的文件表项。
在进程间传递打开的描述符时通常会采用如下两种方法:
(1)fork 调用返回后,子进程自动共享父进程的所有打开的描述符。
(2)exec 调用执行后,所有描述符通常保持打开状态不变。
第一种方法中,进程先打开一个描述符,再调用 fork,之后父进程关闭这个描述符,子进程则处理该描述符。这样一个打开的描述符就从父进程传递到子进程。不过有时候可能想让子进程打开一个描述符并把他传递给父进程。
使用 Unix 域套接字,可以从一个进程向任一其他进程传递打开的描述符,而无需这两个进程之间存在亲缘关系。这种技术要求首先在这两个进程之间创建一个 Unix 域套接字,然后使用 sendmsg 跨这个套接字发送一个特殊消息。该消息由内核来专门处理,会把打开的描述符从发送进程传递到接收进程。
使用 Unix 域套接字在两个进程之间传递描述符涉及的步骤如下。
(1)创建一个字节流或数据报的 Unix 域套接字。如果目标是让子进程将打开的描述符传递回父进程,则父进程可以预先调用 socketpair 函数创建一个可用于在父子进程之间交换描述符的流管道。如果进程间没有亲缘关系,则服务器进程必须先创建一个 Unix 域字节流套接字(也可以是 Unix 域数据报套接字,不过这没什么好处,而且数据报还存在被丢弃的可能性),然后 bind 一个路径名到该套接字,以允许客户进程 connect 到套接字,发送一个打开某个描述符的请求。
(2)发送进程通过调用返回描述符的任一 Unix 函数(如 open、pipe、mkfifo、socket 和 accept,可以在进程之间传递的描述符不限类型)打开一个描述符。
(3)发送进程创建一个 msghdr 结构,其中含有待传递的描述符。POSIX 规定描述符作为辅助数据(msghdr 结构的 msg_control 成员,见辅助数据)发送。发送进程调用 sendmsg 跨来自步骤 1 的 Unix 域套接字发送该描述符。至此,称这个描述符“在飞行中(in flight)”。即使发送进程在调用 sendmsg 之后但在接收进程调用 recvmsg 之前关闭了该描述符,对于接收进程它仍然保持打开状态。发送一个描述符会使该描述符的引用计数加一。
(4)接收进程调用 recvmsg 在来自步骤 1 的 Unix 域套接字上接收这个描述符。这个描述符在接收进程中的描述符号不同于它在发送进程中的描述符号是正常的,因为传递一个描述符并不是传递一个描述符号,而是涉及在接收进程中创建一个新的描述符,该描述符和发送进程中飞行前的那个描述符指向内核中相同的文件表项。