Lab 8: On zombies, wait() and signal()

Many students have reported problems with zombies in Lab 8.

I did not think it would be this hard to prevent zombies, so this is unintended, partly due to last minute change of platform (from Linux to Solaris), and confusion due to the skeleton code given.

I would rather you spend time handling the pipes properly (remember to close every unused ends!) than worrying about zombies.

So, here is a way to remove zombies in Lab 8.

replace the line

 
waitpid(pid, NULL, NULL)

with

for (i = 0; i < num_of_cmds; i++)
    waitpid(-1, NULL, NULL);

This line ensures that the parent will wait as many times as child processes forked before returning to the prompt.

Next, I will describe several other ways that does not work or tricky to get it to work.

One solution that you might have done is to move the waitpid() statement into the for loop. So, in effect, bush is calling fork(), wait(), fork(), wait().. The impact of this change is that, the second command in the pipeline will not start until after the first command finishes execution. This is problematic, because the first command might not exit or might wait for the second command to read from the pipe (e.g., when the pipe is full). Example of a pipe that can cause trouble for this solution is “yes | head”.

Another solution is to wait() asynchronously with signals like what you have done in Lab 4. Using signal is not strictly necessary in Lab 8 since we do not support background processes. But it is not wrong to use signal either. The problem, however, comes from the subtle different semantic of signal() on Linux and Solaris. While on Linux, once you install a signal, it is there to stay. On Solaris, however, after the signal handler is called, the signal handler is reset back to the default handler. To make the signal handler you installed persistent, you have to reinstall it again and again by calling signal() within the signal handler. So if you have used this approach, either use sigaction() or call signal() from within the signal handler to reinstall the handler.

15 thoughts on “Lab 8: On zombies, wait() and signal()

  1. Dear prof,

    if using ur approach to handle the zombie processes, since we donno which child processes will be chosen to run, will there be cases where child 2 read from fd(0) but fd(1) not yet written by 1st child ?

    I am using the fork(), wait(), fork(), wait() approach now.

    Thank you.

    -Steven

    • Yes, it is possible. But since read() will block if there is nothing to read, the 2nd child would get suspended, until eventually the 1st child runs and writes to the pipe.

      This situation is just like the producer/consumer problem.

  2. Hello Professor,
    In a command such as yes | head, the child process (represented by the yes command) would never stop writing to the pipe, and yet, the parent process can only close its own access to the pipes. In this case, how would the pipe be closed from the child process? Thanks!

    • Before exec( ) the shell is responsible for closing unused ends of the pipe.

      After exec( ), there is nothing much the shell can do — the implementation of “yes” will have to take care of itself. Typically a command like this would keep writing until there is an error in writing. To be more precise, until write( ) returns EPIPE. In which case it should stop writing, close the pipe, and exit.

Leave a Reply

Your email address will not be published. Required fields are marked *