How to make child process die after parent exits?
Solution 1
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
Solution 2
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.
Solution 3
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.
Solution 4
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).
Solution 5
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.
Related videos on Youtube
Paweł Hajdan
Updated on June 22, 2021Comments
-
Paweł Hajdan almost 3 years
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:
-
qrdl over 15 yearsYeah, Linux-specific. I don't think there is POSIX way to do it.
-
Shrey Shivam over 14 yearsI found this didn't happen reliably, at least on OS X.
-
Shrey Shivam over 14 yearsHmm. The documentation is vague and contradictory on this, but it appears that the parent process must be the lead process for the session, not just the process group. The lead process for the session was always login, and getting my process to take over as lead process for a new session was beyond my abilities at the moment.
-
Takagi about 14 yearsExcellent solution. Continuesly invoking getppid() until it returns 1 and then exit. This is good and I now use it too. A non-pollig solution would be nice though. Thank you Schof.
-
Rob K almost 14 yearsSIGHUP effectively only gets sent to child processes if the exiting process is a login shell. opengroup.org/onlinepubs/009695399/functions/exit.html "Termination of a process does not directly terminate its children. The sending of a SIGHUP signal as described below indirectly terminates children /in some circumstances/."
-
Jonathan Leffler almost 14 years@Rob: correct - that's what the quote I gave says, too: that only in some circumstances does the child process get a SIGHUP. And it is strictly an over-simplification to say that it is only a login shell that sends SIGHUP, though that is the most common case. If a process with multiple children sets itself up as the controlling process for itself and its children, then the SIGHUP will (conveniently) be sent to its children when the master dies. OTOH, processes seldom go to that much trouble - so I more more nit-picking than raising a really significant quibble.
-
Rob K over 13 yearsI fooled around with it for a couple of hours and couldn't get it to work. It would have nicely handled a case where I have a daemon with some children that all need to die when the parent exits.
-
Jarosław Bielawski over 13 yearsJust for info, on Solaris if you're in a zone, the
gettpid()
does not become 1 but gets thepid
of the zone scheduler (processzsched
). -
Rui Marques over 11 yearsIf anyone is wondering, in Android systems pid seems to be 0 (process System pid) instead of 1, when parent dies.
-
Juan about 11 yearsBeautiful....i made question before finding this answer, and if you want to post it please do so I will give you check. stackoverflow.com/q/15275983/960086
-
Aktau about 11 yearsVery cool, I'm really wondering if this has any reliability issues though. Have you tested this in production? With different apps?
-
Cong Ma about 11 years@Aktau, I've been using the Python equivalent of this trick in a Linux program. I needed it because the child's working logic is "to do best-effort cleanup after parent exits and then exit too". However, I'm really not sure about other platforms. The C snippet works on Linux and FreeBSD but that's all I know... Also, there are cases when you should be careful, such as the parent forking again, or parent giving up the fd before truly exiting (thus creating a time window for race condition).
-
Oleiade over 10 yearsNice, but five system calls, and a sh spawned in ten lines of codes lets me a bit sceptical about this piece of code performances.
-
Kamil Szot about 10 years
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
happily prints 4 instead of Terminated -
Greg Hewgill almost 10 years+1. You can avoid the
dup2
and taking over stdin by using theread -u
flag to read from a specific file descriptor. I also added asetpgid(0, 0)
in the child to prevent it from exiting when pressing ^C in the terminal. -
Good Person almost 10 years@qrdl What is the FreeBSD equivalent ?
-
qrdl almost 10 years@GoodPerson I have no idea, never tried any BSD systems
-
dgatwood about 9 yearsUnfortunately, that solution is not robust. What if the parent process dies before you get the initial value? The child will never exit.
-
Alexis Wilke about 9 yearsNote that a while back, under IRIX, I used a parent/child scheme where I had a pipe between both and reading from the pipe generated a SIGHUP if either one died. That was the way I used to kill my fork()'ed children, without the need for an intermediate process.
-
Alexis Wilke about 9 years@dgatwood, what do you mean?!? The first
getpid()
is done in the parent before callingfork()
. If the parent dies before that the child does not exist. What may happen is the child out living the parent for a while. -
Alexis Wilke about 9 yearsThe
PID equals 1
wait is not valid. The new parent could be some other PID. You should check whether it changes from the original parent (getpid() before the fork()) to the new parent (getppid() in the child not equal the getpid() when called before the fork()). -
Alexis Wilke about 9 yearsThe huge problem with doing the work in the parent is that you are changing the parent process. In case of a server that has to run "forever", that's not an option.
-
dgatwood about 9 yearsIn this somewhat contrived example, it works, but in real-world code, fork is almost invariably followed by exec, and the new process must start over by asking for its PPID. In the time between those two checks, if the parent goes away, the child would have no idea. Also, you're unlikely to have control over both the parent and child code (or else you could just pass the PPID as an argument). So as a general solution, that approach doesn't work very well. And realistically, if a UNIX-like OS came out without init being 1, so much stuff would break that I can't imagine anybody doing it anyway.
-
Maxim Egorushkin over 8 yearsThis is a poor solution because the parent might have died already. Race condition. Correct solution: stackoverflow.com/a/17589555/412080
-
Nish about 8 yearspass parent pid is command line argument when doing exec for child.
-
maxschlepzig almost 8 yearsCalling an answer poor isn't very nice - even if it doesn't address a race condition. See my answer on how to use
prctl()
in a race-condition free way. Btw, the answer linked by Maxim is incorrect. -
maxschlepzig almost 8 yearsThe argument order of the
dup2()
call is wrong. If you want to usepipes[0]
as stdin you have to writedup2(pipes[0], 0)
instead ofdup2(0, pipes[0])
. It isdup2(oldfd, newfd)
where the call closes a previously open newfd. -
maxschlepzig almost 8 years@Oleiade, I agree, especially since the spawned sh does just another fork to execute the real child process ...
-
maxschlepzig almost 8 yearsPolling at full speed is insane.
-
Shalom Crown over 7 yearsOn Linux the parent PID remains the same when the parent is killed.
-
Johannes Schaub - litb over 7 yearsI don't understand "That test can be simplified if we are certain that the grandparent is always the init process ". When a parent process dies, a process becomes a child of the init process (pid 1), not a child of the grandparent, right? So the test always seems to be correct.
-
maxschlepzig over 7 years@JohannesSchaub-litb, it doesn't have to be PID 1 - POSIX 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. For example, when running on a Fedora 25 system in a Gnome terminal, the special system process has PID != 1: gist.github.com/gsauthof/8c8406748e536887c45ec14b2e476cbc
-
Johannes Schaub - litb over 7 yearsinteresting, thanks. although, I fail to see what it has to do with the grandparent.
-
maxschlepzig over 7 years@JohannesSchaub-litb, the grandparent sentence was sloppy - probably wanted to start with an example at that time. I updated my answer to be more generic and more explicit about the general re-parenting logic.
-
Lothar over 7 yearsThis is just a wrong anser. It will send the signal to the child process at the time when the thread which called fork dies, not when the parent process dies.
-
qrdl over 7 years@Lothar It would be nice to see some kind of proof.
man prctl
says: Set the parent process death signal of the calling process to arg2 (either a signal value in the range 1..maxsig, or 0 to clear). This is the signal that the calling process will get when its parent dies. This value is cleared for the child of a fork(2) and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary. -
R.. GitHub STOP HELPING ICE about 7 yearsI think your second caveat is wrong. The pid of a child is a resource belonging to its parent and it can't be freed/reused until the parent (the intermediate process) waits on it (or terminates and lets init wait on it).
-
Luis Colorado about 7 years@neoneye, what's the interest of having a process just continously invoking
getppid()
toexit()
once it gets a different result? Shouldn't it be possible to just call it when neccessary? Shouldn't it be better to callexit()
in the child process immediately and not get a system crowded of processes that only wait for something to just callexit()
? -
Luis Colorado about 7 years@JohannesSchaub-litb, you cannot always assume that the granparent of a process will be
init(8)
process.... the only thing you can assume is that when a parent process dies, is that its parent id will change. This actually happens once in the life of a process.... and is when the process' parent dies. There's only one main exception to this, and is forinit(8)
children, but you are protected from this, asinit(8)
neverexit(2)
(kernel panics in that case) -
Alcaro about 7 yearsSIGPIPE isn't sent on pipe close, it's only sent when the child tries to write to it.
-
rox about 7 years@maxschlepzig I can't find the answer from the link
-
maxschlepzig about 7 years@rox - I don't follow. Doesn't work the link to my answer for you?
-
rox about 7 years@maxschlepzig Thanks for the new link. Seems like the previous link is invalid. By the way, after years, there's still no api for setting options on the parent side. What a pity.
-
rox almost 7 yearsUnfortunately, if a child forks from a thread, and then the thread exit, the child process wil get the SIGTERM.
-
Jean almost 7 yearsOr, the child could open the lockfile in a separate thread, in blocking mode, in which case this could be a pretty nice and clean solution. Probably it has some portability limitations though.
-
Sebastien over 6 yearsTo have a more robust and platform independent way of doing it, before fork()-ing, simply getpid() and if getppid() from child is different, exit.
-
Omnifarious over 6 years@Aktau - This will be completely reliable.
-
joonas.fi about 6 years@SebastianJylanki I don't remember if I tried, but it probably works because the primitives (POSIX streams) are fairly standard across OSs.
-
Lucretiel almost 6 yearsThis doesn't work if you don't control the child process. For instance, I'm working on a command that wraps find(1), and I want to make sure the find is killed if the wrapper dies for some reason.
-
russbishop almost 6 yearsYou can do this with a slightly nicer API, using dispatch sources with DISPATCH_SOURCE_PROC and PROC_EXIT.
-
Darshan b about 5 yearsBut is polling really required. The parent can keep a track of child process ids which are returned during the process creation from fork. The parent will exit only on grace full completion of the task or because of signal. In both the cases the a signal can be sent to the child process. And the child can terminate gracefully on catching the signal. This will ensure that the response is immediate. in case of polling the cpu is wasted and the child might be alive for a longer time than the parent. further if you are using the sleep to poll then sleep is not exactly accurate.
-
Qix - MONICA WAS MISTREATED almost 5 yearsFor whatever reason, this is causing my Mac to panic. Running a process with this code has a 50% chance or so of it freezing, causing the fans to spin at a rate I've never heard them go before (super fast), and then the mac just shuts off. BE VERY CAREFUL WITH THIS CODE.
-
Yi Lin Liu almost 5 yearsIt seems like on my macOS, the child process exits automatically after parent exits. I don't know why.
-
Takagi almost 5 years@YiLinLiu iirc I used
NSTask
or posix spawn. See thestartTask
function in my code here: github.com/neoneye/newton-commander-browse/blob/master/Classes/… -
Omnifarious about 4 yearsI need to edit in another way. It's possible to use prctl to make a process act as the init process for all it's children and grandchildren, and great grandchildren, etc...
-
John Isaac almost 4 yearspoint of caution: systemd disables SIGPIPE's by default in services it manages, but you can still check for the pipe closure. See freedesktop.org/software/systemd/man/systemd.exec.html under IgnoreSIGPIPE
-
locka over 3 yearsThis signals the child if the parent thread dies. If you spawned the child from a temporary thread then it might die prematurely. I found this out the hard way. I think the safest way, assuming you wrote the child code is to poll the parent pid and if it's not there then quit.
-
y_159 over 3 yearsIn the commented line,
// continue child execution
, even if doexecve
to execute a bash script, then also it will work(the same piece of code in the example)? -
maxschlepzig over 3 years@y_159 yes, I've covered
execve
in my answer. And bash isn't special here. -
y_159 over 3 years@maxschlepzig when that signal is delivered to the child when it's parent dies, the child will automatically die too?
-
maxschlepzig over 3 years@y_159 depends on the signal you specify with the
prctl()
call. WithSIGTERM
(which I used in the example) the default action is to terminate. But a process could modify that action, e.g. it could even ignore it. You may also useSIGKILL
there which can't be caught/ignored. -
y_159 over 3 yearsok, thanks, I've used
SIGTERM
only as mentioned in the example, in the same way, I've not written explicitly in the child's code(a bash script in my case) to ignore any signal, so by default, it should terminate only? -
maxschlepzig over 3 years@y_159 yes, it should.
-
patraulea over 3 yearsIs there a way to set PDEATHSIG when starting children via posix_spawn() ?
-
qrdl over 3 years@patraulea It is responsibility of the child to request the signal to be delivered, regardless of how this child was created - using
fork
orposix_spawn
-
Ternvein about 3 years@Darshanb, if you read the question carefully, there is such sentence: normally or abnormally, by kill, ^C, assert failure or anything else. Quite obviously, you can't guarantee graceful termination in such circumstances.
-
Lothar about 3 yearsWith all the container and virutalisation methods that exist or might come in the future, it's not save to use this implementation hack from old unix systems.
-
Jonas Due Vesterheden almost 3 years@russbishop - I tried your suggestion of using a dispatch source, but it did not work for me. Here's a gist with the code I tried: gist.github.com/jdv85/5a67ae81247f21433044b0ffea404693 The event handler block does not run. Using
kqueue
as in the answer from @neoneye works fine. -
russbishop almost 3 years@JonasDueVesterheden If you check the libdispatch sources it is using a kqueue under the covers so it should be identical. You don't appear to be retaining the source anywhere so it would get released and cancelled when the function exits. If you capture the source in the handler block it would resolve that problem.
-
timotheecour over 2 yearsisn't
CFRunLoopRun();
missing afterCFRelease(source);
? similar to developer.apple.com/documentation/corefoundation/… which usedCFRunLoopRunInMode(kCFRunLoopDefaultMode, 20.0, false);
also, I'm not observing the panicing behavior from stackoverflow.com/questions/284325/…