Basics of Inter process communication- PIPE


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