http://www.perlmonks.org?node_id=964628


in reply to forking, waiting, and killing child pids

Your usage of "&" in the command is not only unnecessary (the fork already runs "in the background"), it also is the primary reason your code doesn't work.  Otherwise, your approach to create a new process group is perfectly fine, in case you need or want to kill an entire tree of child processes.

Compare the following (I replaced tcpdump with a simple sleep, which is irrelevant to the discussion):

#!/usr/bin/perl -w use strict; sub ps { system "ps Tf -o pid,ppid,pgrp,sid,cmd"; } $SIG{CHLD} = 'IGNORE'; my $snoop = fork(); if ( $snoop == 0 ){ setpgrp; system("sleep 20 &"); exit; } ps(); sleep 1; print "PID: $snoop\n"; printf "PGRP: %d\n", getpgrp($snoop); kill -1, getpgrp($snoop); ps(); __END__ PID PPID PGRP SID CMD 2360 2358 2360 2360 bash -rcfile .bashrc 19529 2360 19529 2360 \_ /usr/bin/perl -w ./964597.pl 19531 19529 19529 2360 \_ ps Tf -o pid,ppid,pgrp,sid,cmd 19533 1 19530 2360 sleep 20 PID: 19530 PGRP: -1 PID PPID PGRP SID CMD 2360 2358 2360 2360 bash -rcfile .bashrc 19529 2360 19529 2360 \_ /usr/bin/perl -w ./964597.pl 19534 19529 19529 2360 \_ ps Tf -o pid,ppid,pgrp,sid,cmd 19533 1 19530 2360 sleep 20
#!/usr/bin/perl -w use strict; sub ps { system "ps Tf -o pid,ppid,pgrp,sid,cmd"; } $SIG{CHLD} = 'IGNORE'; my $snoop = fork(); if ( $snoop == 0 ){ setpgrp; system("sleep 20 ;"); exit; } ps(); sleep 1; print "PID: $snoop\n"; printf "PGRP: %d\n", getpgrp($snoop); kill -1, getpgrp($snoop); ps(); __END__ PID PPID PGRP SID CMD 2360 2358 2360 2360 bash -rcfile .bashrc 19537 2360 19537 2360 \_ /usr/bin/perl -w ./964597.pl 19538 19537 19538 2360 \_ /usr/bin/perl -w ./964597.pl 19540 19538 19538 2360 | \_ sh -c sleep 20 ; 19541 19540 19538 2360 | \_ sleep 20 19539 19537 19537 2360 \_ ps Tf -o pid,ppid,pgrp,sid,cmd PID: 19538 PGRP: 19538 PID PPID PGRP SID CMD 2360 2358 2360 2360 bash -rcfile .bashrc 19537 2360 19537 2360 \_ /usr/bin/perl -w ./964597.pl 19542 19537 19537 2360 \_ ps Tf -o pid,ppid,pgrp,sid,cmd

As you can see in the first example, the child process (sleep) dissociates, i.e. the original process (your $snoop PID) is no longer alive, so getpgrp($snoop) fails, and you effectively kill nothing...

In the second example, however, without using "&"1, things work as intended. I.e., you have created a new process group (19538 here), which you are then killing successfully.

That said, unless you actually are running multiple child processes, there is no need to use the process group technique. As long as you can make sure that tcpdump is the only and immediate child process (for example using exec, as noted by halfcountplus (but without your "&" !)), killing that process directly would work without a problem.

BTW, to check whether a process is running, you can send it the "0" pseudo signal, e.g.

printf "child%s alive\n", kill(0, $snoop) ? "":" not";

___

1note that I'm using a ";" here in place of the "&", because the example is also meant to show that using an extra process group to kill multiple processes in one go, is working just fine.  Without a shell meta character in the command (the semicolon in this case), Perl would optimize away the extra shell, so there would only be a single child process, which kind of defeats the purpose of using a process group...

Replies are listed 'Best First'.
Re^2: forking, waiting, and killing child pids
by sinmissing (Initiate) on Apr 20, 2012 at 15:47 UTC

    You're sample of forks without & is very close to how I solved my problem, based on your original reply. I used Proc::ProcessTable to get the pid associated with snoop.

    The following is a mock-up of pstree.

    sshd,30982 --bash,30983 --su,31002 --bash,31003 --my.pl,6893 ./my.pl debug --sh,6934 -c ... --tcpdump,6935 --my.pl,6933 ./my.pl debug

    I achieved these results by using open, as in "open, $snoop, $snoop_cmd". Then, while $snoop is running, I start a counter, and test whether the actual snoop pid is running.

    while ( $counter < 31 ) { my $snoop_running = 1; for my $p ( @{ $ps->table } ) { if ( ( $p->pid == $snoop_pid ) && ( $p->ppid == getpgrp($fork_pid) ) && ( ( $p->cmndline ) =~ '$snoop_cmd' ) ) { $snoop_running = 0; print "packet capture still running (", $p->pid, ") ", $p->cmndline, "\n" if (( $debug eq 0 ) && (( $p->cmndline ) =~ '$snoop_ +cmd' )); } } if ( $snoop_running == 1 ) { #snoop must have finished already for my $p ( @{ $ps->table } ) { kill 9, ( $p->pid ) if ( $p->ppid == getpgrp($fork_pid +) ); } last; } $counter++; sleep 1;

    I used this timer method, because at certain check points; 5, 10, and 20 seconds; I wanted to execute additional system commands, but I did not want to execute any if my snoop had already completed. Next, I kill the fork once packet capture is done. That level of fine pid control is admittedly overly fine, and not for everyone.

    I don't think I would have figured this out, without your initial reply. Thank you very much!