In a previous post, I explained how the fork() and exec() system calls are used to create processes on Unix. In this post, I will guide you through a step-by-step practical example of how you can create a process using these two system calls in Linux — the most popular Unix-like OS on the market today.

Let’s get started with the first system call, fork(). Here is the declaration of this function:

int fork();

As I mentioned in the last post, fork() creates a copy of the process that invoked it. After fork() returns, there are two processes in the system. Both of these processes have the same image — i.e., their memory segments have identical content. Note that the fork() system call returns an int, this return value can be used to differentiate between the old process and the newly forked process. The return value of fork() is the PID (process ID) of the newly created process in the old process, and zero in the newly created process. OK, enough theory, lets get our hands dirty with the first code example. Create a new .c file, lets assume that it is called test.c, using your favorite text editor and copy the code below into it.

#include <stdio.h>
int main(){ 
   int val=0;
   printf("This is before I make a call to fork()\n");
   val=fork();
   printf("The value of val is %d\n",val);
   return 0;
}

The code is pretty simple. Up-to line 4, the program is no different from any C program on Linux. A variable, val, is defined in line 3, and the program prints out a message in line 4 stating that the fork() function is yet to be invoked. Note that up-to line 4, there is only one process executing. The interesting stuff begins to occur starting at line 5.

Line 5 contains a call to the fork() function. When this function finishes executing, there will be two processes in the system. Therefore, the fork() function returns in two processes; the original process, and the newly created process.

Go ahead and compile and run this code. Here is the output I got when I ran the program on my system.

This is before I make a call to fork()
The value of val is 3713
The value of val is 0

Notice that the value of val is printed out two times, even though the printf statement is called once. This happens because after the fork() system call in line 5, all the instructions are executed twice — once in the main process and once in the newly forked process. Also, note that val takes on the values 0 and 3713. As mentioned before, zero is printed out in the newly created process and 3713 is printed out in the old process.

Let’s take this one step further, let’s check the return value of fork() and explicitly print out whether we are in the old process or the new process. The code below does this.

#include <stdio.h>
int main(){
  int val=0;
  printf("This is before I make a call to fork()\n");
  val=fork();
  if (val==0)
    printf("We are in the new process\n");
  else
    printf("We are in the old process\n");
  return 0;
}

As you can see, we now check the value of val to see whether it is zero or not. If it is zero, we are in the new process, otherwise we are in the old process.

Now that we can differentiate between the two processes, let us see how we can load a new image into the newly forked process. Remember that we do this using the exec() family of system calls in Unix. There are many versions of exec() in the standard C libraries provided on Linux, do a man exec to check out the semantics of the different version available. I will use the execl() function in this post. Here is the declaration of execl().

int execl(const char *path, const char *arg, ...);

The first parameter, path, is the path containing the binary file we would like to load (including it’s name). The second parameter, arg, is the name of the executable, other arguments, all of type char *, represent the command line parameters you want to send to the new executable. For this example, I chose to create a new process representing the command ls -l. Therefore, I fork a new process and then load the ls binary into this new process. Here is the code I used.

#include <stdio.h>
#include <unistd.h>

int main(){
  int res=0;
  int status=0;

  char *path = "/bin/ls";
  char *arg0 = "ls";
  char *arg1 = "-l";

  printf("This is  before call to fork()\n");
  res=fork();
  if (res==0){
    printf("I am now in the child process\n");
    execl(path,arg0,arg1,NULL);
  }else
    printf("I am now in the parent process\n");
  wait(&status);
  return 0;
}

Let’s zoom in on the new parts of this program. First, examine lines 8-10. In these lines, we setup the strings that will be passed to the execl function. The first string is the path to the executable (including its name), the second is its name and the third is the parameter we wish to send to the executable.

Next, notice that the execl call occurs within the if statement on lines 14-17. This if statement identifies the new process, since it checks if the return value of fork() is equal to zero. This is done because we want to overlay the image of the ls binary on the address space of the newly created process. The final piece of the puzzle is the wait() system call on line 19. I will not go into the details of this call, but suffice it to say that the wait call is necessary because there are now two processes executing and we want the main process to wait until the child it has forked terminates before exiting, this will ensure that the program terminates correctly. Here is the output I received when I ran this program:

This is  before call to fork()
I am now in the parent process
I am now in the child process
total 24
-rwxr-xr-x 1 sherif sherif 7239 2011-08-04 18:30 process
-rwxr-xr-x 1 sherif sherif 7166 2011-08-04 21:03 test2
-rw-r--r-- 1 sherif sherif  234 2011-08-04 21:03 test2.c
-rw-r--r-- 1 sherif sherif  394 2011-08-04 21:37 test.c

That’s it folks, happy forking! 😀

11 Comments

  • Unix pipes | Sherif Fadel Fahmy, October 19, 2011 @ 8:24 am Reply

    […] this question lies in the way a Unix process is created. I describe this process in detail here and here. Essentially, a new process is created by first calling the fork() system call to make a copy of […]

  • dalia, July 11, 2012 @ 8:20 am Reply

    Please I want the compiler of the program

    Thanks in advance
    Dalia

  • arun, September 27, 2012 @ 6:58 am Reply

    oh my i dont no nothing puriyala appu

  • Noor Manseel Mohammed, January 8, 2013 @ 10:37 am Reply

    Nice article. Thanks 🙂

  • Sanjay K, February 21, 2013 @ 10:39 am Reply

    Very interesting and informative.
    Keep writing in such simple way.
    ~Sanjay

  • sontosh, May 30, 2013 @ 7:27 am Reply

    nice man keep it up

  • Sebastian Plaza, October 6, 2013 @ 3:44 pm Reply

    Thanks man,

    That was really helpful !

  • jasmine, November 26, 2013 @ 7:26 pm Reply

    Nicely explained!

    • waseem, April 26, 2014 @ 2:07 pm Reply

      Linux machine and bring up a terminal. It might be useful to look at the manual pages of these shells, for
      example, type “man csh”.
      The most rudimentary shell is structured as the following loop:
      1. Print out a prompt (e.g., “OS_Spring2014_Shell$ “);
      2. Read a line from the user;
      3. Parse the line into the program name and an array of parameters;
      4. Use the fork() system call to spawn a new child process;
      5 The child process then uses the exec() system call (or one of its variants)
      to launch the specified program;
      6. The parent process (the shell) uses the wait() system call (or one of its
      variants) to wait for the child to terminate;
      7.. Once the child (the launched program) finishes, the shell repeats the loop by jumping to 1.
      Although most
      ……………………………………………
      plz help me and solve this question

  • sqqqrly, February 3, 2014 @ 3:44 pm Reply

    $ man 3 exec

    The list of arguments must be terminated by a NULL pointer, and, since these are variadic functions, this pointer must be cast (char *) NULL.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.