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!").
- 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.
- When $i is 1, child1 starts sleeping for 30 seconds. Parent immediately loops to:
- $i is 2, child2 starts sleeping for 15 seconds. Parent immediately loops to:
- $i is 3, child3 starts sleeping for 7 seconds. Parent is done with the loop, so...
- Parent runs system('echo', 'Foo!'), and exits. All the kids are still sleeping. System init process inherits the kids.
- Child3 runs system('echo', 'Foo!') six or seven seconds later, unless already killed by init.
- Child2 runs system('echo', 'Foo!') seven or eight seconds later, unless already killed by init.
- 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
| [reply] [d/l] [select] |
| [reply] |
  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
| [reply] |
Zombies are formed when a child dies but the parent is still active. At any stage, the parent may ask for the return status of the child, hence it can't be removed completely: you have these zombies hanging around just in case. So, they are completely normal, and don't take up resources, etc. They are removed when the parent exits, since they are no longer needed.
The only way you can get rid of them is to prove to the system they are no longer needed: i.e., the parent must be dead already. Daemons do this by forking twice and having the first child quit immediately, which 'detaches' the second child from the original parent. However, you fork a number of times over a period of time, so this probably won't gain you anything in terms of reduced numbers of zombies.
| [reply] |
Unfortunatly this could produce zombies. How could I prevent this?
A fairly standard perl "reaper" construct is:
use POSIX qw/WNOHANG/;
## ...
$SIG{ CHLD } = sub {
while ( waitpid( -1, WNOHANG ) > 0 )
{ ## what to do, empty here }
};
## fork down here
Look up wait() and waitpid(), those are the functions to help you with terminating children.
The following is a little touched up verbose version you can build from.
#!/usr/bin/perl -w
use strict;
use POSIX qw/WNOHANG/;
my $time = 5;
$SIG{ CHLD } = sub {
while ( ( my $x = waitpid( -1, WNOHANG ) ) > 0 )
{ print "\tprocess '$x' got reaped.\n" }
};
my @toDoList = (
'ls -la', 'echo Yay!',
'date', 'perl -V',
);
foreach my $doIt ( @toDoList ) {
my $child = fork();
defined $child or die "Can not fork: $!";
if ( $child > 0 ) { ## parent
print "Parent of $child sleeping for $time seconds . . .\n";
sleep( $time );
print "Parent of $child done sleeping!\n";
} else { ## child
print "Child, going to do '$doIt'\n";
exec( "$doIt && echo '** Finished \"$doIt\" **'" );
}
}
print "\nDone.\n\n";
exit(0);
I added && echo '** Finished \"$doIt\" **' to the exec call just to be even more verbose, it is not needed, nor wanted, in your actual code
Will perl for money
JJ Knitis
(901) 756-7693
gt8073a@industrialmusic.com | [reply] [d/l] [select] |
I'd like to add to all posts above that if you don't want to wait for child processes to finish you can just set SIGCHILD handler to 'IGNORE':
$SIG{CHILD} = 'IGNORE';
You can find some information and examples on fork in perlipc
--
Ilya Martynov
(http://martynov.org/)
| [reply] [d/l] |