进程间传递文件描述符有什么实际应用场景呢?
- 最近看到golang中关于服务器平滑热重启reload时,需要长链接不被断开,需要fork一个子进程出来,然后把父进程的所有socket的文件 描述符(即:所有hold的客户端连接)都传递到子进程。在没研究linux进程间传递文件描述符之前,我的脑海里只有fork时,发生copy-on-write,父子进程间直接就可以共享连接fd了,但是这个时候,如果父进程退出,那所有的连接也被掐断了,因为父子进程共享连接资源了。所以需要将fd关联到的资源完整的clone到子进程中,使得父子进程对应的fd完全脱离关系,不过fd从父进程copy到子进程之后,想要fd值(int)都保持一致,不太可能,因为子进程要重新分配fd的。
如何实现进程间传递fd呢?
- 大概就是通过unixsockt,一端sendmsg, 另外一端recvmsg,但是不是简单的传输消息内容,需要告诉内核传输的是文件描述符 SCM_RIGHTS。 下面直接给出php版本的demo
<?php
$fds = [];
$ret = socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $fds);
if (!$ret) {
exit(socket_strerror(socket_last_error()));
}
$pid = pcntl_fork();
if ($pid > 0) {
socket_close($fds[0]);
$handle = fopen(__DIR__ . '/atomic.txt', 'a+');
var_dump($handle);
socket_sendmsg($fds[1], [
'control' => [
[
'level' => SOL_SOCKET,
'type' => SCM_RIGHTS,
'data' => [$handle]
]
]
], 0);
sleep(1);
} else {
socket_close($fds[1]);
$data = [
'controllen' => socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 1)
];
$result = socket_recvmsg($fds[0], $data, 0);
var_dump("recv ok\n");
var_dump($data['control'][0]['data'][0]);
fwrite($data['control'][0]['data'][0], 'passing fd in PHP' . PHP_EOL);
}
上述代码中,父进程var_dump($handle),和子进程var_dump($data['control'][0]['data'][0]); 两次打印的resource资源类型int值已经变化了,说明fd已经完整clone过去,父进程退出,也不会影响子进程的这个fd了。