Inter
Process Communication
PIPE:
We
use the term pipe when we connect a data flow from one process to
another. Generally we attach, or pipe, the output of one process to
the input of another.
Most
Linux users will already be familiar with the idea of linking shell
commands together, so that the output of one process is fed straight
to the input of another. For shell commands, this is entered as
cmd1
| cmd2
The
shell arranges the standard input and output of the two commands, so
that
❑ The
standard input to cmd1 comes from the terminal keyboard.
❑ The
standard output from cmd1 is fed to cmd2 as its standard input.
❑ The
standard output from cmd2 is connected to the terminal screen.
What
the shell has done, in effect, is reconnect the standard input and
output streams so that data flows from the keyboard input through the
two commands and is then output to the screen.
The
PIPE Call:
The
pipe function has the following prototype:
#include
<unistd.h>
int
pipe(int file_descriptor[2]);
pipe
is passed (a pointer to) an array of two integer file descriptors. It
fills the array with two new file descriptors and returns a zero. On
failure, it returns -1 and sets errno to indicate the reason for
failure. Errors defined in the Linux manual page (section 2 of the
manual) are
❑ EMFILE:
Too many file descriptors are in use by the process.
❑ ENFILE:
The system file table is full.
❑ EFAULT:
The file descriptor is not valid.
The
two file descriptors returned are connected in a special way. Any
data written to file_descriptor[1] can be read back from
file_descriptor[0]. The data is processed in a first in, first out
basis, usually abbreviated to FIFO. This means that if you write the
bytes 1, 2, 3 to file_descriptor[1], reading from file_descriptor[0]
will produce 1, 2, 3. This is different from a stack, which operates
on a last in, first out basis, usually abbreviated to LIFO.
Here’s
a program, pipe1.c, that uses pipe to create a pipe.
Type
the following code.
Note
the file_pipes pointer, which is passed to the pipe function as a
parameter.
#include
<unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
int
main()
{
int
data_processed;
int
file_pipes[2];
const
char some_data[] = “123”;
char
buffer[BUFSIZ + 1];
memset(buffer,
‘\0’, sizeof(buffer));
if
(pipe(file_pipes) == 0)
{
data_processed
= write(file_pipes[1], some_data, strlen(some_data));
printf(“Wrote
%d bytes\n”, data_processed);
data_processed
= read(file_pipes[0], buffer, BUFSIZ);
printf(“Read
%d bytes: %s\n”, data_processed, buffer);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
When
we run this program, the output is
$
./pipe1
Wrote
3 bytes
Read
3 bytes: 123
How
It Works
The
program creates a pipe using the two file descriptors file_pipes. It
then writes data into the pipe using the file descriptor file_pipes
and reads it back from file_pipes. Notice that the pipe has some
internal buffering that stores the data in between the calls to write
and read.
You
should be aware that the effect of trying to write using
file_descriptor, or read using file_descriptor, is undefined, so the
behavior could be very strange and may change without warning. On the
authors’ systems, such calls fail with a –1 return value, which
at least ensures that it’s easy to catch this mistake.
At
first glance, this example of a pipe doesn’t seem to offer us
anything that we couldn’t have done with a simple file. The real
advantage of pipes comes when you wish to pass data between two
processes.
No comments:
Post a Comment