9.1. Multi-process Programming

Advanced operating systems such as Unix and Windows have the ability to run multiple processes at the same time. The system actually switches between processes, but to the user, it appears that they are all running in parallel. Here we discuss how to create additional processes and some system calls which are commonly used together when creating additional processes.

Examples of when we might want to create an additional process are:

  • When the parent program needs to continue running while another process services some request, (e.g., a network based server program).
  • When another program can be used to accomplish a desired objective, (e.g., calling lpr to print some data.)

9.1.1. fork()

The fork() system call starts a new process which is identical to the original running process except for the value returned from the fork() system call. The new process is called the child process and fork() returns a value of zero to the child. The original process is called the parent and fork() returns the PID of the child to the parent.

The fork() system call is the only system call which creates a new process in UNIX. When the operating system boots, it starts a process called init which calls fork() as needed to create other processes. Thus, init is considered the parent of all processes running on the computer. When a user logs in, they are given a shell which reads commands from the user and runs programs for the user. The shell begins the task of running a new program by first calling fork() to create a new process.

9.1.2. pipe( int * )

The pipe() system call creates a mechanism for passing data between processes, such as between the child and parent process after a fork() system call. We pass an integer array of size two as an argument to pipe(). pipe() puts two open file descriptors in the array. The two file descriptors are related to each other in the sense that the first descriptor is opened for reading and the second descriptor is open for writing and what ever is written to the write descriptor can be read from the read descriptor. Thus we say that we have an I/0 pipe.

int p[2];

pipe( p );     /* p[0] - read end of pipe  */
               /* p[1] - write end of pipe */

9.1.3. dup( int )

The dup() system call is used in relation to pipes. If we want to redirect standard input or standard output to be one of the ends of a pipe, then dup() is used to duplicate a file descriptor. The new value associated with the duplicated descriptor is always the numerically lowest available descriptor. So if we close stdin or stdout, then we can use dup() to redirect stdin or stdout to one of the ends of a pipe.

int main(void)
{
   int p[2];     /* for pipe */

   /*
    * parent's stdout goes to child's stdin
    */
   pipe( p );
   if( fork() == 0 ) {  /* child */
      close(0);         /* close stdin */
      dup( p[0] );      /* stdin now comes from the pipe */
      close( p[0] );    /* close extra descriptor */
      close( p[1] );    /* close extra descriptor */
      ....
   } else {             /* parent */
      close(1);         /* close stdout */
      dup( p[1] );      /* stdout now go to the pipe */
      close( p[0] );    /* close extra descriptor */
      close( p[1] );    /* close extra descriptor */
      ....
   }
}

9.1.4. execl( char *cmdPath, char *arg0, … )

The final system call which is commonly used with fork() and friends is execl(). It is used to replace the current running program with another program which is loaded from an executable file on the file system. The arguments passed to execl() need to specify where the file to run is located (its path) and also what the contents of char **argv should be. Thus the number of arguments is variable. Since the last element in argv is a pointer to NULL, the last argument passed to execl() should be a zero.

execl( "/usr/bin/cat", "cat", 0 );  /* no extra argument to cat */

execl( "/usr/bin/cat", "cat", "filename", 0 ); /* one extra argument to cat */

See also

See File Descriptor Example for a complete example of using fork, pipe, dup and execl in a working program.

9.2. Signals

Signals provide another means of interprocess communication (besides pipe). Signals are also used by the operating system to communicate with a processes when something has gone wrong.