5

I am wondering if the following code can create zombies:

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

int main(){
    int i=1;
    pid_t p;
    p = fork();
    i++;
    if(p!=0){
        waitpid(p, NULL, 0);
    }
    printf("%d\n",i);
    return 0;
}

So, the parent process calls the waitpid for the child process, which returns immediately if the child has not already exited. So, no zombies can arise so far. But, if the child exits before

return 0;
command this would be a zombie then? I am actually confused about it. Should the waitpid be the last line of code before the program terminates? Any help would be appreciated. Thanks!
mgus
  • 808
  • 4
  • 17
  • 39
  • 1
    That looks ok but you may have a misunderstanding. The `waitpid` will return immediately if the child already died; otherwise it will block until the child does die. Unless you use WNOHANG with the waitpid in which case it won't block but that is not at issue here. – Duck Dec 15 '13 at 19:43
  • Correct me if I am wrong, but I think that the third parameter(zero) is equivalent to WHOHANG. Anyway, let's suppose it was WHOHANG. Would then a zombie be created and how? And finally, should the waitpid be the last command before return 0; to ensure that no zombies will be created?. Thanks again! – mgus Dec 15 '13 at 19:51
  • 1
    No WNOHANG is definitely not equivalent to 0. Zero is the default and it would make no sense to bitwise-or WNOHANG if was equal to 0. If the child died after a non-blocking `waitpid` and the parent exited first then you would have a zombie. But normally you would use a non-blocking `waitpid` in a loop and possibly in conjunction with receiving SIGCHLD. Waitpid itself can be anywhere in the code so long as it handles your children when called upon to do so. – Duck Dec 15 '13 at 19:57
  • Thanks for your reply! I have one more question: If the parent process call `waitpid(p, NULL, WHOHANG);` before the child dies then the child would become a zombie, right? So, in the above code what prevents the child from dying after the call of `waitpid` and thus becoming a zombie? – mgus Dec 15 '13 at 21:08
  • In the above code you aren't using WNOHANG so there are two scenarios. (1) The child is already dead when `waitpid` is called and waitpid reaps it right away and returns. (2) The child hasn't died and `waitpid` blocks for as long as necessary waiting for the child to die. When it does it gets reaped and waitpid returns. Hope that helps. – Duck Dec 15 '13 at 23:01
  • I got that but I have a problem with the second scenario. The problem is that when I execute this code both parent and child execute the printf command, which means that they are still alive. So, if the child is alive the waitpid would block and wait until it dies. So, only one printf or no printf should be executed. Is that correct? – mgus Dec 16 '13 at 08:27
  • Yes but after the parent reaps the child with `waitpid` the code is going to fall through and execute printf hence two printfs. If you don't want that to happen `exit` the parent in the if statement after the `waitpid`. – Duck Dec 16 '13 at 13:44
  • How can a dead process (in this case the child) execute source code (printf)? Thanks, again – mgus Dec 26 '13 at 16:40

2 Answers2

6

The child only becomes a zombie if it ends and the parent doesn't call wait*() as long as itself lives on.

In the moment the parent also ends the child is reaped by the init process which will take care to call wait*() on the child, so it will finally end and with this leave the zombie state and disappears from the process list.

To provoke the child created in your example code to become a zombie modify the code for example as follows:

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

int main(void) 
{
    pid_t p = fork();

    if (p != 0) 
    {
        waitpid(p, NULL, 0); /* See if the child already had ended. */
        sleep(1); /* Wait 1 seconds for the child to end. And eat away the SIGCHLD in case if arrived. */
        pause(); /* Suspend main task. */
    }
    else
    {
        sleep(3); /* Just let the child live for some tme before becoming a zombie. */
    }

    return 0;
}

Due to the two following facts:

  • the child sleeps for 3s so the parent's call to waitpid() most probably will always fail
  • the default handling of SIGCHLD is to ignrore it.

the code above in fact is the same as:

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

int main(void) 
{
    pid_t p = fork();

    if (p != 0) 
    {
        pause(); /* Suspend main task. */
    }
    else
    {
        sleep(3); /* Just let the child live for some tme before becoming a zombie. */
    }

    return 0;
}
alk
  • 69,737
  • 10
  • 105
  • 255
  • What's in this code that prevents the child from becoming a zombie? Can you explain in more detail why you used sleep and pause commands? Sorry for so many questions but I am not familiar with these notions. – mgus Dec 24 '13 at 21:41
  • @Konstantinos Konstantinidis: "*What's in this code that prevents the child from becoming a zombie?*" Nothing. As stated in my answer, this code **does** create a zombie. – alk Dec 25 '13 at 08:42
0

I found a simple way to create a zombie process and test it using ps -e

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

void  main()
{
    pid_t pid;
    pid = fork();
    //parent sleeps while the child has exited
    //not an orphan since parent still alive
    //child will be still present in process table
    if(pid==0)
    {//child
        exit(0);
    }
    else
    {//parent
        sleep(15);
    }
}

run ps -e while within the 15 seconds... you will see

6454 pts/2 00:00:00 a.out < defunct >

aneesh joshi
  • 553
  • 5
  • 11