I have written two perl scripts (parent.pl and child.pl), and their source codes are as follows:
parent.pl:
# file parent.pl
$SIG{CHLD} = sub {
while(waitpid(-1, WNOHANG) > 0) {
print "child process exit\n";
}
};
my $pid = fork();
if($pid == 0) {
system("perl child.pl");
exit;
}
while(1) {
open my $fh, "date |";
while(<$fh>) {
print "parent: ".$_;
}
close $fh;
sleep(2);
}
child.pl
#file child.pl
while(1) {
open my $fh, "date |";
while(<$fh>) {
print " child: ".$_;
}
close $fh;
sleep(2);
}
What I want is the parent process and the forked sub-process output the current date alternately. But when I run perl parent.pl, the output is like this:
$ perl parent.pl
parent: Mon Jan 21 14:53:36 CST 2013
child: Mon Jan 21 14:53:36 CST 2013
child: Mon Jan 21 14:53:38 CST 2013
child: Mon Jan 21 14:53:40 CST 2013
child: Mon Jan 21 14:53:42 CST 2013
child: Mon Jan 21 14:53:44 CST 2013
It seems that the parent process was blocked when opening pipe.
But if I remove the following operation for signal CHLD.
$SIG{CHLD} = sub {
while(waitpid(-1, WNOHANG) > 0) {
print "child process exit\n";
}
};
And run it again. It seems OK.
$ perl parent.pl
parent: Mon Jan 21 14:57:57 CST 2013
child: Mon Jan 21 14:57:57 CST 2013
parent: Mon Jan 21 14:57:59 CST 2013
child: Mon Jan 21 14:57:59 CST 2013
parent: Mon Jan 21 14:58:01 CST 2013
child: Mon Jan 21 14:58:01 CST 2013
But I still feel puzzling. Why the parent process was blocked when I tried to open a pipe?
I don’t think removing the SIG{CHLD} function is a good idea, becaue zombie processes should be retrieved.
Anyone can help me? Thank you very much!
==================================================================
Thank @Borodin to help me solve my puzzle. And I have tried to modify the parent.pl like this:
my $main_pid = $$;
$SIG{USR1} = sub {
#sleep(1);
while(waitpid(-1, WNOHANG) > 0) {
print "child process exit\n";
}
};
my $pid = fork();
if($pid == 0) {
$SIG{USR1} = 'IGNORE';
system("perl child.pl");
kill USR1, $main_pid;
exit;
}
while(1) {
open my $fh, "date |";
while(<$fh>) {
print "parent: ".$_;
}
close $fh;
sleep(2);
}
Since CHLD signal may be kicked off by open or system, I used another customized signal USR1. And it works well now.
========================================================================
The above modification still has problems. The forked sub-process send USR1 singal before exit. May be the parent process should sleep for a while before waitpid, because the sub-process hasn’t exit yet.
I don’t retrieve sub-process manually now, and set $SIG{$CHLD} = 'IGNORE'. Hope the sub-process can be retrieved by operation system when it exits.
This is being made much more complex because both
open my $fh, "date |"andsystem("perl child.pl")are starting child processes, as well as the explicitfork.So
forkstarts a child process, which doessystem("perl child.pl")to start its own child process, which in turn doesopen my $fh, "date |", which opens yet another child, which is now a great-grandchild of the main parent process.Meanwhile the main process does its own
open my $fh, "date |"which starts another child process. In the end, the main process has two children, a grandchild and a great-grandchild.Unfortunately the children that are started using
openorsystemhave an implcitwaitattached to them, so they will kick off theCHLDsignal when they complete but when the handler is executed there is nothing left to wait for so it will hang as you have seen.perldoc perlipchas this to sayYou can get things going by keeping to just a single parent and a single child process, like this.