hotshot has asked for the wisdom of the Perl Monks concerning the following question:

Hello all!

I'm using kill('KILL', $pid) to kill a process. This process creates children, and I want somehow to kill them too, how can I do that? Is there in Perl something similar to pstree -plc $PID (like in Unix), that can help me get the children's pid, or even something that gets a pid as parameter and kills it with it's children?

Replies are listed 'Best First'.
Re: Killing child process
by robartes (Priest) on Oct 12, 2003 at 10:51 UTC
    Normally, unless the child processes detach from the parent's process group, signals will propagate to the children, so when you kill the parent, the children should go down as well. However, if you want to do this explicitely, you can use Proc::ProcessTable. Something like this (untested):
    use strict; use warnings; use Proc::ProcessTable; my $signal=15; my $parent=shift; # Parent PID from args my $proc_table=Proc::ProcessTable->new(); foreach my $proc (@{$proc_table->table()}) { kill($signal, $proc->pid) if ($proc->ppid == $parent); }
    Making this code recursive to kill the children's children is left as an exercise for the reader :) (read: it's Sunday morning and I can't be bothered to do so myself ;) ).


Re: Killing child process
by Thelonius (Priest) on Oct 12, 2003 at 12:02 UTC
    To quote the perlfunc manpage for kill,
    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.
    So all you have to do is call kill with the negative value for the signal you want. E.g. kill -9, $pid;. If you want to use the name of a signal, I think you'll have to do something like:
    use POSIX; kill - SIGKILL, $pid;
    (If you leave out the space after the minus sign, you'll get a warning message like "Ambiguous use of -SIGKILL resolved as -&SIGKILL()". Of course you could type  kill -&SIGKILL(), $pid, but it seems easier to just leave a space).
      I couldn't get that to work:
      #!/usr/bin/perl -w use strict; $SIG{TERM}= sub { print "TERM $$\n"; exit(1); }; if (my $kidpid = fork) { # Parent sleep(1); print "\n"; kill -15, $kidpid; sleep(5); } else { print "CREATED CHILD $$\n"; # Child for my $i (1..10) { if (!fork) { # Grandchild print "CREATED GRANDCHILD $$\n"; sleep(5); exit(0); } } sleep(300); }
      Is there a bug in this code, or are things not working as documented?
        Oops, yeah, I forgot you need to set the process group for the process you want to be a group leader. To do this call setpgrp(0,0); after the fork. In your test program, do it right where you have print "CREATED CHILD $$\n"; and don't do it in the grandchildren.
        Two points about OS:
        • To send a signal to a process group, you have to be a super user;
        • Most of OS's, if not all, does not let you capture SIGTERM for ordinary users. That's for a good reason, if some processes (intentionally or by mistake - bugs) decide to be alive forever, that will really be a nightmare.
    Re: Killing child process
    by zentara (Archbishop) on Oct 12, 2003 at 17:15 UTC
      You can use Proc::Killfam:
      #!/usr/bin/perl #Proc::Killfam is part of Proc::ProceessTable module use Proc::Killfam; killfam $signal, @pids; killfam 'TERM', ($pid1, $pid2, @more_pids);
      Or here is a snippet of a subroutine posted by robau awhile back:
      #!/usr/bin/perl use warnings; use strict; #robau #This subroutine takes two arguments, the parent process ID #and the numeric signal to pass to the processes #(which would be 9 if you wanted to issue a -TERM). #Using Proc::Process you could find the process ID #of the process login -- zentara with something similar #to the following : #my $proc = Proc::ProcessTable->new; #my @ps = map { $_->pid if ($_->cmndline =~ /login -- zentara/) } @{$p +roc->table}; #&killchd($_, 9) foreach @ps; &killchd(9895, 9); #kill -9 process 9895 sub killchd ($;$) { use Proc::ProcessTable; my $sig = ($_[1] =~ /^\-?\d+$/) ? $_[1] : 0; my $proc = Proc::ProcessTable->new; my %fields = map { $_ => 1 } $proc->fields; return undef unless exists $fields{'ppid'}; foreach (@{$proc->table}) { kill $sig, $_->pid if ($_->ppid == $_[0]); }; kill $sig, $_[0]; };