Thanks for everyone who has replied.

I think Packy's solution solves my problem.

Let me restate my problem to make it clear.

Originally, my perl script launches Eggplant as a child process (use ``, not
an independent process.) so that the perl script could get the log output
from Eggplant.

But the problem is Eggplant sometimes hangs. And the perl script would stuck
forever.

To solve this problem, I fork a process (child) to launch Eggplant (now
Eggplant is a grandchild), and use the parent to count the time.

If over a time limit and Eggplant is still running, the parent will kill
Eggplant (the child process will automatically ends as the Eggplant
returns).

The new problem is, the perl script doesn't know the pid of the grandchild
Eggplant to kill it. I used child's pid+1 as the grandchild's pid. It works
so far.

What Packy said is (as I understand), since the parent knows the child's
pid. It could use that to find the grandchild's pid through the command 'ps
-l'. Then kills grandchild with that pid. I think this should work.

Now, back to generating pid process on OS X. By my observation, OS X doesn't
use the next available pid  for the next process. It use some number close
by, but leave enough space for child processes. So, pid+1 is quite safe.

I will try to implement Packy's solution if time permitted, I only have a
little time left for this project. But after this discussion, I feel a
little bit comfortable with the pid+1.

Thanks for all the discussion.

Ted Zeng
Adobe Systems Inc.


On 7/31/06 5:24 PM, "Packy Anderson" <[EMAIL PROTECTED]> wrote:

> On Jul 31, 2006, at 5:35 PM, Ted Zeng wrote:
>> I don't know how the OS generate the pid for a process. That is why
>> I don't
>> feel comfortable with what I did.
> 
> The way I understand it, the OS assigns the next highest available
> pid when
> creating a process.  However, pids wrap around when they reach the
> maximum
> defined pid.  So, if your process' pid is near the max, your child could
> get a lower pid.
> 
> What you want to do is find the pid of your child's child.  There's
> probably
> a system call you can make to find these things, but I don't know it.
> What
> I do know is how to use "ps -j" or "ps -l".  I've whipped up the
> following
> script to demonstrate the technique.
> 
> Note: the script you're waiting on may have spawned off children of
> it's own,
> so it'd be a good idea to find them and kill them first.  This, of
> course,
> may let the script spawn off even more processes...
> 
> ==== start demo script ====
> #!/usr/bin/perl
> 
> use POSIX qw(:sys_wait_h);  # need this to get non-blocking waitchild
> 
> $|=1; # autoflush on
> 
> unless (defined($kid_pid = fork())) {
>      die "Cannot fork: $!";
> }
> 
> if ($kid_pid == 0) {
>      # we're running as the child process
>      execNightshade();
> }
> else {
>      print "Watching child $kid_pid...\n";
>      my $count=0;
>      my $tConstant = 1; ## I'm impatient
>      do {
> print "Sleeping ... ";
>          sleep 10;
>          $count++;
>          $kid = waitpid($kid_pid, WNOHANG);
> print "kid: $kid_pid, waitpid: $kid\n";
>      } until ($kid > 0 or $count > $tConstant);
> 
>      if ($count > $tConstant ) {
> recursive_kill($kid_pid);
>      }
> }
> 
> sub execNightshade {
>      # since we don't know what execEggplant does beside exec-ing shell
>      # scripts with ``, let's make something up...
> 
>      my $count = join q{ }, 1 .. 10; # make a string '1 2 3 4 5 6 7 8
> 9 10'
> 
>      # this shell script prints numbers from 1 to 100, sleeping 10
>      # seconds between each number...
>      my $output = `for i in $count; do echo "\$i"; sleep 10; done`;
> 
>      # what good is output if we don't use it?
>      print "$output\n";
> }
> 
> sub recursive_kill {
>      my($pid) = @_;
> 
>      while (my $child = child_pid($pid)) {
> # kill all its children, grandchildren, etc...
> recursive_kill($child);
>      }
> 
>      # then kill the process itself
>      print "Killing $pid\n";
>      kill 9, $pid;
> }
> 
> sub child_pid {
>      my($parent) = @_;
> 
>      # we want to know the process id of the child that's been forked
>      # by our own child.  since OS X is a unix, we can use ps to find
>      # it.  the following highly non-portable code should work on OS X
>      # and _maybe_ some other unices, but definately won't work on
>      # WinAnything or other, stranger OSes...
> 
>      my $grandkid_pid;
>      open my $ps, "ps -l |";
>      while (<$ps>) {
> # unless the first field is numeric, we're looking at the
> # column headings.  let's discard it.
> next unless /^ \s* \d+ /msx;
> 
> # split /PATTERN/,EXPR,LIMIT
> #
> # If EXPR is omitted, splits the $_ string.  If PATTERN is
> # also omitted, splits on whitespace (after skipping any
> # leading whitespace).
> #
> # As a special case, specifying a PATTERN of space (' ')
> # will split on white space just as "split" with no
> # arguments does.
> 
> my($uid, $pid, $ppid, $other_fields) = split q{ }, $_, 4;
> 
> # if the process' parent pid isn't our kid's pid, we're
> # not interested in it
> next unless ($ppid == $parent);
> 
> # congratulations!  you're a parent!
> return $pid;
>      }
>      return; # if there's no child, just return
> }
> ==== end demo script ====
> 
> Sample output:
> $ pid_test.pl
> Watching child 7718...
> Sleeping ... kid: 7718, waitpid: 0
> Sleeping ... kid: 7718, waitpid: 0
> Killing 7724
> Killing 7729
> Killing 7733
> Killing 7736
> Killing 7739
> Killing 7742
> Killing 7745
> Killing 7748
> Killing 7751
> 1
> 2
> 3
> 4
> 5
> 6
> 7
> 8
> 9
> 10
> 
> Killing 7719
> Killing 7718
> $
> 
> What's happening is the script is killing the 'sleep', and then before
> the subroutine can return and kill off the for loop, it's sleeping
> again.
> Fortunately, it disposes of the sleeps fairly quickly, and the child
> process actually returns its data before it gets killed.
> 
> --
> Packy Anderson   
> [EMAIL PROTECTED]
> 
> Catapultam habeo. Nisi pecuniam omnem mihi dabis, ad caput tuum saxum
> immane mittam.
> 
> 

Reply via email to