繁体   English   中英

从BEGIN块中调用Perl fork

[英]Calling a Perl fork from within a BEGIN block

在从BEGIN块中调用Perl时,我无法理解Perl中的fork行为。 perlfork中 ,我读到了这个

BEGIN块

BEGIN块中调用时, fork()仿真将无法完全正常工作。 分叉副本将运行BEGIN块的内容,但不会在BEGIN块之后继续解析源流。 例如,请考虑以下代码:

 BEGIN { fork and exit; # fork child and exit the parent print "inner\\n"; } print "outer\\n"; 

这将打印:

 inner 

而不是预期的:

 inner outer 

但是,正如我所读到的,这仅适用于模拟fork平台。 既然我关心(并测试代码)Linux,那应该不是问题,不是吗?

实际上,如果我从该文档中复制示例代码

BEGIN {
    fork and exit;
    print "inner\n";
}
print "outer\n";

这就是我运行它时会发生的事情

jirka@debian:~/xpath$ perl /tmp/test.pl
jirka@debian:~/xpath$ inner
outer

这似乎是一致的。

但是,当我删除exit我希望同时拥有父进程和子进程。 那不符合我的预期。

这是我的新代码

BEGIN {
    fork;
    print "inner\n";
}
print "outer\n";

这是奔跑

jirka@debian:~/xpath$ perl /tmp/test.pl
inner
outer
jirka@debian:~/xpath$ inner

我期待两个inner和两个outer 第二个outer缺失。

我的问题是,是什么导致了这种奇怪的行为,甚至如何描述它。

在我看来,孩子不再打开源文件(或者它只在父母中缓冲?)

通过-e尝试代码成功。

我的第一个猜测是,在孩子完成运行之前父母退出,导致它死亡(SIGPIPE?),但等待孩子产生相同的输出:

BEGIN {
   $pid = fork;
   print "inner\n";
}
print "outer\n";
waitpid $pid, 0 if $pid;

输出:

inner
outer
inner

事实上,似乎无法实现。 问题的原因是父和子共享源文件的相同文件指针。 当从源文件中读取时,它会为两者提前文件指针。

例如,如果我阻止其中一个进程使用__DATA__进一步读取文件,则另一个进程将继续读取__DATA__并在那里执行代码。 如果我将以下内容添加到上述程序中:

__DATA__
...8KB of newlines...
die("boo!");

我明白了:

inner
outer
inner
boo! at a.pl line 90.

好吧,问题实际上似乎是孩子和父母踩在彼此的源文件描述符上。 Strace给出:

read(3, "BEGIN {\n        fork;\n\tprint \"in"..., 8192) = 67
_llseek(3, 46, [46], SEEK_SET)          = 0
_llseek(3, 0, [46], SEEK_CUR)           = 0
clone(Process 29716 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb75329a8) = 29716
[pid 29715] write(1, "inner\n", 6inner
)      = 6
[pid 29715] read(3, "    print \"outer\\n\";\n", 8192) = 21
[pid 29715] read(3, "", 8192)           = 0
[pid 29715] close(3)                    = 0
...
write(1, "inner\n", 6inner
)                  = 6
read(3, "", 8192)                       = 0
close(3)                                = 0

这似乎是由父子共享单个文件读指针的事实引起的。 man fork

  • 子进程继承父进程打开文件描述符的副本。 子节点中的每个文件描述符引用相同的打开文件描述(请参阅open(2))作为父节点中的相应文件描述符。 这意味着两个描述符共享打开文件状态标志, 当前文件偏移量 ,...

现在,这引出了一个问题:如何分离这些文件描述符的偏移量?

我想知道你在最后的提示后怎么会有inner印刷品?

如果您仔细阅读文档

从BEGIN块中调用时,fork()仿真将无法完全正常工作。 分叉副本将运行BEGIN块的内容,但不会在BEGIN块之后继续解析源流

它表示子进程将仅解析(并因此运行) BEGIN块的其余部分。 所以孩子打印inner ,不再打印。

没有exit调用,父进程继续打印inner ,然后是outer ,所以你应该有

inner
inner
outer

我希望我有一个Unix盒可以试试这个,但是当我回到家的时候会这样做

我的问题是,是什么导致了这种奇怪的行为,甚至如何描述它。

实际上,当检查语法时(粗略地)执行BEGIN块。 有许多阶段,如BEGINUNITCHECKCHECKINITEND 所以当你在BEGIN执行fork()时,程序实际上还没有运行。

在模拟fork()系统上,这是由内部Perl解释器状态引起的,因为程序是早期启动状态(您的代码甚至没有编译!)。 因此,在仿真环境中,我认为Perl在编译后会丢弃模拟的分支。

我想要修复你必须将你的代码放入INIT块。 有关这些阶段的更多详细信息,请阅读perlmod手册页。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM