How does fork() work? - c

Im really new to forking, what is the pid doing in this code? Can someone please explain what comes out at line X and line Y ?
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 5
int nums[SIZE] = {0,1,2,3,4};
int main()
{
int i;
pid_t pid;
pid = fork();
if (pid == 0) {
for (i = 0; i < SIZE; i++) {
nums[i] *= -i;
printf("CHILD: %d ",nums[i]); /* LINE X */
}
}
else if (pid > 0) {
wait(NULL);
for (i = 0; i < SIZE; i++)
printf("PARENT: %d ",nums[i]); /* LINE Y */
}
return 0;
}

fork() duplicates the process, so after calling fork there are actually 2 instances of your program running.
How do you know which process is the original (parent) one, and which is the new (child) one?
In the parent process, the PID of the child process (which will be a positive integer) is returned from fork(). That's why the if (pid > 0) { /* PARENT */ } code works. In the child process, fork() just returns 0.
Thus, because of the if (pid > 0) check, the parent process and the child process will produce different output, which you can see here (as provided by #jxh in the comments).

Simplest example for fork()
printf("I'm printed once!\n");
fork();
// Now there are two processes running one is parent and another child.
// and each process will print out the next line.
printf("You see this line twice!\n");
The return value of fork(). Return value -1= failed; 0= in child process; positive = in parent process (and the return value is the child process id)
pid_t id = fork();
if (id == -1) exit(1); // fork failed
if (id > 0)
{
// I'm the original parent and
// I just created a child process with id 'id'
// Use waitpid to wait for the child to finish
} else { // returned zero
// I must be the newly made child process
}
What is different in the child process than the parent process?
The parent is notified via a signal when the child process finishes but not vice versa.
The child does not inherit pending signals or timer alarms. For a complete list see the fork()
Here the process id can be returned by getpid(). The parent process id can be returned by getppid().
Now let's visualize your program code
pid_t pid;
pid = fork();
Now OS make two identical copies of address spaces, one for the parent and the other for the child.
Both parent and child process start their execution right after the system call fork(). Since both processes have identical but separate address spaces, those variables initialized before the fork() call have the same values in both address spaces. Every process has its own address space so any modifications will be independent of the others. If the parent changes the value of its variable, the modification will only affect the variable in the parent process's address space. Other address spaces created by fork() sysem calls will not be affected even though they have identical variable names .
Here parent pid is non-zero, it calls function ParentProcess(). On the other hand, the child has a zero pid and it calls ChildProcess() as shown below:
In your code parent process call wait() it pauses at that point until the child exits. So the child's output appears first.
if (pid == 0) {
// The child runs this part because fork returns 0 to the child
for (i = 0; i < SIZE; i++) {
nums[i] *= -i;
printf("CHILD: %d ",nums[i]); /* LINE X */
}
}
OUTPUT from child process
what comes out at line X
CHILD: 0 CHILD: -1 CHILD: -4 CHILD: -9 CHILD: -16
Then after the child exits, the parent continues from after the wait() call and prints its output next.
else if (pid > 0) {
wait(NULL);
for (i = 0; i < SIZE; i++)
printf("PARENT: %d ",nums[i]); /* LINE Y */
}
OUTPUT from parent process:
what comes out at line Y
PARENT: 0 PARENT: 1 PARENT: 2 PARENT: 3 PARENT: 4
At last both output combined by child and parent process will be shown on terminal as follow:
CHILD: 0 CHILD: -1 CHILD: -4 CHILD: -9 CHILD: -16 PARENT: 0 PARENT: 1 PARENT: 2 PARENT: 3 PARENT: 4
For more info refer this link

The fork() function is special because it actually returns twice: once to the parent process and once to the child process. In the parent process, fork() returns the pid of the child. In the child process, it returns 0. In the event of an error, no child process is created and -1 is returned to the parent.
After a successful call to fork(), the child process is basically an exact duplicate of the parent process. Both have their own copies of all local and global variables, and their own copies of any open file descriptors. Both processes run concurrently, and because they share the same file descriptors, the output of each process will likely interleave with each other.
Taking a closer look at the example in the question:
pid_t pid;
pid = fork();
// When we reach this line, two processes now exist,
// with each one continuing to run from this point
if (pid == 0) {
// The child runs this part because fork returns 0 to the child
for (i = 0; i < SIZE; i++) {
nums[i] *= -i;
printf("CHILD: %d ",nums[i]); /* LINE X */
}
}
else if (pid > 0) {
// The parent runs this part because fork returns the child's pid to the parent
wait(NULL); // this causes the parent to wait until the child exits
for (i = 0; i < SIZE; i++)
printf("PARENT: %d ",nums[i]); /* LINE Y */
}
This will output the following:
CHILD: 0 CHILD: -1 CHILD: -4 CHILD: -9 CHILD: -16 PARENT: 0 PARENT: 1 PARENT: 2 PARENT: 3 PARENT: 4
Because the parent process calls wait() it pauses at that point until the child exits. So the child's output appears first. Then after the child exits, the parent continues from after the wait() call and prints its output next.

In the simplest cases, the behaviour of fork() is very simple — if a bit mind-blowing on your first encounter with it. It either returns once with an error, or it returns twice, once in the original (parent) process, and once in a brand new almost exact duplicate of the original process (the child process). After return, the two processes are nominally independent, though they share a lot of resources.
pid_t original = getpid();
pid_t pid = fork();
if (pid == -1)
{
/* Failed to fork - one return */
…handle error situation…
}
else if (pid == 0)
{
/* Child process - distinct from original process */
assert(original == getppid() || getppid() == 1);
assert(original != getpid());
…be childish here…
}
else
{
/* Parent process - distinct from child process */
assert(original != pid);
…be parental here…
}
The child process is a copy of the parent. It has the same set of open file descriptors, for example; each file descriptor N that was open in the parent is open in the child, and they share the same open file description. That means that if one of the processes alters the read or write position in a file, that also affects the other process. On the other hand, if one of the processes closes a file, that has no direct effect on the file in the other process.
It also means that if there was data buffered in the standard I/O package in the parent process (e.g. some data had been read from the standard input file descriptor (STDIN_FILENO) into the data buffer for stdin, then that data is available to both the parent and the child, and both can read that buffered data without affecting the other, which will also see the same data. On the other hand, once the buffered data is read, if the parent reads another buffer full, that moves the current file position for both the parent and the child, so the child won't then see the data that the parent just read (but if the child also reads a block of data, the parent won't see that). This can be confusing. Consequently, it is usually a good idea to make sure that there's no pending standard I/O before forking — fflush(0) is one way to do that.
In the code fragment, assert(original == getppid() || getppid() == 1); allows for the possibility that by the time the child executes the statement, the parent process may have exited, in which case the child will have been inherited by a system process — which normally has PID 1 (I know of no POSIX system where orphaned children are inherited by a different PID, but there probably is one).
Other shared resources, such as memory-mapped files or shared memory, continue to be available in both. The subsequent behaviour of a memory-mapped file depends on the options used to create the mapping; MAP_PRIVATE means that the two processes have independent copies of the data, and MAP_SHARED means that they share the same copy of the data and changes made by one process will be visible in the other.
However, not every program that forks is a simple as the story described so far. For example, the parent process might have acquired some (advisory) locks; those locks are not inherited by the child. The parent may have been multi-threaded; the child has a single thread of execution — and there are constraints placed on what the child may do safely.
The POSIX specification for fork() specifies the differences in detail:
The fork() function shall create a new process. The new process (child process) shall be an exact copy of the calling process (parent process) except as detailed below:
The child process shall have a unique process ID.
The child process ID also shall not match any active process group ID.
The child process shall have a different parent process ID, which shall be the process ID of the calling process.
The child process shall have its own copy of the parent's file descriptors. Each of the child's file descriptors shall refer to the same open file description with the corresponding file descriptor of the parent.
The child process shall have its own copy of the parent's open directory streams. Each open directory stream in the child process may share directory stream positioning with the corresponding directory stream of the parent.
The child process shall have its own copy of the parent's message catalog descriptors.
The child process values of tms_utime, tms_stime, tms_cutime, and tms_cstime shall be set to 0.
The time left until an alarm clock signal shall be reset to zero, and the alarm, if any, shall be canceled; see alarm.
[XSI] ⌦ All semadj values shall be cleared. ⌫
File locks set by the parent process shall not be inherited by the child process.
The set of signals pending for the child process shall be initialized to the empty set.
[XSI] ⌦ Interval timers shall be reset in the child process. ⌫
Any semaphores that are open in the parent process shall also be open in the child process.
[ML] ⌦ The child process shall not inherit any address space memory locks established by the parent process via calls to mlockall() or mlock(). ⌫
Memory mappings created in the parent shall be retained in the child process. MAP_PRIVATE mappings inherited from the parent shall also be MAP_PRIVATE mappings in the child, and any modifications to the data in these mappings made by the parent prior to calling fork() shall be visible to the child. Any modifications to the data in MAP_PRIVATE mappings made by the parent after fork() returns shall be visible only to the parent. Modifications to the data in MAP_PRIVATE mappings made by the child shall be visible only to the child.
[PS] ⌦ For the SCHED_FIFO and SCHED_RR scheduling policies, the child process shall inherit the policy and priority settings of the parent process during a fork() function. For other scheduling policies, the policy and priority settings on fork() are implementation-defined. ⌫
Per-process timers created by the parent shall not be inherited by the child process.
[MSG] ⌦ The child process shall have its own copy of the message queue descriptors of the parent. Each of the message descriptors of the child shall refer to the same open message queue description as the corresponding message descriptor of the parent. ⌫
No asynchronous input or asynchronous output operations shall be inherited by the child process. Any use of asynchronous control blocks created by the parent produces undefined behavior.
A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls.
When the application calls fork() from a signal handler and any of the fork handlers registered by pthread_atfork() calls a function that is not async-signal-safe, the behavior is undefined.
[OB TRC TRI] ⌦ If the Trace option and the Trace Inherit option are both supported:
If the calling process was being traced in a trace stream that had its inheritance policy set to POSIX_TRACE_INHERITED, the child process shall be traced into that trace stream, and the child process shall inherit the parent's mapping of trace event names to trace event type identifiers. If the trace stream in which the calling process was being traced had its inheritance policy set to POSIX_TRACE_CLOSE_FOR_CHILD, the child process shall not be traced into that trace stream. The inheritance policy is set by a call to the posix_trace_attr_setinherited() function. ⌫
[OB TRC] ⌦ If the Trace option is supported, but the Trace Inherit option is not supported:
The child process shall not be traced into any of the trace streams of its parent process. ⌫
[OB TRC] ⌦ If the Trace option is supported, the child process of a trace controller process shall not control the trace streams controlled by its parent process. ⌫
[CPT] ⌦ The initial value of the CPU-time clock of the child process shall be set to zero. ⌫
[TCT] The initial value of the CPU-time clock of the single thread of the child process shall be set to zero.⌫
All other process characteristics defined by POSIX.1-2008 shall be the same in the parent and child processes. The inheritance of process characteristics not defined by POSIX.1-2008 is unspecified by POSIX.1-2008.
After fork(), both the parent and the child processes shall be capable of executing independently before either one terminates.
Most of these issues do not affect most programs, but multi-threaded programs that fork need to be very careful. It is worth reading the Rationale section of the POSIX definition of fork().
Inside the kernel, the system manages all the issues highlighted in the definition above. Memory page mapping tables have to be replicated. The kernel will typically mark the (writable) memory pages as COW — copy on write — so that until one or the other process modifies memory, they can access the same memory. This minimizes the cost of replicating the process; memory pages are only made distinct when they're modified. Many resources, though, such as file descriptors, have to be replicated, so fork() is quite an expensive operation (though not as expensive as the exec*() functions). Note that replicating a file descriptor leaves both descriptors referring to the same open file description — see the open() and dup2() system calls for a discussion of the distinctions between file descriptors and open file descriptions.

In simple terms, fork is called by one process but it returns in two processes.
To understand it well, a process is nothing but a data-structure inside the Linux kernel. That data-structure will have the memory location of the code that should run next. So typically when a process is in execution, all its code is loaded into RAM and the address of line to be executed will be stored in that data-structure. Whenever Linux schedules that process, it executes that particular line. Upon yield or interruption (pre-emption), that line to be executed variable will be updated in the data-structure. Lets call that data-structure as task_struct.
When a process calls fork, a replication of its task_struct will be created. Now you can see there will be two processes that will execute the same line of instruction upon scheduling. Thus all the lines after fork() call will be executed by both processes.
Suppose you want to run different instructions in the parent and child process, you should check the return value of fork and execute those instructions accordingly.
A simple explanation of fork using a C program is given at this video - https://www.youtube.com/watch?v=W0AP03RG85E

Related

In C programming language, how can variable store two values? [duplicate]

This question already has answers here:
How is it possible for fork() to return two values?
(5 answers)
Closed 7 years ago.
I've decided to learn C, and here is the snippet from one of the books that I use:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t result = fork();
if (result == -1){
fprintf(stderr, "Error\n");
return 1;
}
if (result == 0)
printf("I'm a child with PID = %d\n", getpid());
else
printf("I'm a parent with PID = %d\n", getpid());
return 0;
}
Its output is:
I'm a parent with PID = 5228
I'm a child with PID = 5229
Everything's clear, but how could it be that result == 0 and result != 0 at the same time? It looks like this variable stores two values, because the printf instruction is executed twice. I know, that fork() returns 0 and a parent's PID, but how does result check if it returns true for different conditions?
Because it's not the same variable. When you fork a process, you end up with two totally different processes (see this answer for more detail).
Hence the result variable in the parent is not the same as the one in the child. What you're seeing is two processes, both attached to the same output device, each writing their own message.
In fact, the fork documentation specifically covers that:
On success, the PID of the child process is returned in the parent, and 0 is returned in the child.
So you can use the return value from fork (as you do) to see if you're the parent or child (and to see if it worked as well, it'll return -1 if it fails and you'll be the parent with no child).
The idea is that the parent gets the process ID of the child so it can do something with it (like wait() for it to finish) and the child gets zero. The child can always get the process ID of the parent by calling getppid().
A variable can only hold a single value at a time. What you're seeing is happening because fork() is creating another process: there's now two instances of your program running; one in which result == 0 (the spawned process), and another where result != 0 (the original process)
fork replicates a child from a parent. So the newly created child inherits several properties like shared memory, message queue, file streams etc from the parent. So when you call fork, another process with another variable result is created.
The fork() function create a new process, after this line your program split to 2 from that spot. Because you need to know which process are you, the function return 0 if you are the child process, and some pid if you are the father process.
from the man page:
On success, the PID of the child process is returned in the parent, and
0 is returned in the child. On failure, -1 is returned in the parent,
no child process is created, and errno is set appropriately.
fork() function will create a new process. in parent process, fork() will return the pid of child process, so "result" variable will not equal with 0, and in child process, fork() just return 0, so "result" is 0.

C: fork() child processes

According to the textbook I am reading, the code below creates N child processes that will exit with unique statuses.
/* Parent creates N children */
for (i = 0; i < N; i++)
if ((pid[i] = Fork()) == 0) /* Child */
exit(100+i);
Earlier in the textbook, it states that the following code will have 8 lines of output:
int main(){
Fork();
Fork();
Fork();
printf("hello\n");
exit(0);
}
This leads me to believe that there are 2^n child processes, where n is the number of times fork() is called. Is the reason the first code only produces N child processes (as opposed to 2^N) because the child exits every time, so by the time the subsequent fork() is called, it only operates on the parent process?
Every successful call to fork(), creates a new process.
In the first example, the children processes (the return value of fork() being 0) call exit();, which means they won't call the next fork().
In the second example, every children process continues forking.
When fork() is called it copies the parent data and starts executing from that point individually. So the execution of the parent or child is depend on the scheduling of process. Whichever process get cpu time will be executed whether it is child or parent. We have to take care that which code should run by which process(child or process).

At what point does a fork() child process actually begin?

Does the process begin when fork() is declared? Is anything being killed here?
pid_t child;
child = fork();
kill (child, SIGKILL);
Or do you need to declare actions for the fork process in order for it to actually "begin"?
pid_t child;
child = fork();
if (child == 0) {
// do something
}
kill (child, SIGKILL);
I ask because what I am trying to do is create two children, wait for the first to complete, and then kill the second before exiting:
pid_t child1;
pid_t child2;
child1 = fork();
child2 = fork();
int status;
if (child1 == 0) { //is this line necessary?
}
waitpid(child1, &status, 0);
kill(child2, SIGKILL);
The C function fork is defined in the standard C library (glibc on linux). When you call it, it performs an equivalent system call (on linux its name is clone) by the means of a special CPU instruction (on x86 sysenter). This causes the CPU to switch to a privileged mode and start executing instructions of the kernel. The kernel then creates a new process (a record in a list and accompanying structures), which inherits a copy of memory mappings of the original process (text, heap, stack, and others), file descriptors and more.
The memory areas are marked as non-writable, so that when the new or the original process tries to overwrite them, the kernel gets to handle a CPU exception and perform a copy-on-write (therefore delaying the need to copy a memory page until absolutely necessary). That's because the mappings initially point to the same pages (pieces of physical memory) in both processes.
The kernel then gives execution to the scheduler, which decides which process to run next. It could be the original process, the child process, or any other process running in the system.
Note: The Linux kernel actually puts the child process in front of the parent process in the run queue, so it is run earlier than the parent. This is deemed to give better performance when the child calls exec right after forking.
When execution is given to the original process, the CPU is switched back to nonprivileged mode and starts executing the next instruction. In this case it continues with the fork function of the standard library, which returns the PID of the child process (as returned by the clone system call).
Similarly, the child process continues execution in the fork function, but here it returns 0 to the calling function.
After that, the program continues in both cases normally. The child process has the original process as the parent (this is noted in a structure in the kernel). When it exists, the parent process is supposed to do the cleanup (receiving the exit status of the child) by calling wait.
Note: The clone system call is rather complicated, because it unifies fork with the creation of threads, as well as linux namespaces. Other operating systems have different implementation of fork, e.g. FreeBSD has fork system call by itself.
Disclaimer: I am not a kernel developer. If you know better, please correct the answer.
See Also
clone (2)
The Design and Implementation of the FreeBSD Operating System (Google Books)
Understanding the Linux Kernel (Google Books)
Is it true that fork() calls clone() internally?
"Declare" is the wrong word to use in this context; C uses that word to talk about constructs that merely assert the existence of something, e.g.
extern int fork(void);
is a declaration of the function fork. Writing that in your code (or having it written for you as a consequence of #include <unistd.h>) does not cause fork to be called.
Now, the statement in your sample code, child = fork(); when written inside a function body, does (generate code to) make a call to the function fork. That function, assuming it is in fact the system primitive fork(2) on your operating system, and assuming it succeeds, has the special behavior of returning twice, once in the original process and once in a new process, with different return values in each so you can tell which is which.
So the answer to your question is that in both of the code fragments you showed, assuming the things I mentioned in the previous paragraph, all of the code after the child = fork(); line is at least potentially executed twice, once by the child and once by the parent. The if (child == 0) { ... } construct (again, this is not a "declaration") is the standard idiom for making parent and child do different things.
EDIT: In your third code sample, yes, the child1 == 0 block is necessary, but not to ensure that the child is created. Rather, it is there to ensure that whatever you want child1 to do is done only in child1. Moreover, as written (and, again, assuming all calls succeed) you are creating three child processes, because the second fork call will be executed by both parent and child! You probably want something like this instead:
pid_t child1, child2;
int status;
child1 = fork();
if (child1 == -1) {
perror("fork");
exit(1);
}
else if (child1 == 0) {
execlp("program_to_run_in_child_1", (char *)0);
/* if we get here, exec failed */
_exit(127);
}
child2 = fork();
if (child2 == -1) {
perror("fork");
kill(child1, SIGTERM);
exit(1);
}
else if (child2 == 0) {
execlp("program_to_run_in_child_2", (char *)0);
/* if we get here, exec failed */
_exit(127);
}
/* control reaches this point only in the parent and only when
both fork calls succeeded */
if (waitpid(child1, &status, 0) != child1) {
perror("waitpid");
kill(child1, SIGTERM);
}
/* only use SIGKILL as a last resort */
kill(child2, SIGTERM);
FYI, this is only a skeleton. If I were writing code to do this for real (which I have: see for instance https://github.com/zackw/tbbscraper/blob/master/scripts/isolate.c ) there would be a whole bunch more code just to comprehensively detect and report errors, plus the additional logic required to deal with file descriptor management in the children and a few other wrinkles.
The fork process spawns a new process identical to the old one and returns in both functions.
This happens automatically so you don't have to take any actions.
But nevertheless, it is cleaner to check if the call indeed succeeded:
A value below 0 indicates failure. In this case, it is not good to call kill().
A value == 0 indicates that we are the child process. In this case, it is not very clean to call kill().
A value > 0 indicates that we are the parent process. In this case, the return value is our child. Here it is safe to call kill().
In your case, you even end up with 4 processes:
Your parent calls fork(), being left with 2 processes.
Both of them call fork() again, resulting in a new child process for each of them.
You should move the 2nd fork() process into the branch where the parent code runs.
The child process begins some time after fork() has been called (there is some setup which happens in the context of the child).
You can be sure that the child is running when fork() returns.
So the code
pid_t child = fork();
kill (child, SIGKILL);
will kill the child. The child might execute kill(0, SIGKILL) which does nothing and returns an error.
There is no way to tell whether the child might ever live long enough to execute it's kill. Most likely, it won't since the Linux kernel will set up the process structure for the child and let the parent continue. The child will just be waiting in the ready list of the processes. The kill will then remove it again.
EDIT If fork() returns a value <= 0, then you shouldn't wait or kill.

What is the difference between fork()!=0 and !fork() in process creation

Currently, I am doing some exercises on operating system based on UNIX. I have used the fork() system call to create a child process and the code snippet is as follows :
if(!fork())
{
printf("I am parent process.\n");
}
else
printf("I am child process.\n");
And this program first executes the child process and then parent process.
But, when I replace if(!fork()) by if(fork()!=0) then the parent block and then child block executes.Here my question is - does the result should be the same in both cases or there is some reason behind this? Thanks in advance!!
There is no guaranteed order of execution.
However, if(!fork()) and if(fork()!=0) do give opposite results logically: if fork() returns zero, then !fork() is true whilst fork()!=0 is false.
Also, from the man page for fork():
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.
So the correct check is
pid_t pid = fork();
if(pid == -1) {
// ERROR in PARENT
} else if(pid == 0) {
// CHILD process
} else {
// PARENT process, and the child has ID pid
}
EDIT: As Wyzard says, you should definitely make sure you make use of pid later as well. (Also, fixed the type to be pid_t instead of int.)
You shouldn't really use either of those, because when the child finishes, it'll remain as a zombie until the parent finishes too. You should either capture the child's pid in a variable and use it to retrieve the child's exit status:
pid_t child_pid = fork();
if (child_pid == -1)
{
// Fork failed, check errno
}
else if (child_pid)
{
// Do parent stuff...
int status;
waitpid(child_pid, &status, 0);
}
else
{
// Child stuff
}
or you should use the "double-fork trick" to dissociate the child from the parent, so that the child won't remain as a zombie waiting for the parent to retrieve its exit status.
Also, you can't rely on the child executing before the parent after a fork. You have two processes, running concurrently, with no guarantee about relative order of execution. They may take turns, or they may run simultaneously on different CPU cores.
The order in which the parent and child get to their respective printf() statements is undefined. It is likely that if you were to repeat your tests a large number of times, the results would be similar for both, in that for either version there would be times that the parent prints first and times the parent prints last.
!fork() and fork() == 0 both behave in the same way.
The condition itself cannot be the reason the execution sequence is any different.
The process is replicated, which means that child is now competing with parent for resources, including CPU. It is the OS scheduler that decides which process will get the CPU.
The sequence in which child and parent processes are being execute is determined by the scheduler. It determines when and for how long each process is being executed by the processor. So the sequence of the output may vary for one and the same program code. It is purely coincidental that the change in the source code led to the change of the output sequence.
By the way, your printf's should be just the other way round: if fork() returns 0, it's the child, not the parent process.
See code example at http://en.wikipedia.org/wiki/Fork_%28operating_system%29. The German version of this article (http://de.wikipedia.org/wiki/Fork_%28Unix%29) contains a sample output and a short discusion about operation sequence.

How to make child process die after parent exits?

Suppose I have a process which spawns exactly one child process. Now when the parent process exits for whatever reason (normally or abnormally, by kill, ^C, assert failure or anything else) I want the child process to die. How to do that correctly?
Some similar question on stackoverflow:
(asked earlier) How can I cause a child process to exit when the parent does?
(asked later) Are child processes created with fork() automatically killed when the parent is killed?
Some similar question on stackoverflow for Windows:
How do I automatically destroy child processes in Windows?
Kill child process when parent process is killed
Child can ask kernel to deliver SIGHUP (or other signal) when parent dies by specifying option PR_SET_PDEATHSIG in prctl() syscall like this:
prctl(PR_SET_PDEATHSIG, SIGHUP);
See man 2 prctl for details.
Edit: This is Linux-only
I'm trying to solve the same problem, and since my program must run on OS X, the Linux-only solution didn't work for me.
I came to the same conclusion as the other people on this page -- there isn't a POSIX-compatible way of notifying a child when a parent dies. So I kludged up the next-best thing -- having the child poll.
When a parent process dies (for any reason) the child's parent process becomes process 1. If the child simply polls periodically, it can check if its parent is 1. If it is, the child should exit.
This isn't great, but it works, and it's easier than the TCP socket/lockfile polling solutions suggested elsewhere on this page.
I have achieved this in the past by running the "original" code in the "child" and the "spawned" code in the "parent" (that is: you reverse the usual sense of the test after fork()). Then trap SIGCHLD in the "spawned" code...
May not be possible in your case, but cute when it works.
Under Linux, you can install a parent death signal in the child, e.g.:
#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h> // perror()
// ...
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
Note that storing the parent process id before the fork and testing it in the child after prctl() eliminates a race condition between prctl() and the exit of the process that called the child.
Also note that the parent death signal of the child is cleared in newly created children of its own. It is not affected by an execve().
That test can be simplified if we are certain that the system process who is in charge of adopting all orphans has PID 1:
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() == 1)
exit(1);
// continue child execution ...
Relying on that system process being init and having PID 1 isn't portable, though. POSIX.1-2008 specifies:
The parent process ID of all of the existing child processes and zombie processes of the calling process shall be set to the process ID of an implementation-defined system process. That is, these processes shall be inherited by a special system process.
Traditionally, the system process adopting all orphans is PID 1, i.e. init - which is the ancestor of all processes.
On modern systems like Linux or FreeBSD another process might have that role. For example, on Linux, a process can call prctl(PR_SET_CHILD_SUBREAPER, 1) to establish itself as system process that inherits all orphans of any of its descendants (cf. an example on Fedora 25).
If you're unable to modify the child process, you can try something like the following:
int pipes[2];
pipe(pipes)
if (fork() == 0) {
close(pipes[1]); /* Close the writer end in the child*/
dup2(pipes[0], STDIN_FILENO); /* Use reader end as stdin (fixed per  maxschlepzig */
exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}
close(pipes[0]); /* Close the reader end in the parent */
This runs the child from within a shell process with job control enabled. The child process is spawned in the background. The shell waits for a newline (or an EOF) then kills the child.
When the parent dies--no matter what the reason--it will close its end of the pipe. The child shell will get an EOF from the read and proceed to kill the backgrounded child process.
For completeness sake. On macOS you can use kqueue:
void noteProcDeath(
CFFileDescriptorRef fdref,
CFOptionFlags callBackTypes,
void* info)
{
// LOG_DEBUG(#"noteProcDeath... ");
struct kevent kev;
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
kevent(fd, NULL, 0, &kev, 1, NULL);
// take action on death of process here
unsigned int dead_pid = (unsigned int)kev.ident;
CFFileDescriptorInvalidate(fdref);
CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example
int our_pid = getpid();
// when our parent dies we die as well..
LOG_INFO(#"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
exit(EXIT_SUCCESS);
}
void suicide_if_we_become_a_zombie(int parent_pid) {
// int parent_pid = getppid();
// int our_pid = getpid();
// LOG_ERROR(#"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);
int fd = kqueue();
struct kevent kev;
EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
kevent(fd, &kev, 1, NULL, 0, NULL);
CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
Inspired by another answer here, I came up with the following all-POSIX solution. The general idea is to create an intermediate process between the parent and the child, that has one purpose: Notice when the parent dies, and explicitly kill the child.
This type of solution is useful when the code in the child can't be modified.
int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
close(p[1]); // close write end of pipe
setpgid(0, 0); // prevent ^C in parent from stopping this process
child = fork();
if (child == 0) {
close(p[0]); // close read end of pipe (don't need it here)
exec(...child process here...);
exit(1);
}
read(p[0], 1); // returns when parent exits for any reason
kill(child, 9);
exit(1);
}
There are two small caveats with this method:
If you deliberately kill the intermediate process, then the child won't be killed when the parent dies.
If the child exits before the parent, then the intermediate process will try to kill the original child pid, which could now refer to a different process. (This could be fixed with more code in the intermediate process.)
As an aside, the actual code I'm using is in Python. Here it is for completeness:
def run(*args):
(r, w) = os.pipe()
child = os.fork()
if child == 0:
os.close(w)
os.setpgid(0, 0)
child = os.fork()
if child == 0:
os.close(r)
os.execl(args[0], *args)
os._exit(1)
os.read(r, 1)
os.kill(child, 9)
os._exit(1)
os.close(r)
Does the child process have a pipe to/from the parent process? If so, you'd receive a SIGPIPE if writing, or get EOF when reading - these conditions could be detected.
I don't believe it's possible to guarantee that using only standard POSIX calls. Like real life, once a child is spawned, it has a life of its own.
It is possible for the parent process to catch most possible termination events, and attempt to kill the child process at that point, but there's always some that can't be caught.
For example, no process can catch a SIGKILL. When the kernel handles this signal it will kill the specified process with no notification to that process whatsoever.
To extend the analogy - the only other standard way of doing it is for the child to commit suicide when it finds that it no longer has a parent.
There is a Linux-only way of doing it with prctl(2) - see other answers.
This solution worked for me:
Pass stdin pipe to child - you don't have to write any data into the stream.
Child reads indefinitely from stdin until EOF. An EOF signals that the parent has gone.
This is foolproof and portable way to detect when the parent has gone. Even if parent crashes, OS will close the pipe.
This was for a worker-type process whose existence only made sense when the parent was alive.
Some posters have already mentioned pipes and kqueue. In fact you can also create a pair of connected Unix domain sockets by the socketpair() call. The socket type should be SOCK_STREAM.
Let us suppose you have the two socket file descriptors fd1, fd2. Now fork() to create the child process, which will inherit the fds. In the parent you close fd2 and in the child you close fd1. Now each process can poll() the remaining open fd on its own end for the POLLIN event. As long as each side doesn't explicitly close() its fd during normal lifetime, you can be fairly sure that a POLLHUP flag should indicate the other's termination (no matter clean or not). Upon notified of this event, the child can decide what to do (e.g. to die).
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
int sv[2]; /* sv[0] for parent, sv[1] for child */
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
pid_t pid = fork();
if ( pid > 0 ) { /* parent */
close(sv[1]);
fprintf(stderr, "parent: pid = %d\n", getpid());
sleep(100);
exit(0);
} else { /* child */
close(sv[0]);
fprintf(stderr, "child: pid = %d\n", getpid());
struct pollfd mon;
mon.fd = sv[1];
mon.events = POLLIN;
poll(&mon, 1, -1);
if ( mon.revents & POLLHUP )
fprintf(stderr, "child: parent hung up\n");
exit(0);
}
}
You can try compiling the above proof-of-concept code, and run it in a terminal like ./a.out &. You have roughly 100 seconds to experiment with killing the parent PID by various signals, or it will simply exit. In either case, you should see the message "child: parent hung up".
Compared with the method using SIGPIPE handler, this method doesn't require trying the write() call.
This method is also symmetric, i.e. the processes can use the same channel to monitor each other's existence.
This solution calls only the POSIX functions. I tried this in Linux and FreeBSD. I think it should work on other Unixes but I haven't really tested.
See also:
unix(7) of Linux man pages, unix(4) for FreeBSD, poll(2), socketpair(2), socket(7) on Linux.
Install a trap handler to catch SIGINT, which kills off your child process if it's still alive, though other posters are correct that it won't catch SIGKILL.
Open a .lockfile with exclusive access and have the child poll on it trying to open it - if the open succeeds, the child process should exit
As other people have pointed out, relying on the parent pid to become 1 when the parent exits is non-portable. Instead of waiting for a specific parent process ID, just wait for the ID to change:
pit_t pid = getpid();
switch (fork())
{
case -1:
{
abort(); /* or whatever... */
}
default:
{
/* parent */
exit(0);
}
case 0:
{
/* child */
/* ... */
}
}
/* Wait for parent to exit */
while (getppid() != pid)
;
Add a micro-sleep as desired if you don't want to poll at full speed.
This option seems simpler to me than using a pipe or relying on signals.
I think a quick and dirty way is to create a pipe between child and parent. When parent exits, children will receive a SIGPIPE.
Another way to do this that is Linux specific is to have the parent be created in a new PID namespace. It will then be PID 1 in that namespace, and when it exits it all of it's children will be immediately killed with SIGKILL.
Unfortunately, in order to create a new PID namespace you have to have CAP_SYS_ADMIN. But, this method is very effective and requires no real change to the parent or the children beyond the initial launch of the parent.
See clone(2), pid_namespaces(7), and unshare(2).
Under POSIX, the exit(), _exit() and _Exit() functions are defined to:
If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.
So, if you arrange for the parent process to be a controlling process for its process group, the child should get a SIGHUP signal when the parent exits. I'm not absolutely sure that happens when the parent crashes, but I think it does. Certainly, for the non-crash cases, it should work fine.
Note that you may have to read quite a lot of fine print - including the Base Definitions (Definitions) section, as well as the System Services information for exit() and setsid() and setpgrp() - to get the complete picture. (So would I!)
If you send a signal to the pid 0, using for instance
kill(0, 2); /* SIGINT */
that signal is sent to the entire process group, thus effectively killing the child.
You can test it easily with something like:
(cat && kill 0) | python
If you then press ^D, you'll see the text "Terminated" as an indication that the Python interpreter have indeed been killed, instead of just exited because of stdin being closed.
In case it is relevant to anyone else, when I spawn JVM instances in forked child processes from C++, the only way I could get the JVM instances to terminate properly after the parent process completed was to do the following. Hopefully someone can provide feedback in the comments if this wasn't the best way to do this.
1) Call prctl(PR_SET_PDEATHSIG, SIGHUP) on the forked child process as suggested before launching the Java app via execv, and
2) Add a shutdown hook to the Java application that polls until its parent PID equals 1, then do a hard Runtime.getRuntime().halt(0). The polling is done by launching a separate shell that runs the ps command (See: How do I find my PID in Java or JRuby on Linux?).
EDIT 130118:
It seems that was not a robust solution. I'm still struggling a bit to understand the nuances of what's going on, but I was still sometimes getting orphan JVM processes when running these applications in screen/SSH sessions.
Instead of polling for the PPID in the Java app, I simply had the shutdown hook perform cleanup followed by a hard halt as above. Then I made sure to invoke waitpid in the C++ parent app on the spawned child process when it was time to terminate everything. This seems to be a more robust solution, as the child process ensures that it terminates, while the parent uses existing references to make sure that its children terminate. Compare this to the previous solution which had the parent process terminate whenever it pleased, and had the children try to figure out if they had been orphaned before terminating.
I found 2 solutions, both not perfect.
1.Kill all children by kill(-pid) when received SIGTERM signal.
Obviously, this solution can not handle "kill -9", but it do work for most case and very simple because it need not to remember all child processes.
var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});
var counter=0;
setInterval(function(){
console.log('c '+(++counter));
},1000);
if (process.platform.slice(0,3) != 'win') {
function killMeAndChildren() {
/*
* On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
* the process itself and children.
* On Windows, an JOB object has been applied to current process and children,
* so all children will be terminated if current process dies by anyway.
*/
console.log('kill process group');
process.kill(-process.pid, 'SIGKILL');
}
/*
* When you use "kill pid_of_this_process", this callback will be called
*/
process.on('SIGTERM', function(err){
console.log('SIGTERM');
killMeAndChildren();
});
}
By same way, you can install 'exit' handler like above way if you call process.exit somewhere.
Note: Ctrl+C and sudden crash have automatically been processed by OS to kill process group, so no more here.
2.Use chjj/pty.js to spawn your process with controlling terminal attached.
When you kill current process by anyway even kill -9, all child processes will be automatically killed too (by OS?). I guess that because current process hold another side of the terminal, so if current process dies, the child process will get SIGPIPE so dies.
var pty = require('pty.js');
//var term =
pty.spawn('any_child_process', [/*any arguments*/], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.cwd(),
env: process.env
});
/*optionally you can install data handler
term.on('data', function(data) {
process.stdout.write(data);
});
term.write(.....);
*/
Even though 7 years have passed I've just run into this issue as I'm running SpringBoot application that needs to start webpack-dev-server during development and needs to kill it when the backend process stops.
I try to use Runtime.getRuntime().addShutdownHook but it worked on Windows 10 but not on Windows 7.
I've change it to use a dedicated thread that waits for the process to quit or for InterruptedException which seems to work correctly on both Windows versions.
private void startWebpackDevServer() {
String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
logger.info("webpack dev-server " + cmd);
Thread thread = new Thread(() -> {
ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.directory(new File("."));
Process process = null;
try {
// Start the node process
process = pb.start();
// Wait for the node process to quit (blocking)
process.waitFor();
// Ensure the node process is killed
process.destroyForcibly();
System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
} catch (InterruptedException | IOException e) {
// Ensure the node process is killed.
// InterruptedException is thrown when the main process exit.
logger.info("killing webpack dev-server", e);
if (process != null) {
process.destroyForcibly();
}
}
});
thread.start();
}
Historically, from UNIX v7, the process system has detected orphanity of processes by checking a process' parent id. As I say, historically, the init(8) system process is a special process by only one reason: It cannot die. It cannot die because the kernel algorithm to deal with assigning a new parent process id, depends on this fact. when a process executes its exit(2) call (by means of a process system call or by external task as sending it a signal or the like) the kernel reassigns all children of this process the id of the init process as their parent process id. This leads to the most easy test, and most portable way of knowing if a process has got orphan. Just check the result of the getppid(2) system call and if it is the process id of the init(2) process then the process got orphan before the system call.
Two issues emerge from this approach that can lead to issues:
first, we have the possibility of changing the init process to any user process, so How can we assure that the init process will always be parent of all orphan processes? Well, in the exit system call code there's a explicit check to see if the process executing the call is the init process (the process with pid equal to 1) and if that's the case, the kernel panics (It should not be able anymore to maintain the process hierarchy) so it is not permitted for the init process to do an exit(2) call.
second, there's a race condition in the basic test exposed above. Init process' id is assumed historically to be 1, but that's not warranted by the POSIX approach, that states (as exposed in other response) that only a system's process id is reserved for that purpose. Almost no posix implementation does this, and you can assume in original unix derived systems that having 1 as response of getppid(2) system call is enough to assume the process is orphan. Another way to check is to make a getppid(2) just after the fork and compare that value with the result of a new call. This simply doesn't work in all cases, as both call are not atomic together, and the parent process can die after the fork(2) and before the first getppid(2) system call. The processparent id only changes once, when its parent does anexit(2)call, so this should be enough to check if thegetppid(2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children ofinit(8)`, but you can assume safely these processes as having no parent either (except when you substitute in a system the init process)
I've passed parent pid using environment to the child,
then periodically checked if /proc/$ppid exists from the child.
I managed to do a portable, non-polling solution with 3 processes by abusing terminal control and sessions.
The trick is:
process A is started
process A creates a pipe P (and never reads from it)
process A forks into process B
process B creates a new session
process B allocates a virtual terminal for that new session
process B installs SIGCHLD handler to die when the child exits
process B sets a SIGPIPE handler
process B forks into process C
process C does whatever it needs (e.g. exec()s the unmodified binary or runs whatever logic)
process B writes to pipe P (and blocks that way)
process A wait()s on process B and exits when it dies
That way:
if process A dies: process B gets a SIGPIPE and dies
if process B dies: process A's wait() returns and dies, process C gets a SIGHUP (because when the session leader of a session with a terminal attached dies, all processes in the foreground process group get a SIGHUP)
if process C dies: process B gets a SIGCHLD and dies, so process A dies
Shortcomings:
process C can't handle SIGHUP
process C will be run in a different session
process C can't use session/process group API because it'll break the brittle setup
creating a terminal for every such operation is not the best idea ever
If parent dies, PPID of orphans change to 1 - you only need to check your own PPID.
In a way, this is polling, mentioned above.
here is shell piece for that:
check_parent () {
parent=`ps -f|awk '$2=='$PID'{print $3 }'`
echo "parent:$parent"
let parent=$parent+0
if [[ $parent -eq 1 ]]; then
echo "parent is dead, exiting"
exit;
fi
}
PID=$$
cnt=0
while [[ 1 = 1 ]]; do
check_parent
... something
done

Resources