the most frequent setup is to direct the output of one process directly to another process
alternatively, a file can be used as intermediate repository
the function pipe() sets up two file descriptors connected by a buffer
the writing process deposits its data into the write end of the pipe (its input, or head)
the data are temporarily stored in a buffer
the reading process obtains its input data from the read end of the pipe (its output, or tail)
if the pipe's buffer is full, the writing process receives a signal indicating that it should not send any more data
if the pipe is empty, the reading process sleeps until data arrive