Question

This code below is for executing ls -l | wc -l. In the code, if I comment close(p[1]) in parent then the program just hangs, waiting for some input. Why it is so? The child writes output of ls on p1 and parent should have taken that output from p0.

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

main ()
{
  int i;
  int p[2];
  pid_t ret;
  pipe (p);
  ret = fork ();

  if (ret == 0)
    {
      close (1);
      dup (p[1]);
      close (p[0]);
      execlp ("ls", "ls", "-l", (char *) 0);
    }

  if (ret > 0)
    {
      close (0);
      dup (p[0]);
      //Doubt, Commenting the line below does not work WHy?
      close (p[1]);
      wait (NULL);
      execlp ("wc", "wc", "-l", (char *) 0);
    }
}
Was it helpful?

Solution

pipe + fork creates 4 file descriptors, two are inputs

Before the fork you have a single pipe with one input and one output.

After the fork you will have a single pipe with two inputs and two outputs.

If you have two inputs for the pipe (that a proc writes to) and two outputs (that a proc reads from), you need to close the other input or the reader will also have a pipe input which never gets closed.

In your case the parent is the reader, and in addition to the output end of the pipe, it has an open other end, or input end, of the pipe that stuff could, in theory, be written to. As a result, the pipe never sends an eof, because when the child exits the pipe is still open due to the parent's unused fd.

So the parent deadlocks, waiting forever for it to write to itself.

OTHER TIPS

Note that 'dup(p[1])' means you have two file descriptors pointing to the same file. It does not close p[1]; you should do that explicitly. Likewise with 'dup(p[0])'. Note that a file descriptor reading from a pipe only returns zero bytes (EOF) when there are no open write file descriptors for the pipe; until the last write descriptor is closed, the reading process will hang indefinitely. If you dup() the write end, there are two open file descriptors to the write end, and both must be closed before the reading process gets EOF.

You also do not need or want the wait() call in your code. If the ls listing is bigger than a pipe can hold, your processes will deadlock, with the child waiting for ls to complete and ls waiting for the child to get on with reading the data it has written.

When the redundant material is stripped out, the working code becomes:

#include <unistd.h>

int main(void)
{
    int p[2];
    pid_t ret;
    pipe(p);
    ret = fork();

    if (ret == 0)
    {
        close(1);
        dup(p[1]);
        close(p[0]);
        close(p[1]);
        execlp("ls", "ls", "-l", (char *) 0);
    } 
    else if (ret > 0)
    {
        close(0);
        dup(p[0]);
        close(p[0]);
        close(p[1]);
        execlp("wc", "wc", "-l", (char *) 0);
    }
    return(-1);
}

On Solaris 10, this compiles without warning with:

Black JL: gcc -Wall -Werror -Wmissing-prototypes -Wstrict-prototypes -o x x.c
Black JL: ./x
      77
Black JL:

If the child doesn't close p[1], then that FD is open in two processes -- parent and child. The parent eventually closes it, but the child never does -- so the FD stays open. Therefore any reader of that FD (the child in this case) is going to wait forever just in case more writing it's gonna be done on it... it ain't, but the reader just doesn't KNOW!-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top