≡ Menu

Linux Processes – Process IDs, fork, execv, wait, waitpid C Functions

In this article, we will start from a small explanation of process IDs and then we will quickly jump on to the practical aspects where-in we will discuss some process related C functions like fork(), execv() and wait() .

Linux Processes Series: part 1, part 2, part 3 (this article).

Process IDs

Process IDs are the process identifiers that are non negative numbers associated with a process. These numbers are unique across the processes running in the system.

This uniqueness of the process ID sometimes is used by the process to create some unique filenames. When a process is terminated from system, its process ID is made available for reuse.

But there is a specific delay that is accounted before making the process ID available for reuse. This is because the process ID that was associated with the previous process that is now terminated may well be into use in form of a file name etc. So a delay is added before reusing the same process ID.

Process ID  1 is for the init process. This is the first process that is started once a system boots up.

The program file for the init process can be found either in /etc/init or in /sbin/init. The init process is a user level process but runs with root privileges and is responsible for bringing the system up to a state once the kernel has bootstrapped. The startup files read by the init process to achieve a certain state are

  • /etc/rc*.d
  • /etc/init.d
  • /etc/inittab

Process ID 0 is of the scheduler of the system. It is a kernel level process responsible for all the process scheduling that takes place inside the system.

Process Control Functions

The fork() Function

As already discussed in the article creating a daemon process in C, the fork function is used to create a process from within a process.

The resultant new process created by fork() is known as child process while the original process (from which fork() was called) becomes the parent process.

The function fork() is called once (in the parent process) but it returns twice. Once it returns in the parent process while the second time it returns in the child process. Note that the order of execution of the parent and the child may vary depending upon the process scheduling algorithm. So we see that fork function is used in process creation.

The signature of fork() is  :

pid_t fork(void);

The exec Family of Functions

Another set of functions that are generally used for creating a process is the exec family of functions. These functions are mainly used where there is a requirement of running an existing binary from withing a process.

For example, suppose we want to run the ‘whoami’ command from within a process, then in these kind of scenarios the exec() function or other members of this family is used. A point worth noting here is that with a call to any of the exec family of functions, the current process image is replaced by a new process image.

A common member of this family is the execv() function. Its signature is :

int execv(const char *path, char *const argv[]);

Note: Please refer to the man-page of exec to have a look at the other members of this family.

The wait() and waitpid() Functions

There are certain situations where when a child process terminates or changes state then the parent process should come to know about the change of the state or termination status of the child process. In that case functions like wait() are used by the parent process where the parent can query the change in state of the child process using these functions.

The signature of wait() is  :

pid_t wait(int *status);

For the cases where a parent process has more than one child processes, there is a function waitpid() that can be used by the parent process to query the change state of a particular child.

The signature of waitpid() is :

pid_t waitpid(pid_t pid, int *status, int options);

By default,  waitpid() waits only for terminated children, but this behavior is modifiable via the options argument, as described below.

The value of pid can be:

  • < -1 : Wait for any child process whose process group ID is equal to the absolute value of pid.
  • -1 : Wait for any child process.
  • 0 : Wait for any child process whose process group ID is equal to that of the calling process.
  • > 0 : Wait for the child whose process ID is equal to the value of pid.

The value of options is an OR of zero or more of the following constants:

  • WNOHANG : Return immediately if no child has exited.
  • WUNTRACED : Also  return if a child has stopped. Status for traced children which have stopped is provided even if this option is not specified.
  • WCONTINUED : Also return if a stopped child has been resumed by delivery of SIGCONT.

For more information on waitpid() check-out the man-page of this function.

An Example Program

Here we have an example where we have made use of all the types of functions described above.

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>

int global; /* In BSS segement, will automatically be assigned '0'*/

int main()
{
    pid_t child_pid;
    int status;
    int local = 0;
    /* now create new process */
    child_pid = fork();

    if (child_pid >= 0) /* fork succeeded */
    {
        if (child_pid == 0) /* fork() returns 0 for the child process */
        {
            printf("child process!\n");

            // Increment the local and global variables
            local++;
            global++;

            printf("child PID =  %d, parent pid = %d\n", getpid(), getppid());
            printf("\n child's local = %d, child's global = %d\n",local,global);

            char *cmd[] = {"whoami",(char*)0};
            return execv("/usr/bin/",cmd); // call whoami command

         }
         else /* parent process */
         {
             printf("parent process!\n");
             printf("parent PID =  %d, child pid = %d\n", getpid(), child_pid);
             wait(&status); /* wait for child to exit, and store child's exit status */
             printf("Child exit code: %d\n", WEXITSTATUS(status));

             //The change in local and global variable in child process should not reflect here in parent process.
             printf("\n Parent'z local = %d, parent's  global = %d\n",local,global);

             printf("Parent says bye!\n");
             exit(0);  /* parent exits */
         }
    }
    else /* failure */
    {
        perror("fork");
        exit(0);
    }
}

In the code above, I have tried to create a program that :

  • Uses fork() API to create a child process
  • Uses a local and global variable to prove that fork creates a copy of the parent process and child has its own copy of variables to work on.
  • Uses execv API to call ‘whoami’ command.
  • Uses wait() API to get the termination status of child in the parent. Note that this API holds the execution of the parent until child terminates or changes its state.

Now, when the above program is executed, it produces the following output :

$ ./fork
parent process!
parent PID =  3184, child pid = 3185
child process!
child PID =  3185, parent pid = 3184

child's local = 1, child's global = 1
himanshu
Child exit code: 0

Parent'z local = 0, parent's  global = 0
Parent says bye!
Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • Jalal Hajigholamali March 23, 2012, 2:44 am

    Hi,

    Thanks a lot, very nice article..

  • Vandycke Michael March 23, 2012, 4:18 am

    I really appreciate your tutorials, I’m a student and I have courses about Linux development. Your articles give another point of view about this subject, lots of fun!

  • bob March 23, 2012, 7:26 am

    again, well written, concise, straight to the point. Examples have taken out all the fluff. I like this guy!!!

  • Chandra March 23, 2012, 10:38 am

    return execv(“/usr/bin/whoami”,cmd); has to be return execv(“/usr/bin/”,cmd);

  • Himanshu March 23, 2012, 12:43 pm

    @Chandra
    Thanks for pointing it out. It is fixed now.

  • Horacio Melendez June 5, 2013, 4:33 pm

    I tried the code, but I found a problem: the whoami command didn´ start. Finally, I replaced the line
    return execv(“/usr/bin/”,cmd);
    by
    return execv(“/usr/bin/whoami”,cmd);
    and everythig went fine.

  • Saket Sinha November 5, 2013, 3:51 am

    Excellent tutorial

  • halif January 26, 2015, 5:53 am

    i found out that my first line output is child process? is that wrong?

  • pkvj October 10, 2015, 8:47 am

    Thanks for the tutorial

  • Sushant December 15, 2015, 10:31 am

    Hello,
    Please explan the these its behave like odd child and parent process merged some time.
    #include
    #include // For the pid_t data type
    #include // for fork() create duplicate process
    int main(){

    pid_t new_pid;
    int index,i;
    printf(“********** Explanation of Fork() ************\n”);
    printf(“fork() return a 0 for success and PID of child process to parent process\n”);
    printf(“Its create a copy of parent process contexts\n”);

    printf(“Lets start the Program\n”);

    new_pid = fork(); //Create duplicate process using this function.
    switch(new_pid){
    case -1:
    printf(“Memory creations fail\n”);
    break;
    case 0:
    printf(“Child Process Created sucessfully..\n”);
    printf(“Child pid = %d Parent pid =%d\n”,getppid(),getppid());
    index=10; // For print the for loop below 10 times.
    break;
    default:
    printf(“Parent process\n”);
    index=15;
    break;
    }
    for(i=0;i<index;i++){
    printf("i =%d New_pid %d …\n",i,new_pid);
    }
    return 0;
    }
    ********** Explanation of Fork() ************
    fork() return a 0 for success and PID of child process to parent process
    Its create a copy of parent process contexts
    Lets start the Program
    Parent process
    Child Process Created sucessfully..
    i =0 New_pid 4677 …
    i =1 New_pid 4677 …
    i =2 New_pid 4677 …
    i =3 New_pid 4677 …
    i =4 New_pid 4677 …
    i =5 New_pid 4677 …
    i =6 New_pid 4677 …
    i =7 New_pid 4677 …
    i =8 New_pid 4677 …
    i =9 New_pid 4677 …
    i =10 New_pid 4677 …
    Child pid = 4676 Parent pid =4676
    i =11 New_pid 4677 …
    i =12 New_pid 4677 …
    i =0 New_pid 0 …
    i =13 New_pid 4677 …
    i =1 New_pid 0 …
    i =14 New_pid 4677 …
    i =2 New_pid 0 …
    i =3 New_pid 0 …
    i =4 New_pid 0 …
    i =5 New_pid 0 …
    i =6 New_pid 0 …
    i =7 New_pid 0 …
    i =8 New_pid 0 …
    i =9 New_pid 0 …