mario::konrad
programming / C++ / sailing / nerd stuff
Multiprocessing: Tutorial 02
© 2002 / Mario Konrad

What is a Pipe?

The name says it already: a pipe is some kind of a tube, on the one end you put something in and on the other end it comes out. You could say: it’s some kind of transportation channel.

The good we like to transport are information, data. One process produces them and like to pass them to another process. That’s what pipes are good for. There are some programs that have other needs, in many cases are pipes not the right choice, but when it comes to some kind of “streaming data” between processes, pipes are surly a good choice.

So far so good, but how are pipes creates and do I use them in conjunction with processes? Those are questions which this tutorial is about.

The Program

The program is very simple. All it does is let the parent process generate the characters 0 through 9. The characters are sent through the pipe to the child process, which is displaying the characters.

Walkthrough

Business as usual: first the header inclusion.

#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>

The program is very small, so there’s no need to use functions. Let’s do all in the function main.

int main(int argc, char ** argv)
{

There are a few variables to define. pit_t pid is to hold the id of the new created process. c is the character variable. The most interesting thing is int p[2]. The pipe uses two file descriptors, one to read and one to write. This array contains those two file descriptors.

    pid_t pid;
    int p[2];
    char c;

The first thing we do is to create the pipe, using the command pipe. This has to be done before the child process is created.

    if (pipe(p) < 0)
    {
            fprintf(stderr, "%s: error, could not create pipe\n", argv[0]);
            exit(-1);
    }

Now, since the pipe is created we’re ready to create the child process.

    pid = fork();
    if (pid < 0)
    {
            fprintf(stderr, "%s: error, could not create process\n", argv[0]);
            exit(-2);
    }
    else if (pid == 0)
    {

The child process closes one end of the pipe, the end for writing. Remember: the fork command makes a full copy of the entire process, so this operations run in the context of the child process, and only in the child process.

The child then reads characters from the pipe and prints them out (stdout). If no more characters are following close the other end of the pipe (again, only in the context of the child process) and exit the process.

        /* child code */
        close(p[1]);
        while (read(p[0], &c, 1) > 0) printf("%s: child: [%c]\n", argv[0], c);
        close(p[0]);
        exit(0);
    }
    else
    {

The parent process does exaclty the opposite of the work that the child is doing. First close the read end of the pipe. Then write some characters into the pipe. At the time the parent process closes the second end of the pipe, the child knows that no more data is coming through the pipe. The parent waits then for the child process to terminate and comes to and end also.

        /* parent code */
        close(p[0]);
        c = '0';
        while (c <= '9')
        {
            printf("%s: parent: [%c]\n", argv[0], c);
            if (write(p[1], &c, 1) < 0)
            {
                close(p[1]);
                fprintf(stderr, "%s: parent: error during write\n", argv[0]);
                exit(-3);
            }
            c++;
        }
        close(p[1]);
        waitpid(pid, 0, 0);
        exit(0);
    }
    return 0;
}

Compile and Execute

Compilation and execution is very easy since you don’t need any special library.

To compile use the following command:

$ gcc -o process2 process2.c

To execute is just as easy as to compile:

$ ./process2

Excercises

Downloads

All source code files provided by this page is free to copy, modify, redistribute and use for any purpose. The tutorial is copyrighted by Mario Konrad.