Forking

Start extra processes

Problem: how can I create multiple processes? The answer in the Unix world has always been: fork off.

  1. Code your program as you normally would, up to the point where you want another process.
  2. Then use the fork() system call, which makes a copy of the running process. The copy is called child process and the original is called the parent process. Each process has an unique identifier called the process id (check out the man pages of the ps utility).
  3. fork() returns an integer: it gives 0 (zero) to the child process. To the parent process, it gives the child process its process id.

Note that although the child process has an exact copy of all variables, it is a copy, so any changes in the child process its variables do not affect the original (parent) process its variables.

One application which nowadays immediately jumps to mind is the webserver, which forks off a child process for every connection.

Example

  #include <stdlib.h>
  #include <unistd.h>
  
  main() 
  { 
        int n_return_value;                    /* to check fork() return value */
  
        char* psz_args[2];                 /* the arguments to pass to execv() */
           
        printf("Forking process\n");
        n_return_value=fork();
        if(n_return_value < 0)                      /* check for error code */
        {
              printf("Cannot fork!\n");
              exit(1);
        }
  
        /* After this comment, two copies exist of the same program. The only
         * difference between the copies is that they receive a different
         * result of the fork() call */
  	
        if(n_return_value == 0)               /* the child copy of the program 
                                              receives 0 as the result of fork */
        {
             printf("Child process pid is %d and 
             return value is %d\n", getpid(), n_return_value);
        }    
        else                       /* the parent copy of the program receives
                                        the process id of the child from fork */
        {
              printf("Parent process pid is %d and 
              return value is %d\n", getpid(), n_return_value);
        }
  
        pzs_args[0] = "-l";                      /* this is the first argument */
        pzs_args[1] = NULL;            /* the list of arguments must be closed
                                                by a character pointer to NULL */
        execv("/bin/ls", pzs_args);
        printf("This line will never be printed, because the previous
        instruction execv() replaces the process image!");
        return 0;
  }

Server process

Fork is also used to "fork into the background"; i.e. the process is started, forks and lets the parent end. This way, the process continues in the background as a server process.

Nowadays, it's more common not to fork into the background. It makes debugging and in general, managing the process harder. Instead, a script or separate executable is offered that starts the software and puts it in the background.

Also, since forking is a relatively heavy call, it's not really suitable for forking on request. Rather, instances are preforked so as not to overload the machine when instances are suddenly needed.

From the GNU manual: "Another way to provide a service on an Internet port is to let the daemon program inetd do the listening. inetd is a program that runs all the time and waits (using select) for messages on a specified set of ports. When it receives a message, it accepts the connection (if the socket style calls for connections) and then forks a child process to run the corresponding server program. You specify the ports and their programs in the file /etc/inetd.conf."

Example clone() call

You can also use the Linux-specific clone() call, which unlike fork(), shares the complete memory space of the parent. This call is also used for implementing threading libraries. Example below by Tiemen Schut:

  #include <stdio.h>
  #include <sched.h>
  #include <stdlib.h>
  
  int clone_f (void);
  int a = 0, b = 0;
  
  
  int main (int argc, char **argv) {
      void *child_stack = malloc (8192) + 8192;
      
      printf ("main: cloning ... \n");
      
      clone ((void *)clone_f, child_stack, CLONE_VM, NULL);
  
      while (a == 0);
      
      if (a == 1) {
          printf ("clone changed a in 1 :o\n");
      }
      
      return 0;
  }
  
  
  int clone_f (void) {
      printf ("clone: hello\n");
      
      a = 1;
      usleep (1000000);
      //while (1);
      
      return 0;    
  }