Why fork() twice [duplicate]

Solution 1:

All right, so now first of all: what is a zombie process?

It's a process that is dead, but its parent was busy doing some other work, hence it could not collect the child's exit status.

In some cases, the child runs for a very long time, the parent cannot wait for that long, and will continue with it's work (note that the parent doesn't die, but continues its remaining tasks but doesn't care about the child).

In this way, a zombie process is created.


Now let's get down to business. How does forking twice help here?

The important thing to note is that the grandchild does the work which the parent process wants its child to do.

Now the first time fork is called, the first child simply forks again and exits. This way, the parent doesn't have to wait for a long time to collect the child's exit status (since the child's only job is to create another child and exit). So, the first child doesn't become a zombie.

As for the grandchild, its parent has already died. Hence the grandchild will be adopted by the init process, which always collects the exit status of all its child processes. So, now the parent doesn't have to wait for very long, and no zombie process will be created.


There are other ways to avoid a zombie process; this is just a common technique.


Hope this helps!

Solution 2:

In Linux, a daemon is typically created by forking twice with the intermediate process exiting after forking the grandchild. This has the effect of orphaning the grandchild process. As a result, it becomes the responsibility of the OS to clean up after it if it terminates. The reason has to do with what are known as zombie processes which continue to live and consume resources after exiting because their parent, who'd normally be responsible for the cleaning up, has also died.

Solution 3:

Also from the documentation,

Normally Nagios will fork() twice when it executes host and service checks. This is done to (1) ensure a high level of resistance against plugins that go awry and segfault and (2) make the OS deal with cleaning up the grandchild process once it exits.

Solution 4:

Unix Programming Faq §1.6.2:

1.6.2 How do I prevent them from occuring?

You need to ensure that your parent process calls wait() (or waitpid(), wait3(), etc.) for every child process that terminates; or, on some systems, you can instruct the system that you are uninterested in child exit states.

Another approach is to fork() twice, and have the immediate child process exit straight away. This causes the grandchild process to be orphaned, so the init process is responsible for cleaning it up. For code to do this, see the function fork2() in the examples section.

To ignore child exit states, you need to do the following (check your system's manpages to see if this works):

     struct sigaction sa;
     sa.sa_handler = SIG_IGN;
 #ifdef SA_NOCLDWAIT
     sa.sa_flags = SA_NOCLDWAIT;
 #else
     sa.sa_flags = 0;
 #endif
     sigemptyset(&sa.sa_mask);
     sigaction(SIGCHLD, &sa, NULL);

If this is successful, then the wait() functions are prevented from working; if any of them are called, they will wait until all child processes have terminated, then return failure with errno == ECHILD.

The other technique is to catch the SIGCHLD signal, and have the signal handler call waitpid() or wait3(). See the examples section for a complete program.

Solution 5:

This code demonstrates how to use the double fork method to allow the grandchild process to become adopted by init, without risk of zombie processes.

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

int main()
{
    pid_t p1 = fork();

    if (p1 != 0)
    {
        printf("p1 process id is %d", getpid());
        wait();
        system("ps");
    }
    else
    {
        pid_t p2 = fork();
        int pid = getpid();

        if (p2 != 0) 
        {
            printf("p2 process id is %d", pid);
        }
        else
        {
            printf("p3 process id is %d", pid);
        }

        exit(0);
    }
}

The parent will fork the new child process, and then wait for it to finish. The child will fork a grandchild process, and then exit(0).

In this case, the grandchild doesn't do anything except exit(0), but could be made to do whatever you'd like the daemon process to do. The grandchild may live long and will be reclaimed by the init process, when it is complete.