You will certainly have encountered pipes before, even if you have not yet written any programs that use one - the shell uses them all the time, whenever you get it to execute a pipeline where the output of one process is sent to the input of another:
$ ls | wc -w
The pipe overcomes both the problems of using files. First, a pipe is a fixed-size buffer (4096 bytes in Linux) so that its size can't grow unchecked like a file.
The consequence of using a single fixed-sized buffer is that, on writing, the pipe can become full. When this happens subsequent write() calls to the pipe will block by default, waiting for some data to be read to make enough room for the write() to take place. Notice that reading data from a pipe is a one-off operation and that the data, once read, is discarded from the pipe to free up the space for more data to be written.
Second, it is also possible, if the reader is working faster than the writer, for the pipe to become empty when all the current data has been read. When this happens a subsequent read() call will block by default, waiting for more data to be written. This solves the read() returning end-of-file problem.
A pipe is created using the pipe() system call rather than open() because different parameters are required and open() could not easily have been pressed into service in this case. The prototype for the pipe() call is:
#include <unistd.h> int pipe(int fd[2]);
There are two file descriptors associated with a pipe, one for the read() end and one for the write() end. Because a function call cannot return two values, the parameter to pipe() is a pointer to a two element int array which will be filled by the pipe() call with the two required file descriptors. The fd[0] element will contain the file descriptor for the read() end of the pipe, while fd[1] contains the file descriptor for the write() end.
Notice that there is no pathname given as a parameter to pipe(). This means that when a pipe is created it does not have a directory link created for it. As a consequence, other existing processes have no means of obtaining a file descriptor for the pipe and thus cannot access it. How, then, do two processes manage to use a pipe to communicate?
Remember that the fork() and exec() system calls can ensure that copies of the file descriptors available to a parent process are also available to its children. This information gives the required mechanism. What happens is that a process will create the pipe with a pipe() system call and then fork() one or more child processes, all of which will have file descriptors to the pipe available to them.
The implication here is that an ordinary pipe can only be used between two processes that share a common ancestor, and that this ancestor must have created the pipe for them both to use.
The data in a pipe is always read in the same order that it was written. This means that the lseek() system call has no effect with pipes.
A simple program to set up and use a pipe between two proceses follows:
#include <unistd.h> #include <stdio.h> main() { int fda[2]; char buf[1]; if (pipe (fda) ==-1) /* Create the pipe */ fatal("creating pipe"); switch (fork()) { case -1: fatal("forking child"); break; case 0: /* Child process is pipe reader */ close (fda[1]); /* Close write end of pipe */ read(fda[0], buf, 1); printf ("%c\n", buf [O]); break; default: /* Parent process is pipe writer */ close(fda[0]); /* Close read end of pipe */ write(fda[1] , "a", 1); break; } fatal(char *mess) { fprintf(stderr, "Error: %s\n", mess); exit(i); }
Notice in this example that each of the two processes closes the end of the pipe that it does not need. This is important so that the end-of-file condition is transmitted properly to reading processes when the writing processes all close the pipe.
Blocking reads and writes form the default action for a pipe which is empty or full respectively. These defaults can be overridden by setting the O_NONBLOCK flag on the pipe file descriptors with the fcntl() system call:
#include <fcntl.h> fcntl(fd, F_SETFL, O_NONBLOCK);