Discussion:
how do I avoiding zombie processes without interfering with " die if system(ls) " type calls
(too old to reply)
Rahul
2006-01-06 04:04:54 UTC
Permalink
Hi

I have a script main.pl as follows



for ($i = 1; $i<=5000; $i++)
{
&foo();
}




sub foo {

if (fork() == 0){
# inside CHILD
# do something
exit 0;
else {
# inside PARENT
# do nothing
}
}

now, since the parent does not wait upon the child to finish (i don't want
it to explicitly wait, since i want to have multiple children run
simultaneously), so there are lot of zombie children left, thereby making
the system slow, and finally hang.

i researched a lot, there are 2 ways to avoid this.

1) $SIG{'CHLD'} = 'IGNORE';
2) $SIG{'CHLD'} = sub { while(waitpid(-1,WNOHANG)>0) {} } ;

also I have lots of calls such as
die if system("ls")
die if system("rm ..")
etc in my main parent routine as well as the child part of the foo()
routine...

So if I follow method 1) above to avoid zombie, the system() calls rreturn
error (as the inherent 'wait' has no child process to wait upon , as SIGCHLD
is ignored) (The ERRORS are seen only on perl 5.8.6 and not on perl 5.6.1)

If I follow method 2) above to avoid zombies, by writing my own handler, the
system() calls wait indefinitely as the child's SIGCHLD has already been
taken care of by my handler.


Could some1 help me avoid zombies without interfering with my system() calls
?

thanks,
Rahul
Tom Phoenix
2006-01-06 16:58:06 UTC
Permalink
Post by Rahul
if (fork() == 0){
Because your process table can become full and temporarily prevent
forking new processes, you should check the return value of fork
whenever you're making more than N processes. The value of N depends
upon a number of factors, so I generally assume N=0. Most of the time,
I just call this safe_fork routine in place of fork, since this
handles the retries for me automatically.

sub safe_fork () {
use Errno;
my $retries = 10;
while ($retries--) {
my $rv = fork;
return $rv if defined $rv; # it worked
return unless $retries;
return unless $!{EAGAIN};
sleep 3;
}
die "Well, how did I get here?";
}
Post by Rahul
now, since the parent does not wait upon the child to finish (i don't want
it to explicitly wait, since i want to have multiple children run
simultaneously), so there are lot of zombie children left, thereby making
the system slow, and finally hang.
i researched a lot, there are 2 ways to avoid this.
1) $SIG{'CHLD'} = 'IGNORE';
2) $SIG{'CHLD'} = sub { while(waitpid(-1,WNOHANG)>0) {} } ;
There are other ways, too. One popular way is the "double-fork" trick.
Instead of making a child process, you make a grandchild process. That
is, the first fork makes a child process which forks again to make the
grandchild, then exits. The grandchild does the work, the parent
continues running. Because the grandchild process's parent process is
gone, the responsibility for reaping the zombie passes to init(8). At
least, that's what happens on most Unix-like systems.

If you're starting many such processes, you can use one child process
to start a number of grandchild processes at once, limited of course
by your system's abilities. That's more efficient than starting just a
single grandchild per child process.

And with all this talk about fork, I have to ask everyone playing
along at home whether they know what you can get when your program has
both a fork and an infinite loop? If you're the only user on your box,
you can do whatever you want. But other users may not appreciate a
fork bomb.

http://catb.org/~esr/jargon/html/F/fork-bomb.html

Cheers!

--Tom Phoenix
Stonehenge Perl Training
u***@DavidFilmer.com
2006-01-06 08:12:23 UTC
Permalink
Post by Rahul
die if system("ls")
die if system("rm ..")
You will have FAR fewer problems if you stop treating Perl like bash or
ksh (which are simply frameworks to execute system commands) and start
treating Perl like a programming language with its own system
interaction command set. Instead of shelling out to system calls to do
stuff like this, use Perl functions (such as readdir and unlink,
instead of 'ls' and 'rm').

Every time you execute a system command, you invoke a new shell
instance. Stop doing that, and your problems will (most likely) simply
go away.
--
http://DavidFilmer.com
Loading...