Tuesday, July 24, 2012

The fork() System Call

System call fork() is used to create processes. It takes no arguments and returns a process ID. The purpose of fork() is to create a new process, which becomes the child process of the caller. After a new child process is created, both processes will execute the next instruction following the fork() system call. Therefore, we have to distinguish the parent from the child. This can be done by testing the returned value of fork():
  • If fork() returns a negative value, the creation of a child process was unsuccessful.
  • fork() returns a zero to the newly created child process.
  • fork() returns a positive value, the process ID of the child process, to the parent. The returned process ID is of type pid_t defined in sys/types.h. Normally, the process ID is an integer. Moreover, a process can use function getpid() to retrieve the process ID assigned to this process. 
  •  
  •  
    #include  <stdio.h>
    #include  <string.h>
    #include  <sys/types.h>
    
    #define   MAX_COUNT  200
    #define   BUF_SIZE   100
    
    void  main(void)
    {
         pid_t  pid;
         int    i;
         char   buf[BUF_SIZE];
    
         fork();
         pid = getpid();
         for (i = 1; i <= MAX_COUNT; i++) {
              sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
              write(1, buf, strlen(buf));
         } 
    }
    If the call to fork() is executed successfully, Unix will
  • make two identical copies of address spaces, one for the parent and the other for the child.
  • Both processes will start their execution at the next statement following the fork() call. In this case, both processes will start their execution at the assignment statement as shown below:
  •  
     
    Both processes start their execution right after the system call fork(). Since both processes have identical but separate address spaces, those variables initialized before the fork() call have the same values in both address spaces. Since every process has its own address space, any modifications will be independent of the others. In other words, if the parent changes the value of its variable, the modification will only affect the variable in the parent process's address space. Other address spaces created by fork() calls will not be affected even though they have identical variable names.
    What is the reason of using write rather than printf? It is because printf() is "buffered," meaning printf() will group the output of a process together. While buffering the output for the parent process, the child may also use printf to print out some information, which will also be buffered. As a result, since the output will not be send to screen immediately, you may not get the right order of the expected result. Worse, the output from the two processes may be mixed in strange ways. To overcome this problem, you may consider to use the "unbuffered" write.
    If you run this program, you might see the following on the screen:
         ................
    This line is from pid 3456, value 13
    This line is from pid 3456, value 14
         ................
    This line is from pid 3456, value 20
    This line is from pid 4617, value 100
    This line is from pid 4617, value 101
         ................
    This line is from pid 3456, value 21
    This line is from pid 3456, value 22
         ................
    
    Process ID 3456 may be the one assigned to the parent or the child. Due to the fact that these processes are run concurrently, their output lines are intermixed in a rather unpredictable way. Moreover, the order of these lines are determined by the CPU scheduler. Hence, if you run this program again, you may get a totally different result. Consider one more simple example, which distinguishes the parent from the child. Click here to download this file fork-02.c.
    #include  <stdio.h>
    #include  <sys/types.h>
    
    #define   MAX_COUNT  200
    
    void  ChildProcess(void);                /* child process prototype  */
    void  ParentProcess(void);               /* parent process prototype */
    
    void  main(void)
    {
         pid_t  pid;
    
         pid = fork();
         if (pid == 0) 
              ChildProcess();
         else 
              ParentProcess();
    }
    
    void  ChildProcess(void)
    {
         int   i;
    
         for (i = 1; i <= MAX_COUNT; i++)
              printf("   This line is from child, value = %d\n", i);
         printf("   *** Child process is done ***\n");
    }
    
    void  ParentProcess(void)
    {
         int   i;
    
         for (i = 1; i <= MAX_COUNT; i++)
              printf("This line is from parent, value = %d\n", i);
         printf("*** Parent is done ***\n");
    }
    
    In this program, both processes print lines that indicate (1) whether the line is printed by the child or by the parent process, and (2) the value of variable i. For simplicity, printf() is used. When the main program executes fork(), an identical copy of its address space, including the program and all data, is created. System call fork() returns the child process ID to the parent and returns 0 to the child process. The following figure shows that in both address spaces there is a variable pid. The one in the parent receives the child's process ID 3456 and the one in the child receives 0.

    Now both programs (i.e., the parent and child) will execute independent of each other starting at the next statement:

    In the parent, since pid is non-zero, it calls function ParentProcess(). On the other hand, the child has a zero pid and calls ChildProcess() as shown below:

    Due to the fact that the CPU scheduler will assign a time quantum to each process, the parent or the child process will run for some time before the control is switched to the other and the running process will print some lines before you can see any line printed by the other process. Therefore, the value of MAX_COUNT should be large enough so that both processes will run for at least two or more time quanta. If the value of MAX_COUNT is so small that a process can finish in one time quantum, you will see two groups of lines, each of which contains all lines printed by the same process.
     

wait():

The parent process will often want to wait until all child processes have been completed. this can be implemented with the wait() function call.
wait(): Blocks calling process until the child process terminates. If child process has already teminated, the wait() call returns immediately. if the calling process has multiple child processes, the function returns when one returns.
waitpid(): Options available to block calling process for a particular child process not the first one. 
 #include <sys/wait.h>
02 
03...
04 
05      pid_t pID = <i>set to child process id with call to fork OR:</i>
06                  // If set <-1, wait for any child process whose process group ID = abs(pID)
07                  // If = -1, wait  for  any child process. Same as wait().
08                  // If =  0, wait for any child process whose process group ID is same as calling process.
09                  // If >  0, wait for the child whose process ID = pID.
10 
11...
12      int childExitStatus;
13 
14      pid_t ws = waitpid( pID, &childExitStatus, WNOHANG);
15 
16      if( WIFEXITED(childExitStatus) )
17      {
18         // Child process exited thus exec failed.
19         // LOG failure of exec in child process.
20         cout << "Result of waitpid: Child process exited thus exec failed." << endl;
21      }

vfork():

 The vfork() function is the same as fork() except that it does not make a copy of the address space. The memory is shared reducing the overhead of spawning a new process with a unique copy of all the memory. This is typically used when using fork() to exec() a process and terminate. The vfork() function also executes the child process first and resumes the parent process when the child terminates.
01 #include <iostream>
02#include <string>
03 
04// Required by for routine
05#include <sys/types.h>
06#include <unistd.h>
07 
08using namespace std;
09 
10 
11int globalVariable = 2;
12 
13main()
14{
15   string sIdentifier;
16   int    iStackVariable = 20;
17 
18   pid_t pID = vfork();
19   if (pID == 0)                // child
20   {
21      // Code only executed by child process
22 
23      sIdentifier = "Child Process: ";
24      globalVariable++;
25      iStackVariable++;
26      cout << sIdentifier;
27      cout << " Global variable: " << globalVariable;
28      cout << " Stack variable: "  << iStackVariable << endl;
29      _exit(0);
30    }
31    else if (pID < 0)            // failed to fork
32    {
33        cerr << "Failed to fork" << endl;
34        exit(1);
35        // Throw exception
36    }
37    else                                   // parent
38    {
39      // Code only executed by parent process
40 
41      sIdentifier = "Parent Process:";
42    }
43 
44    // executed only by parent
45 
46    cout << sIdentifier;
47    cout << " Global variable: " << globalVariable;
48    cout << " Stack variable: "  << iStackVariable << endl;
49    exit(0);
50}

fork():,


fork():

The fork() system call will spawn a new child process which is an identical process to the parent except that has a new system process ID. The process is copied in memory from the parent and a new process structure is assigned by the kernel. The return value of the function is which discriminates the two threads of execution. A zero is returned by the fork function in the child's process.
The environment, resource limits, umask, controlling terminal, current working directory, root directory, signal masks and other process resources are also duplicated from the parent in the forked child process. 



#include <iostream>
02#include <string>
03 
04// Required by for routine
05#include <sys/types.h>
06#include <unistd.h>
07 
08#include <stdlib.h>   // Declaration for exit()
09 
10using namespace std;
11 
12int globalVariable = 2;
13 
14main()
15{
16   string sIdentifier;
17   int    iStackVariable = 20;
18 
19   pid_t pID = fork();
20   if (pID == 0)                // child
21   {
22      // Code only executed by child process
23 
24      sIdentifier = "Child Process: ";
25      globalVariable++;
26      iStackVariable++;
27    }
28    else if (pID < 0)            // failed to fork
29    {
30        cerr << "Failed to fork" << endl;
31        exit(1);
32        // Throw exception
33    }
34    else                                   // parent
35    {
36      // Code only executed by parent process
37 
38      sIdentifier = "Parent Process:";
39    }
40 
41    // Code executed by both parent and child.
42   
43    cout << sIdentifier;
44    cout << " Global variable: " << globalVariable;
45    cout << " Stack variable: "  << iStackVariable << endl;
46} 





                                                                      

fork() and wait() system calls (Fibonacci, quicksort and merging these two)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h> int main() {    int a=0, b=1, n=a+b,i,ii;    pid_t pid;    printf("Enter the number of a Fibonacci Sequence:\n");    scanf("%d", &ii);    if (ii < 0)
      printf("Please enter a non-negative integer!\n");    else    {
      pid = fork();
      if (pid == 0)
      {
         printf("Child is producing the Fibonacci Sequence...\n");
         printf("%d %d",a,b);
         for (i=0;i<ii;i++)
         {
            n=a+b;
            printf("%d ", n);
            a=b;
            b=n;
         }
         printf("Child ends\n"); 
      }
      else 
      {
         printf("Parent is waiting for child to complete...\n");
         wait(NULL);
         printf("Parent ends\n");
      }    }    return 0; }

How to write a C program using the fork() system call that generates the Fibonacci sequence in the child process

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
   int a=0, b=1, n=a+b,i,ii;
   pid_t pid;

   printf("Enter the number of a Fibonacci Sequence:\n");
   scanf("%d", &ii);

   if (ii < 0)
      printf("Please enter a non-negative integer!\n");
   else
   {
      pid = fork();
      if (pid == 0)
      {
         printf("Child is producing the Fibonacci Sequence...\n");
         printf("%d %d",a,b);
         for (i=0;i<ii;i++)
         {
            n=a+b;
            printf("%d ", n);
            a=b;
            b=n;
         }
         printf("Child ends\n"); 
      }
      else 
      {
         printf("Parent is waiting for child to complete...\n");
         wait(NULL);
         printf("Parent ends\n");
      }
   }
   return 0;
}

c program showing - fork() and wait()

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <sys/wait.h>
  6.  
  7. int main(void)  {
  8.  
  9.     pid_t child_pid, wpid, pid;
  10.     int status = 0;
  11.     int i;
  12.  
  13.     int a[3] = {1, 2, 1};
  14.     for(i = 1; i < 3; i++)  {
  15.         printf("i = %d\n", i);
  16.         pid = getpid();
  17.         printf("pid after i = %d\n", pid);
  18.         if((child_pid = fork()) == 0)  {
  19.             printf("In child process\n");
  20.             pid = getpid();
  21.             printf("pid in child process is %d\n", pid);
  22.             /* Is a child process */
  23.             if(a[i] < 2)  {
  24.                 printf("Should be accept\n");
  25.                 _exit(1);
  26.             } else  {
  27.                 printf("Should be reject\n");
  28.                 _exit(0);
  29.             }
  30.         }
  31.     }
  32.  
  33.     if(child_pid > 0)  {
  34.         /* Is the parent process */
  35.         pid = getpid();
  36.         printf("parent_pid = %d\n", pid);
  37.         wpid = wait(&status);
  38.         if(wpid != -1)  {
  39.             printf("Child's exit status was %d\n", status);
  40.             if(status > 0)  {
  41.                 printf("Accept\n");
  42.             } else  {
  43.                 printf("Complete parent process\n");
  44.                 if(a[0] < 2)  {
  45.                     printf("Accept\n");
  46.                 } else  {
  47.                     printf("Reject\n");
  48.                 }
  49.             }
  50.         }
  51.     }
  52.     return 0;
  53. }