in reply to zombies after forking

Your parent process needs to wait() for the children to exit.

Have you traced what your program does? Lets give it the arguments @ARGV = ( 3, "echo", "Foo!").

  1. Parent shifts off the count of 3, @ARGV is now ("echo","Foo!"). The for loop is set up so $i takes successive values 1,2,and 3.
  2. When $i is 1, child1 starts sleeping for 30 seconds. Parent immediately loops to:
  3. $i is 2, child2 starts sleeping for 15 seconds. Parent immediately loops to:
  4. $i is 3, child3 starts sleeping for 7 seconds. Parent is done with the loop, so...
  5. Parent runs system('echo', 'Foo!'), and exits. All the kids are still sleeping. System init process inherits the kids.
  6. Child3 runs system('echo', 'Foo!') six or seven seconds later, unless already killed by init.
  7. Child2 runs system('echo', 'Foo!') seven or eight seconds later, unless already killed by init.
  8. Child3 runs system('echo', 'Foo!') fifteen or sixteen seconds later, unless already killed by init.
There is no real problem with that, except that I don't think it's doing what you wanted it to do.

The trouble begins if the parent's instance of system(@ARGV) runs longer than some of the kids do. By not calling wait() for the dead kids, the parent leaves them zombie.

I won't worry here about the lack of error checking, use strict;, or the mishandling of the kids. Instead, I'll ask if this would serve you better:

#!/usr/bin/perl -w use strict; my $count = shift; my $interval = 60 / $count; for (1..$count) { system(@ARGV); select(undef,undef,undef,$interval); }
The select code is an accurate sleep, giving more even timing than the one second granularity of sleep() allows. This will do what you want if the system call is brief.

If you need evenly spaced start times for long running calls, you do need to fork them off. Here's one way to do that:

#!/usr/bin/perl -w use strict; my $count = shift; my $interval = 60 / $count; my ($pid,%kids); for (1..$count) { defined($pid = fork()) or warn $! and last; # can't die, kids to t +end $pid or exec(@ARGV); $kids{$pid}++; select(undef,undef,undef,$interval); } delete $kids{wait()} while %kids;
Using exec() instead of system() lets us avoid handling their return. They will exit on their own. The hash trick for keeping track of children is one of my favorites.

Update: Taint mode would be a very good thing for this program.

Update2 tye is correct, init will kill only if the session ends. A cron job is a session leader (at least on my Vixie cron). petral may well be right, but I took the question to be about running something every 10 seconds or so from a cron job that fires every minute.

After Compline,
Zaxo

Replies are listed 'Best First'.
(tye)Re: zombies after forking
by tye (Sage) on Jan 18, 2002 at 19:59 UTC

    Minor nit:

    unless already killed by init

    "init" doesn't kill processes that it inherits. Perhaps you are thinking of background processes getting killed when their session is terminated.

            - tye (but my friends call me "Tye")
Re: Re: zombies after forking
by petral (Curate) on Jan 21, 2002 at 02:37 UTC
      If you need evenly spaced start times for long running calls,

    I was taking his sleeps to be a quick and dirty attempt to get them all to fire at more or less the same time, even though they are started sequentially.   Kinda neat, though I'm not sure I can think of a real need for it.

      p