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.