Conceptually, we can consider fork() as creating copies of the parent’s text, data, heap, and stack segments. It would be wasteful in many cases.
Most modern UNIX implementations, including Linux, use two techniques to avoid such wasteful copying:
The kernel marks the text segment of each process read-only, so that a process can’t modify its own code. With a per-process page-table that points to the same virtual memory pages in parent.
copy-on-write technique. After the fork(), the kernel traps any attempts by either the parent or the child to modify one of these pages, and makes aduplicate copy of the about-to-be-modifiedpage.
Race Conditions after fork()
It is indeterminate which process the parent or the child next statement has access to the CPU. In a complex system, this kind of issues or bugs can be hard to find.
Source Code: procexec/fork_whos_on_first.c from TLPI
Let's test:
for i in $(seq 1 1 1000); do ./fork_whos_on_first 1; done | ./fork_whos_on_first.count.awk
for i in $(seq 1 1 1000); do ./fork_whos_on_first 5; done | ./fork_whos_on_first.count.awk
for i in $(seq 1 1 1000); do ./fork_whos_on_first 10; done | ./fork_whos_on_first.count.awk
How we can avoid those race conditions?
What if I want concurrent childs but I want some order in the execution?
Can I wait for a specific child in order to continue in the parent process?
Process termination
There are 2 general ways for terminating a process.
Abnormal termination (with our without core dump)
Normal termination using the _exit() system call
#include <unistd.h>
void _exit(int status);
exit() is generally used because it provides extra functionality.
#include <stdlib.h>
void exit(int status);
The following actions are performed by exit():
Exit handlers
stdio stream buffers are flushed
The _exit() system call is invoked with the provided status code.
Exit handlers
Sometimes, an application needs to automatically perform some operations on process termination.
#include <stdlib.h>
int atexit(void (*func)(void)); /* Returns 0 on success, or nonzero on error */
In case you need the exit status code, there's a nonstandard alternative provided by glibc
#define _BSD_SOURCE /* Or: #define _SVID_SOURCE */
#include <stdlib.h>
int on_exit(void (*func)(int, void *), void *arg); /* Returns 0 on success, or nonzero on error */
The func argument is a pointer to a function as follows: