从fork
块中调用Perl时,我无法理解Perl中的BEGIN
行为。在perlfork中,我读了这个
BEGIN阻止
从
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
丢失了。
我的问题是,是什么导致了这种奇怪的行为,甚至可以如何描述。
答案 0 :(得分:3)
在我看来,孩子不再打开源文件(或者只在父母中缓冲了吗?)
通过-e尝试代码成功。
答案 1 :(得分: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.
答案 2 :(得分: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))作为父对应的文件描述符。这个 表示两个描述符共享打开文件状态标志当前文件 抵消,...
现在,这引出了一个问题:如何分离这些文件描述符的偏移量?
答案 3 :(得分:-1)
我想知道你在最终提示后如何打印inner
?
如果您仔细阅读文档
fork()仿真在调用时无法完全正常工作 在BEGIN区块内。分叉副本将运行的内容 BEGIN块,但不会继续解析源流后的 BEGIN块
它表示子进程将仅解析(并因此运行)BEGIN
块的其余部分。所以孩子打印inner
并且不再打印。
如果没有exit
调用,父进程会继续打印inner
,然后是outer
,所以你应该
inner
inner
outer
我希望我有一个Unix盒可以试用,但是当我回到家时会这样做
答案 4 :(得分:-1)
我的问题是,是什么导致了这种奇怪的行为,甚至可以如何描述。
实际上,当检查语法时(粗略地)执行 BEGIN 块。有许多阶段,例如BEGIN
,UNITCHECK
,CHECK
,INIT
和END
。因此,当您在fork()
中执行BEGIN
时,该程序实际上尚未运行。
在模拟fork()
的系统上,这是由内部Perl解释器状态引起的,因为程序是早期启动状态(您的代码甚至没有编译!)。因此,在仿真环境中,我认为Perl在编译后会丢弃模拟的分支。
我认为您必须将代码置于 INIT 块中。有关这些阶段的更多详细信息,请阅读perlmod手册页。