Perl - 在fork/exec之间传递一个打开的套接字

Cha*_*hap 9 sockets perl fork exec

我希望有一个Perl守护进程监听接受来自客户端的传入连接,然后fork&exec另一个Perl程序继续与客户端进行对话.

简单地分叉时我可以做到这一点 - 守护进程代码也包含子代码.但是我没有看到open socket如何通过exec()传递给另一个Perl程序.

不知怎的,我得到的印象是这在Unix(这是我的环境)中很容易,因此在Perl中也是如此.真的可以吗?

mel*_*ene 14

这可以通过大约三个步骤完成:

  1. 清除文件描述符上的close-on-exec标志.
  2. 告诉exec'd程序使用哪个文件描述符.
  3. 将文件描述符恢复为句柄.

1. Perl(默认情况下)在它打开的文件描述符上设置close-on-exec标志.这意味着文件描述符不会被保留exec.你必须先清除这个标志:

use Fcntl;

my $flags = fcntl $fh, F_GETFD, 0 or die "fcntl F_GETFD: $!";
fcntl $fh, F_SETFD, $flags & ~FD_CLOEXEC or die "fcntl F_SETFD: $!";
Run Code Online (Sandbox Code Playgroud)

2.现在文件描述符将保持打开状态exec,您需要告诉程序它是哪个描述符:

my $fd = fileno $fh;
exec 'that_program', $fd;  # pass it on the command line
# (you could also pass it via %ENV or whatever)
Run Code Online (Sandbox Code Playgroud)

3.恢复另一侧的文件句柄:

my $fd = $ARGV[0];  # or however you passed it
open my $fh, '+<&=', $fd;  # fdopen
$fh->autoflush(1);  # because "normal" sockets have that enabled by default
Run Code Online (Sandbox Code Playgroud)

现在你$fh又有了一个Perl级别的句柄.

附录:正如ikegami在评论中提到的那样,你也可以确保套接字使用三个"标准"文件描述符之一(0(stdin),1(stdout),2(stderr)),它们是1.默认跨越高管,2.已知数字,所以不需要传递任何东西,并且3. perl将自动为它们创建相应的句柄.

open STDIN, '+<&', $fh;  # now STDIN refers to the socket
exec 'that_program';
Run Code Online (Sandbox Code Playgroud)

现在that_program可以简单地使用STDIN.这甚至适用于输出; 文件描述符0,1,2没有固有的限制,它们仅用于输入或输出.这只是所有unix程序遵循的惯例.