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 aBEGIN
block. The forked copy will run the contents of theBEGIN
block, but will not continue parsing the source stream after theBEGIN
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.
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.
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:
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. 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:
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?
I wonder how you had inner
printed after the final prompt?
If you read the documentation carefully
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
it says that the child process will parse (and therefore run) only the remainder of the BEGIN
block. So the child prints inner
and does no more.
Without the exit
call the parent process goes on to print inner
, and subsequently outer
, so you should have
inner
inner
outer
I wish I had a Unix box to hand to try this out, but will do so when I get home
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). There is many phases such as BEGIN
, UNITCHECK
, CHECK
, INIT
and END
. So when you do your fork()
in BEGIN
the program actually is not run yet.
On system where fork()
is emulated, this caused by internal Perl interpretator state, since program is early start state (your code isnot even compiled!). So on emulated environment i think Perl drops emulated forks after compile.
I think to fix that you must place your code into INIT block. Please read perlmod man page for more detailed info about these stages.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.