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

by Himanshu Arora on March 23, 2012

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!

Linux Sysadmin Course Linux provides several powerful administrative tools and utilities which will help you to manage your systems effectively. If you don’t know what these tools are and how to use them, you could be spending lot of time trying to perform even the basic administrative tasks. The focus of this course is to help you understand system administration tools, which will help you to become an effective Linux system administrator.
Get the Linux Sysadmin Course Now!

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

{ 7 comments… read them below or add one }

1 Jalal Hajigholamali March 23, 2012 at 2:44 am

Hi,

Thanks a lot, very nice article..

2 Vandycke Michael March 23, 2012 at 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!

3 bob March 23, 2012 at 7:26 am

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

4 Chandra March 23, 2012 at 10:38 am

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

5 Himanshu March 23, 2012 at 12:43 pm

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

6 Horacio Melendez June 5, 2013 at 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.

7 Saket Sinha November 5, 2013 at 3:51 am

Excellent tutorial

Leave a Comment

Previous post:

Next post: