简体   繁体   English

从BEGIN块中调用Perl fork

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

I am having trouble understanding the fork behavior in Perl when it is called from within a BEGIN block. 在从BEGIN块中调用Perl时,我无法理解Perl中的fork行为。 In perlfork , I read this perlfork中 ,我读到了这个

BEGIN blocks BEGIN块

The fork() emulation will not work entirely correctly when called from within a BEGIN block. BEGIN块中调用时, fork()仿真将无法完全正常工作。 The forked copy will run the contents of the BEGIN block, but will not continue parsing the source stream after the BEGIN block. 分叉副本将运行BEGIN块的内容,但不会在BEGIN块之后继续解析源流。 For example, consider the following code: 例如,请考虑以下代码:

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

This will print: 这将打印:

 inner 

rather than the expected: 而不是预期的:

 inner outer 

But, as I read it, this only applies to platforms where fork is emulated. 但是,正如我所读到的,这仅适用于模拟fork平台。 Since I'm concerned about (and test the code on) Linux, that shouldn't be a problem, should it? 既然我关心(并测试代码)Linux,那应该不是问题,不是吗?

Indeed, if I copy the example code from that document 实际上,如果我从该文档中复制示例代码

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

this is what happens when I run it 这就是我运行它时会发生的事情

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

which seems consistent. 这似乎是一致的。

However, when I removed the exit I expected to have both a parent and a child process. 但是,当我删除exit我希望同时拥有父进程和子进程。 That that didn't behave as I expected. 那不符合我的预期。

Here is my new code 这是我的新代码

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

and here is the run 这是奔跑

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

I expected two inner and two outer . 我期待两个inner和两个outer The second outer is missing. 第二个outer缺失。

My question is, what causes this strange behaviour, and how could it even be described. 我的问题是,是什么导致了这种奇怪的行为,甚至如何描述它。

It looks to me like the child no longer has the source file open (or it is all buffered in the parent only?) 在我看来,孩子不再打开源文件(或者它只在父母中缓冲?)

Trying the code via -e succeeds. 通过-e尝试代码成功。

My first guess is that the parent exits before the child has finished running, causing it to die (SIGPIPE?), but waiting for the child yields the same output: 我的第一个猜测是,在孩子完成运行之前父母退出,导致它死亡(SIGPIPE?),但等待孩子产生相同的输出:

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

Output: 输出:

inner
outer
inner

So indeed, it doesn't seem possible to accomplish. 事实上,似乎无法实现。 The reason for the problem is that the parent and the child share the same file pointer to the source file. 问题的原因是父和子共享源文件的相同文件指针。 When one reads from the source file, it advances the file pointer for both. 当从源文件中读取时,它会为两者提前文件指针。

For example, if I prevent one of the processes from reading further down the file using __DATA__ , the other process will continue reading past the __DATA__ and execute the code there. 例如,如果我阻止其中一个进程使用__DATA__进一步读取文件,则另一个进程将继续读取__DATA__并在那里执行代码。 If I append the following to the above program: 如果我将以下内容添加到上述程序中:

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

I get: 我明白了:

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

OK, the problem really seems to be that the child and the parent stomp on each other's source file descriptor. 好吧,问题实际上似乎是孩子和父母踩在彼此的源文件描述符上。 Strace gives: 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

This seems to be caused by the fact that parent and child share a single file read pointer. 这似乎是由父子共享单个文件读指针的事实引起的。 From man fork : man fork

  • The child inherits copies of the parent's set of open file descriptors. 子进程继承父进程打开文件描述符的副本。 Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. 子节点中的每个文件描述符引用相同的打开文件描述(请参阅open(2))作为父节点中的相应文件描述符。 This means that the two descriptors share open file status flags, current file offset , ... 这意味着两个描述符共享打开文件状态标志, 当前文件偏移量 ,...

Now, this begs the question: How to separate those file descriptors' offsets? 现在,这引出了一个问题:如何分离这些文件描述符的偏移量?

I wonder how you had inner printed after the final prompt? 我想知道你在最后的提示后怎么会有inner印刷品?

If you read the documentation carefully 如果您仔细阅读文档

The fork() emulation will not work entirely correctly when called from within a BEGIN block. 从BEGIN块中调用时,fork()仿真将无法完全正常工作。 The forked copy will run the contents of the BEGIN block, but will not continue parsing the source stream after the BEGIN block 分叉副本将运行BEGIN块的内容,但不会在BEGIN块之后继续解析源流

it says that the child process will parse (and therefore run) only the remainder of the BEGIN block. 它表示子进程将仅解析(并因此运行) BEGIN块的其余部分。 So the child prints inner and does no more. 所以孩子打印inner ,不再打印。

Without the exit call the parent process goes on to print inner , and subsequently outer , so you should have 没有exit调用,父进程继续打印inner ,然后是outer ,所以你应该有

inner
inner
outer

I wish I had a Unix box to hand to try this out, but will do so when I get home 我希望我有一个Unix盒可以试试这个,但是当我回到家的时候会这样做

My question is, what causes this strange behaviour, and how could it even be described. 我的问题是,是什么导致了这种奇怪的行为,甚至如何描述它。

Actually BEGIN block is executed when syntax is checked (roughly). 实际上,当检查语法时(粗略地)执行BEGIN块。 There is many phases such as BEGIN , UNITCHECK , CHECK , INIT and END . 有许多阶段,如BEGINUNITCHECKCHECKINITEND So when you do your fork() in BEGIN the program actually is not run yet. 所以当你在BEGIN执行fork()时,程序实际上还没有运行。

On system where fork() is emulated, this caused by internal Perl interpretator state, since program is early start state (your code isnot even compiled!). 在模拟fork()系统上,这是由内部Perl解释器状态引起的,因为程序是早期启动状态(您的代码甚至没有编译!)。 So on emulated environment i think Perl drops emulated forks after compile. 因此,在仿真环境中,我认为Perl在编译后会丢弃模拟的分支。

I think to fix that you must place your code into INIT block. 我想要修复你必须将你的代码放入INIT块。 Please read perlmod man page for more detailed info about these stages. 有关这些阶段的更多详细信息,请阅读perlmod手册页。

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

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