It is found that in any Linux/Unix based Operating Systems it is good to understand fork and vfork system calls, how they behave, how we can use them and differences between them. Along with these wait and exec system calls are used for process spawning and various other related tasks.
Most of these concepts are explained using programming examples. In this article, I will be covering what are fork, vfork, exec and wait system calls, their distinguishing characters and how they can be better used.
fork()
fork(): System call to create a child process.
shashi@linuxtechi ~}$ man fork
This will yield output mentioning what is fork used for, syntax and along with all the required details.
The syntax used for the fork system call is as below,
pid_t fork(void);
Fork system call creates a child that differs from its parent process only in pid(process ID) and ppid(parent process ID). Resource utilization is set to zero. File locks and pending signals are not inherited. (In Linux “fork” is implemented as “copy-on-write()“).
Note:- “Copy on write” -> Whenever a fork() system call is called, a copy of all the pages(memory) related to the parent process is created and loaded into a separate memory location by the Operating System for the child process. But this is not needed in all cases and may be required only when some process writes to this address space or memory area, then only separate copy is created/provided.
Return values:- PID (process ID) of the child process is returned in parents thread of execution and “zero” is returned in child’s thread of execution. Following is the c-programming example which explains how fork system call works.
shashi@linuxtechi ~}$ vim 1_fork.c #include<stdio.h> #include<unistd.h> Int main(void) { printf("Before fork\n"); fork(); printf("after fork\n"); } shashi@linuxtechi ~}$ shashi@linuxtechi ~}$ cc 1_fork.c shashi@linuxtechi ~}$ ./a.out Before fork After fork shashi@linuxtechi ~}$
Whenever any system call is made there are plenty of things that take place behind the scene in any unix/linux machines.
First of all context switch happens from user mode to kernel(system) mode. This is based on the process priority and unix/linux operating system that we are using. In the above C example code we are using “{” opening curly brace which is the entry of the context and “}” closing curly brace is for exiting the context. The following table explains context switching very clearly.
vfork()
vfork –> create a child process and block parent process.
Note:- In vfork, signal handlers are inherited but not shared.
shashi@linuxtechi ~}$ man vfork
This will yield output mentioning what is vfork used for, syntax and along with all the required details.
pid_t vfork(void);
vfork is as same as fork except that behavior is undefined if process created by vfork either modifies any data other than a variable of type pid_t used to store the return value p of vfork or calls any other function between calling _exit() or one of the exec() family.
Note: vfork is sometimes referred to as special case of clone.
Following is the C programming example for vfork() how it works.
shashi@linuxtechi ~}$ vim 1.vfork.c #include<stdio.h> #include<unistd.h> Int main(void) { printf("Before fork\n"); vfork(); printf("after fork\n"); } shashi@linuxtechi ~}$ vim 1.vfork.c shashi@linuxtechi ~}$ cc 1.vfork.c shashi@linuxtechi ~}$ ./a.out Before vfork after vfork after vfork a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed. Aborted
Note:– As explained earlier, many a times the behaviour of the vfork system call is not predictable. As in the above case it had printed before once and after twice but aborted the call with _exit() function. It is better to use fork system call unless otherwise and avoid using vfork as much as possible.
Differences between fork() and vfork()
Vfork() behaviour explained in more details in the below program.
shashi@linuxtechi ~}$ cat vfork_advanced.c #include <sys/types.h> #include <unistd.h> #include <stdio.h> int main() { int n =10; pid_t pid = vfork(); //creating the child process if (pid == 0) //if this is a chile process { printf("Child process started\n"); } else//parent process execution { printf("Now i am coming back to parent process\n"); } printf("value of n: %d \n",n); //sample printing to check "n" value return 0; } shashi@linuxtechi ~}$ cc vfork_advanced.c shashi@linuxtechi ~}$ ./a.out Child process started value of n: 10 Now i am coming back to parent process value of n: 594325573 a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed. Aborted
Note: Again if you observe the outcome of vfork is not defined. Value of “n” has been printed first time as 10, which is expected. But the next time in the parent process it has printed some garbage value.
wait()
wait() system call suspends execution of current process until a child has exited or until a signal has delivered whose action is to terminate the current process or call signal handler.
pid_t wait(int * status);
There are other system calls related to wait as below,
1) waitpid(): suspends execution of current process until a child as specified by pid arguments has exited or until a signal is delivered.
pid_t waitpid (pid_t pid, int *status, int options);
2) wait3(): Suspends execution of current process until a child has exited or until signal is delivered.
pid_t wait3(int *status, int options, struct rusage *rusage);
3) wait4(): As same as wait3() but includes pid_t pid value.
pid_t wait3(pid_t pid, int *status, int options, struct rusage *rusage);
exec()
exec() family of functions or sys calls replaces current process image with new process image.
There are functions like execl, execlp,execle,execv, execvp and execvpe are used to execute a file.
These functions are combinations of array of pointers to null terminated strings that represent the argument list , this will have path variable with some environment variable combinations.
exit()
This function is used for normal process termination. The status of the process is captured for future reference. There are other similar functions exit(3) and _exit()., which are used based on the exiting process that one is interested to use or capture.
Conclusion:-
The combinations of all these system calls/functions are used for process creation, execution and modification. Also these are called “shell” spawning set-of functions. One has to use these functions cautiously keeping in mind the outcome and behaviour.
I tried your first example in a virtual terminal in Linux Mint 19 XFCE, and it produced an error:
l_fork.c:3:1: error: unknown type name ‘Int’; did you mean ‘int’?
Int main(void)
^~~
int
I corrected the ‘Int’ to ‘int’ and ran it again, and this time it produced a slightly different output to yours:
Before fork
after fork
after fork
Why does it produce two ‘after fork’ lines?
Fork() creates a child process that is why it prints two times, one for the parent and other for the child.
As you can see there is fork() call before the “after fork” print statement.