Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a Perl fork from within a BEGIN block

Tags:

fork

posix

perl

I am having trouble understanding the fork behavior in Perl when it is called from within a BEGIN block. In perlfork, I read this

BEGIN blocks

The fork() emulation will not work entirely correctly when called from within a BEGIN block. The forked copy will run the contents of the BEGIN block, but will not continue parsing the source stream after the BEGIN block. 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. Since I'm concerned about (and test the code on) Linux, that shouldn't be a problem, should it?

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. 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. The second outer is missing.

My question is, what causes this strange behaviour, and how could it even be described.

like image 317
jpalecek Avatar asked Nov 23 '12 00:11

jpalecek


2 Answers

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.

like image 86
ysth Avatar answered Nov 04 '22 02:11

ysth


OK, the problem really seems to be that the child and the parent stomp on each other's source file descriptor. Strace gives:

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:

  • 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. 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?

like image 3
jpalecek Avatar answered Nov 04 '22 02:11

jpalecek