7

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

开始块

从块中调用时,fork()仿真将无法完全正常工作。BEGIN分叉的副本将运行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不见了。

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

4

5 回答 5

3

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

通过 -e 尝试代码成功。

于 2012-11-23T00:40:26.990 回答
3

我的第一个猜测是父级在子级完成运行之前退出,导致它死亡(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.
于 2012-11-23T01:13:12.467 回答
3

好的,问题似乎确实是孩子和父母踩着对方的源文件描述符。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))。这意味着两个描述符共享打开文件状态标志,当前文件偏移量,...

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

于 2012-11-23T01:17:36.800 回答
-1

我想知道你inner在最后提示后是如何打印的?

如果您仔细阅读文档

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

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

如果没有exit调用,父进程会继续打印inner,随后会打印outer,所以您应该有

inner
inner
outer

我希望我有一个 Unix 盒子来试试这个,但我回家后会这样做

于 2012-11-23T03:15:55.663 回答
-1

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

实际上,当检查语法时(粗略地)执行BEGIN块。有许多阶段,例如BEGINUNITCHECK、和。所以当你做你的程序实际上还没有运行。CHECKINITENDfork()BEGIN

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

我认为要解决您必须将代码放入INIT块的问题。有关这些阶段的更多详细信息,请阅读perlmod手册页。

于 2012-11-23T08:04:55.913 回答