In this Linuxhint article, you’ll learn how to use the fork() and exec() functions to create, run, or replace a process with another. We’ll look at a description of these two functions and explain their syntax and calling method. We’ll also look at a brief practical example of each of the two functions. We will then explain how to use fork() and execv() in combination to create a process and replace it with another.
Fork() Function in the C Language
The fork() function creates a duplicate of the calling process. Although the child process is a duplicate of the parent process, they do not share certain properties such as allocated memory areas, PID, etc. Next, let’s look at the syntax of the fork() function:
The fork() function returns the PID of the child process as a result in the parent process, while the same call has no effect on the child process and returns 0 as a result. This mechanism allows us to execute different code in the two processes through an “if” condition where the condition is the return value of fork(). Let’s look at this following concept:
{
code for child process
}
else
{
code for parent process
}
In this way, the parent process executes the code that is between the curly braces of the “else” statement, while the child process executes the code that is in the “if” statement.
Let’s see this with a simple example. The following code that we see consists of two infinite loops. In the parent process, the program falls into the loop corresponding to the “else” statement which displays the “Write for parent process” message, while in the child process created by fork() falls into the “if” statement which displays the “Write for child process” message.
#include <unistd.h>
void main(){
if (fork() == 0)
{
while (1){
printf ("Write by child process \n");
sleep(5);}
}
else
{
while (1){
printf ("Write by parent process \n");
sleep(2);}
}
}
The following image shows the compilation and execution of this code. As seen in the command console, each of the processes executes different code:
ExecXXX() Function in the C Language
The execXXX() family of functions replaces a running process with a new process. The image of the new process is copied into the memory area that is allocated to the process that is being replaced, preserving, among other things, its PID and allocated resources.
The functions in this group which are defined in the “unistd.h” header use different call methods depending on their inputs and are of “variadics” type, so they can pass an unspecified list of arguments or pointers from the old process to the new one. Next, let’s look at the syntax for each function.
int execlp(const char *file, const char *arg, ... (char *) NULL );
int execle(const char *path, const char *arg, ... , (char *) NULL, char * const envp[] );
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
The execl(), execle(), and execv() functions use a pointer as their first input arguments to a string that contains the absolute path of the executable file of the new process, while the execlp(), execvp(), and execvpe() use the name of the file in the current directory. The second input is the arguments that you pass to the new process. These must be either const char *arg strings or a list of pointers to the char *const argv[] strings.
Now, let’s look at an example that uses the execv() function to replace a process and pass the input arguments from one program to another.
To do this, we create two very simple codes. One is the parent process which calls the execv() function to execute the child process. When the execv() function starts the child process, it passes it two input arguments in the form of a string that the child process will retrieve and display on the shell.
Child Process
The child process is a simple piece of code that prints the “I am the child process” message, retrieves the input argument that is sent by the parent process, and displays them on the shell. Here is the code for the child process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf ("I am the child process\n\n");
printf ("Argument 1: %s\n", argv[1]);
printf ("Argument 2: %s\n", argv[2]);
}
We compile this code and save its production in “Documents” under the “child” name with the “.bin” extension as shown in the following:
~$ gcc Documents/child.c -o Documents/child.bin
In this way, we save the child executable file in “Documents”. The path of this executable is the input argument path when calling execv() in the parent process.
Parent Process
The parent process is the one from which we call the execv() function to replace it with the child process. In this code, we define an array of pointers to the strings that represent the input arguments to the process that the execv() function opens.
In the following illustration, you can see how to correctly create an array of pointers to the strings. In this case, it consists of four pointers and is called “arg_Ptr[]”.
char *arg_Ptr[4];
Once the pointer array is defined, each pointer must be assigned with a string that contains the input argument that we send to the child process. As a rule of thumb for using the execxx() functions, the first argument should be a string that contains the name and extension of the executable file, and the last pointer should be NULL.
Thus, we assign the corresponding argument in a string format to each pointer:
arg_Ptr[0] = "child.bin";
arg_Ptr[1] = " Hello from ";
arg_Ptr[2] = " process 2 ";
arg_Ptr[3] = NULL;
The next step is to call the execv() function, passing the string that contains the absolute path of the executable file as the first argument and the array of strings arg_Ptr[] as the second argument. You can see the full code of the parent process in the following:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main (){
printf ("I am the parent process");
char *arg_Ptr[4];
arg_Ptr[0] = " child.c";
arg_Ptr[1] = " Hello from ";
arg_Ptr[2] = " process 2 ";
arg_Ptr[3] = NULL;
execv("/home/linuxhint/Documents/child.bin", arg_Ptr);
}
We compile this code that specifies the path of the “.c” file and the name of the output:
~$ gcc Documents/parent.c -o pattern
Then, we run the output:
~$ ./ pattern
The parent process displays the “I am the parent process” message, creates the string array by assigning a string for each input argument that is passed to the next process, and calls the execv() function.
If the execv() function executes successfully, the “child.bin” executable replaces the parent process and takes over its ID and allocated memory. So, this action cannot be undone.
The child process displays the “I am the child process” message and retrieves each of the input arguments that are passed by the parent process for display on the command console.
The Fork() and Execve() Functions in Combination to Create New Processes in Linux
As we have seen so far, the fork() function duplicates a process, while execve() replaces a process. In this example, we’ll see how we can use these two functions in combination to open a new process from a duplicate that is then replaced. To do this, we combine the code from the two functions that we saw earlier so that fork() duplicates the parent process and execve() replaces it with an executable which, in this case, is the same one that we used in the previous “child.bin” example.
Now, we take an empty file with the “.c” extension and insert the program code that we saw in the fork() function example.
In this program, we modify only the code of the child process so that the execv() function replaces it with the “child.bin” executable, while the main task is identical to the fork() function example.
To do this, we copy the contents of the main() function from the execv() function example and replace the contents of the “if” statement with this code. Now, let’s see what the complete program looks like:
#include <unistd.h>
void main(){
if (fork() == 0)
{
printf ("I am the child process\n");
char *arg_Ptr[5];
arg_Ptr[0] = " child.c";
arg_Ptr[1] = " Hello from ";
arg_Ptr[2] = " process 2 ";
arg_Ptr[3] = NULL;
execv(/home/linuxhint/Documents/child.bin", arg_Ptr);
}
else
{
while (1){
printf ("Write by parent process \n");
sleep(3);}
}
}
In this way, the process is duplicated by creating a new on the system which the execv() function then replaces with the process from the “child.bin” executable. Next, we see an image with the compilation of this code.
As we can see in the image, the fork() function creates a new process by duplicating it, which the execv() function then replaced with the “child.bin” executable.
Conclusion
In this Linuxhint article, we showed you how to use the fork() and execv() functions to open the new processes in Linux. We showed you a brief description of each of these functions, their syntax, and the input and output arguments. To help you better understand how it works, we also included an example of each of these functions where we learned their call method and the task each of them performs. After looking at these two functions individually, we explained how to use them in combination to implement the method that the C language provides to create the new processes in Linux.