Why Zombie Processes Exist

Why zombie processes exist?

In your code, zombie is created on exit(0) (comment with arrow below):

pid=fork();
if (pid==0) {
exit(0); // <--- zombie is created on here
} else {
// some parent code ...
}

Why? Because you never waited on it. When something calls waitpid(pid), it returns postmortem information about process, like its exit code. Unfortunately, when process exited, kernel cannot just dispose of this process entry, or return code will be lost. So it waits for somebody to wait on it, and leaves this process entry around even if it does not really occupy any memory except for entry in process table - this is exactly what is called zombie.

You have few options to avoid creating zombies:

  1. Add waitpid() somewhere in the parent process. For example, doing this will help:

    pid=fork();
    if (pid==0) {
    exit(0);
    } else {
    waitpid(pid); // <--- this call reaps zombie
    // some parent code ...
    }
  2. Perform double fork() to obtain grandchild and exit in child while grandchild is still alive. Grandchildren will be automatically adopted by init if their parent (our child) dies, which means if grandchild dies, it will be automatically waited on by init. In other words, you need to do something like this:

    pid=fork();
    if (pid==0) {
    // child
    if (fork()==0) {
    // grandchild
    sleep(1); // sleep a bit to let child die first
    exit(0); // grandchild exits, no zombie (adopted by init)
    }
    exit(0); // child dies first
    } else {
    waitpid(pid); // still need to wait on child to avoid it zombified
    // some parent code ...
    }
  3. Explicitly ignore SIGCHLD signal in parent. When child dies, parent gets sent SIGCHLD signal which lets it react on children death. You can call waitpid() upon receiving this signal, or you can install explicit ignore signal handler (using signal() or sigaction()), which will make sure that child does not become zombie. In other words, something like this:

    signal(SIGCHLD, SIG_IGN); // <-- ignore child fate, don't let it become zombie
    pid=fork();
    if (pid==0) {
    exit(0); // <--- zombie should NOT be created here
    } else {
    // some parent code ...
    }

What are Zombies and what causes them? Are there Zombie processes and Zombie objects?

Zombie processes and zombie objects are totally unrelated. Zombie processes are when a parent starts a child process and the child process ends, but the parent doesn't pick up the child's exit code. The process object has to stay around until this happens - it consumes no resources and is dead, but it still exists - hence, 'zombie'.

Zombie objects are a debugging feature of Cocoa / CoreFoundation to help you catch memory errors - normally when an object's refcount drops to zero it's freed immediately, but that makes debugging difficult. Instead, if zombie objects are enabled, the object's memory isn't instantly freed, it's just marked as a zombie, and any further attempts to use it will be logged and you can track down where in the code the object was used past its lifetime.

EXEC_BAD_ACCESS is your run-of-the-mill "You used a bad pointer" exception, like if I did:

(*(0x42)) = 5;

Zombie process vs Orphan process

When a child exits, some process must wait on it to get its exit code. That exit code is stored in the process table until this happens. The act of reading that exit code is called "reaping" the child. Between the time a child exits and is reaped, it is called a zombie. (The whole nomenclature is a bit gruesome when you think about it; I recommend not thinking about it too much.)

Zombies only occupy space in the process table. They take no memory or CPU. However, the process table is a finite resource, and excessive zombies can fill it, meaning that no other processes can launch. Beyond that, they are bothersome clutter, and should be strongly avoided.

If a process exits with children still running (and doesn't kill its children; the metaphor continues to be bizarre), those children are orphans. Orphaned children are immediately "adopted" by init (actually, I think most people call this "reparenting," but "adoption" seems to carry the metaphor better). An orphan is just a process. It will use whatever resources it uses. It is reasonable to say that it is not an "orphan" at all since it has a parent, but I've heard them called that often.

init automatically reaps its children (adopted or otherwise). So if you exit without cleaning up your children, then they will not become zombies (at least not for more than a moment).

But long-lived zombies exist. What are they? They're the former children of an existing process that hasn't reaped them. The process may be hung. Or it may be poorly written and forgets to reap its children. Or maybe it's overloaded and hasn't gotten around to it. Or whatever. But for some reason, the parent process continues to exist (so they aren't orphans), and they haven't been waited on, so they live on as zombies in the process table.

So if you see zombies for longer than a moment, then it means that there is something wrong with the parent process, and something should be done to improve that program.

Zombie vs Defunct processes?

For Linux "defunct" and "zombie" processes are the same.

From man ps:

Processes marked <defunct> are dead processes (so-called "zombies") that remain because their parent has not destroyed them properly. These processes will be destroyed by init(8) if the parent process exits.

PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped by job control signal
t stopped by debugger during the tracing
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent

Zombie processes

-- what the benefits from zombie process concept?

A zombie process is just a pid, an exit status, and some accounting information that stays around until a parent uses one of the wait family of system calls to get its final status. Until a parent calls wait the child's process ID must stay marked as used so that no other process can be assigned it. If another process were to get assigned a recycled pid it would be difficult to tell the difference between it and previous processes that had that same pid. Once wait is called by the parent and returns a final exit status it can be assumed that no one will go looking for the child at that pid again, so the pid may now be reused.
(I think on Linux if a parent leaves SIGCHLD as SIG_IGN the kernel will not keep zombies around, but that re-registering SIGCHLD's disposition as SIG_IGN does not have the same effect)

-- know that the kernel keeps (PID,termination status, resource usage information) for zombie process what's the meaning of "resource usage information"

Some of this information is what running a program as:

time my_program

will report. These values are usually reported in the siginfo structure for SIGCHLD (which isn't exactly a call to wait) but also available from a call to the waitid form of systme call (on some systems). Look at man sigaction for info about this structure.

-- how zombie's PPID() = 1 and it still zombie , (init reaps Zombies because it wait() by default)

A zombie whose ppid = 1 should not stay a zombie for very long because init should reap it pretty quickly. A process will remain a zombie from a point soon after it dies (either via exit or by an unhanded signal that kills it) until its parent calls wait and gets it's final status. This means that even if init does nothing but call init over and over there could be a small amount of time where a process may show up as a zombie. If processes show up as children of init (0=ppid) for long amounts of time (seconds) then something is probably wrong.

-- can any one write some C code to make a zombie it's parent is Init?

This isn't clear, but I think you want:

pid_t f = fork();
if (f > 0) {
exit(0); // this is the parent dying, so the child will be an orphan
// and get adopted by init
} else if (f == 0) {
sleep(100); // This is the child doing something that takes enough time for
// its parent to commit suicide (exit(0)) and then for you to
// observe that it has now been adopted by init
exit(0); // And now it dies as well, so init should reap its status, but
// it may be a zombie for a short amount of time first.
} else /* error condition would be handled here */

-- can zombies refusing to release some lock on memory??

Zombies can't hold onto much of anything. They lose all of their memory pages, open file handles, ...etc. Pretty much everything the operating system can figure out how to free up should get freed. It would be a bug not to, but remember that the OS has to know that it is something that is supposed to be freed. It is very easy to create resources in user space that should be freed when a program dies that the OS doesn't know are supposed to be freed.

why does this program create a zombie process and how do I fix it?

You have an infinite sleep loop in your signal handler sigcallback. Once a signal has been handled by that function, the process will never handle another SIGCHLD signal again, since the signal handler never returns. This is likely the cause of your zombies.

You do not get one signal delivered for every exited child. If two children exit "simultaneously", you will only get a single SIGCHLD delivered. So, when you wait in your signal handler (either of them), you should wait in a loop to reap all the children that have exited. Something along the lines of:

for (;;) {
pid_t p = waitpid(-1, 0, WNOHANG);
if (p <= 0) break;
/* ... */
}

When your process exits, the children of that process will be inherited by the init process, so they will be reaped properly. However, your infinite loop prevented 3 things:

  • It would not reap simultaneously exited children.
  • It would not reap any more children after reaping the first.
  • It would not allow the process to exit, so the children could not be reaped by the init process.

How does a zombie process manifest itself?

steenhulthin is correct, but until it's moved someone may as well answer it here. A zombie process exists between the time that a child process terminates and the time that the parent calls one of the wait() functions to get its exit status.

A simple example:

/* Simple example that creates a zombie process. */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
pid_t cpid;
char s[4];
int status;

cpid = fork();

if (cpid == -1) {
puts("Whoops, no child process, bye.");
return 1;
}

if (cpid == 0) {
puts("Child process says 'goodbye cruel world.'");
return 0;
}

puts("Parent process now cruelly lets its child exist as\n"
"a zombie until the user presses enter.\n"
"Run 'ps aux | grep mkzombie' in another window to\n"
"see the zombie.");

fgets(s, sizeof(s), stdin);
wait(&status);
return 0;
}

Where do Zombie processes go after their parent dies?

According to man 2 wait:

A child that terminates, but has not been waited for becomes a
"zombie". The kernel maintains a minimal set of information about the
zombie process (PID, termination status, resource usage information)
in order to allow the parent to later perform a wait to obtain
information about the child. As long as a zombie is not removed from
the system via a wait, it will consume a slot in the kernel process
table, and if this table fills, it will not be possible to create
further processes. If a parent process terminates, then its "zombie"
children (if any) are adopted by init(8), which automatically performs
a wait to remove the zombies.

When the parent process finishes, the child process (even if it's a zombie process) will be adopted by init. Then, as you said, init will wait() for its exit status.

So, I don't think "orphan zombie" to be any special case.



Related Topics



Leave a reply



Submit