Kill those pesky forked children!
2009/08/06 filed under /perlDuring some test writing, I had to spawn a shell command that would run while /bin/true was true. Unfortunately, my test script figured the run time should be limited, so I had to kill that little brat myself.
Let's have a look at a simplified setup first:
my $pid = fork();
if($pid == 0) {
print "I'm the child: $$\n";
system('while(/bin/true); do echo "Mwuahaha"; sleep 1; done');
} else {
print "I'm the parent: $$\n";
}
sleep 3;
kill 1, $pid;
Although this might look valid perl, the "Mwhuahaha" will continue to fill up you terminal. The forked system command will not be killed when your program ends.
There had to be a way to kill the child (grandchild, if you want), and there is. Using setpgrp and getpgrp to the rescue!
Let's have a look at the documentation first (always a good thing):
setpgrp PID,PGRP
Sets the current process group for the specified PID, 0 for the
current process. Will produce a fatal error if used on a
machine that doesn't implement POSIX setpgid(2) or BSD
setpgrp(2). If the arguments are omitted, it defaults to
"0,0". Note that the BSD 4.2 version of "setpgrp" does not
accept any arguments, so only "setpgrp(0,0)" is portable. See
also "POSIX::setsid()".
getpgrp PID
Returns the current process group for the specified PID. Use a
PID of 0 to get the current process group for the current
process. Will raise an exception if used on a machine that
doesn' implement getpgrp(2). If PID is omitted, returns
process group of current process. Note that the POSIX version
of "getpgrp" does not accept a PID argument, so only "PID==0"
is truly portable.
Almost there! Let's see the kill documentation as well, for it contains a fatal clue:
Unlike in the shell, if SIGNAL is negative, it kills process
groups instead of processes. (On System V, a negative PROCESS
number will also kill process groups, but that's not portable.)
That means you usually want to use positive not negative
signals. You may also use a signal name in quotes.
Note that we need the negative signal!
So, if we change our test script to this, it'll all work, and those pesky (grand)children are killed when we want to:
my $pid = fork();
if($pid == 0) {
setpgrp;
print "I'm the child: $$\n";
system('while(/bin/true); do echo "Mwuahaha"; sleep 1; done');
} else {
print "I'm the parent: $$\n";
}
sleep 3;
kill -1, getpgrp($pid);



Comments