Re: open(KID, "-|") and wait()?
by Improv (Pilgrim) on Apr 10, 2003 at 13:38 UTC
|
From the open() perldoc,
Closing any piped filehandle causes the parent process to wait
for the child to finish, and returns the status value in $?.
In other words, perl will handle it for you when you call
close (which may block until the child finishes). | [reply] |
Re: open(KID, "-|") and wait()?
by robartes (Priest) on Apr 10, 2003 at 13:38 UTC
|
The answer is 'yes'. Zombies are created when a child has exited, but the parent has not wait()ed for it yet. Zombies are no good (they tend to eat nice friendly old ladies, for example). One way of ensuring that the parent always acts correctly upon the death of a child is installing a signal handler for SIGCHLD:
$SIG{'CHLD'}=sub {
my $pid=wait();
if ($pid == -1 ) {
print "SIGCHLD handler called without dead child.\n";
return;
}
print "Child $pid exited with $?\n";
}
That way, whatever else the parent is doing, it will normally always catch a SIGCHLD. The only exception to this is if the parent is in uninterruptible sleep (e.g. when it's waiting on an NFS op on a filesystem whose server has gone away) - then it becomes night of the living dead :).
Update: In the case of an open()ed child, as apparently the OP is using (bad robartes, I had parsed the snippet as a standard fork and exec), see Improv's remark. However, installing a SIGCHLD handler can never hurt, for example in the case where the child process abnormally (or even normally) terminates before the parent closes the filehandle.
CU Robartes- | [reply] [d/l] |
|
|
| [reply] |
|
|
May I make another suggestion? This is originally from the Perl 4 Camel, but it actually works very well to prevent zombies:
unless (fork) { # this is the child
unless (fork) { # this is the grandchild
exec "yourprogram"; # the detached process
# can also be perl code and exit(0)
} # exits when done
# Back in the child
exit 0; # immediately terminates
}
wait; # gets rid of the terminated child immediately
This works because the child process exits immediately and
is wait()ed on immediately, so no zombie. The grandchild ends up as a child of init because its parent has exited, and
init will take care of reaping the grandchild for you. | [reply] |
|
|
"Zombies are created when a child has exited, but the parent has not wait()ed for it yet."
We have a little bit misunderstanding of "zombie" here ;-) Zombie is actually a process still running, but we lost track of it. In other words, in the fork case, if the parent created a child that cannot exit on its own, say it has some sort of dead loop, whatever the dead loop is created by mistake or purposely, as a good practice, the parent process should kill the child process. Otherwise, the child becomes a zombie, as it will run forever, and it will have that seat in the process table forever.
If a process exited, it will be removed from the process table, doesn't matter whether it is created by a parent process, or it runs on its own, as it is a process any way.
Other than the zombie situation, there are also other cases you may want to waitpid on your child process. For example, a parent process keeps creating child processes, and delegating tasks to them at a high speed. If you don't waitpid() to control the number of child processes you created and still alive, you would soon see an error message saying that, you cannot create child process, as resource used up.
In this case, you would keep a counter of running child processes, and when it reaches a predefined max, stop creating child process, and start waitpid(), until the number of running child processes drop below a safe line.
Also a little comment about SIGCHLD. You cannot capture SIGCHLD on win32, so the approach is not fully portable.
| [reply] |
|
|
Q. How do you kill a zombie?
A. You can't, it's already dead.
Zombies are not running. They have terminated, but they have a living parent who either hasn't waited for them, or hasn't told the kernel that it doesn't want to do that.
Zombies are created because child processes run asynchronously from their parents, and often terminate long before the parent has had a chance to call wait/waitpid on them. The kernel has to keep some minimal information about such child processes in the table so that the parent can have a chance to collect its status, but releases the memory & other resources they might be holding
| [reply] |
|
|
Zombie is actually a process still running, but we lost track of it. In other words, in the fork case, if the parent created a child that cannot exit on its own, say it has some sort of dead loop, whatever the dead loop is created by mistake or purposely, as a good practice, the parent process should kill the child process. Otherwise, the child becomes a zombie, as it will run forever, and it will have that seat in the process table forever.
Ehm, I beg to differ. A zombie process is not what you describe here. A zombie process is one that is no longer running (it has exited), but its parent has not yet collected its exit status, so the kernel keeps it around in the process table. What you describe is a runaway process, or even a perfectly normal daemon. Something like inetd or sendmail fits your description, and I do not think many people will call those zombies.
If a process exited, it will be removed from the process table, doesn't matter whether it is created by a parent process, or it runs on its own, as it is a process any way.
Except when its a child process - it exits but stays in the process table unless its parent collects its status.
Update: Added point about daemons.
YAU: After a chatterbox conversation with pg, we've decided that we're both right - different sources mean different things with 'zombie process'. YMMV.
CU Robartes-
| [reply] |
Re: open(KID, "-|") and wait()?
by edan (Curate) on Apr 10, 2003 at 13:48 UTC
|
Improv++ - for reading the open() docs more carefully than me (sheesh, it's even got its own paragraph there, and silly old me didn't see it!?)
robartes++ - for zombie humor, even though I think Improv's answer is better in this case - your answer is great for the general fork() case, though!
My next question: how do I prevent the close() from blocking until the child exits?
--
3dan
| [reply] |
|
|
how do I prevent the close() from blocking until the child exits?
You could delay the close until you know for sure the child process has finished. Use the non blocking version of waitpid for this. From the docs:
use POSIX ":sys_wait_h";
$kid = waitpid(-1, WNOHANG);
This will see if any child has died (without blocking) and return the PID of said child (and the return value in $?, as normal). You can also wait for a specific child by using the pid of that child as first argument to waitpid.
Using this, you can make sure you only close the filehandle when the child has already exited. Just periodically check whether the child is dead, and close its filehandle if it is (you'll get a -1 return from the close, but that should be no problem).
Unfortunately, this is not portable to all OS. See waitpid for more information.
Update: 'now' ne 'know'
CU Robartes- | [reply] [d/l] |
|
|
I'd suggest SIG{CHLD} = 'IGNORE'; unless you care what the child processes get up to after they spring into life.
| [reply] [d/l] |