Working with pipes

Pipes are unnamed objects created to allow two processes to communicate.

One process reads and the other process writes to the pipe file. This unique type of file is also called a first-in-first-out (FIFO) file. The data blocks of the FIFO are manipulated in a circular queue, maintaining read and write pointers internally to preserve the FIFO order of data. The PIPE_BUF system variable, defined in the limits.h file, designates the maximum number of bytes guaranteed to be atomic when written to a pipe.

The shell uses unnamed pipes to implement command pipelining. Most unnamed pipes are created by the shell. The | (vertical) symbol represents a pipe between processes. In the following example, the output of the ls command is printed to the screen:
ls | pr

Pipes are treated as regular files as much is possible. Normally, the current offset information is stored in the system file table. However, because pipes are shared by processes, the read/write pointers must be specific to the file, not to the process. File table entries are created by the open subroutine and are unique to the open process, not to the file. Processes with access to pipes share the access through common system file table entries.

Using pipe subroutines

The pipe subroutine creates an interprocess channel and returns two file descriptors. File descriptor 0 is opened for reading. File descriptor 1 is opened for writing. The read operation accesses the data on a FIFO basis. These file descriptors are used with read, write, and close subroutines.

In the following example, a child process is created and sends its process ID back through a pipe:
#include <sys/types.h>
main()
{
        int p[2];
        char buf[80];
        pid_t pid;

        if (pipe(p))
        {
                  perror("pipe failed");
                exit(1)'
        }
        if ((pid=fork()) == 0)
        {
                                       /* in child process */
                close(p[0]);           /*close unused read */
                                       *side of the pipe */
                sprintf(buf,"%d",getpid()); 
                                       /*construct data */
                                       /*to send */
                write(p[1],buf,strlen(buf)+1);
                        /*write it out, including
                        /*null byte */
                exit(0);
        }
                                        /*in parent process*/
        close(p[1]);                    /*close unused write side of pipe */
        read(p[0],buf,sizeof(buf));     /*read the pipe*/
        printf("Child process said: %s/n", buf);
                                       /*display the result */
        exit(0);
}

If a process reads an empty pipe, the process waits until data arrives. If a process writes to a pipe that is too full (PIPE_BUF), the process waits until space is available. If the write side of the pipe is closed, a subsequent read operation to the pipe returns an end-of-file.

Other subroutines that control pipes are the popen and pclose subroutines:
popen
Creates the pipe (using the pipe subroutine) then forks to create a copy of the caller. The child process decides whether it is supposed to read or write, closes the other side of the pipe, then calls the shell (using the execl subroutine) to run the desired process.

The parent closes the end of the pipe it did not use. These closes are necessary to make end-of-file tests work correctly. For example, if a child process intended to read the pipe does not close the write end of the pipe, it will never see the end of file condition on the pipe, because there is one write process potentially active.

The conventional way to associate the pipe descriptor with the standard input of a process is:

close(p[1]);
close(0);
dup(p[0]);
close(p[0]);

The close subroutine disconnects file descriptor 0, the standard input. The dup subroutine returns a duplicate of an already open file descriptor. File descriptors are assigned in ascending order and the first available one is returned. The effect of the dup subroutine is to copy the file descriptor for the pipe (read side) to file descriptor 0, thus standard input becomes the read side of the pipe. Finally, the previous read side is closed. The process is similar for a child process to write from a parent.

pclose
Closes a pipe between the calling program and a shell command to be executed. Use the pclose subroutine to close any stream opened with the popen subroutine.

The pclose subroutine waits for the associated process to end, then closes and returns the exit status of the command. This subroutine is preferable to the close subroutine because pclose waits for child processes to finish before closing the pipe. Equally important, when a process creates several children, only a bounded number of unfinished child processes can exist, even if some of them have completed their tasks. Performing the wait allows child processes to complete their tasks.