Reputation: 8424
I use the following code to wait for all child processes to finish:
int pid;
do {
pid = wait(NULL);
} while (pid != -1);
However, it seems not to work in the event that I have grandchildren running when their parent processes have died. So for example, say I have process Alpha that forks a child Beta, and Beta forks a child Gamma. Then Beta dies while Gamma continues to run. I want to be able to make Alpha wait for Gamma to finish. The catch is that I am not able to store Gamma's process id - Alpha needs to be able to wait for all children, grandchildren, great-grandchildren, etc. to finish without knowing their individual pids. Can this be done (if so how)?
Upvotes: 2
Views: 3420
Reputation: 51
There is actually one way to wait the grandchildren elegantly. Since Linux 3.4, there is a new API called PR_SET_CHILD_SUBREAPER
, which can make the parent process become something like init
. The parent process will reparent all the orphans in its descendent.
Following are copied from the man page of PRCTL(2):
A subreaper fulfills the role of init(1) for its descendant processes. When a process becomes orphaned (i.e., its immediate parent terminates) then that process will be reparented to the nearest still living ancestor subreaper. Subsequently, calls to getppid() in the orphaned process will now return the PID of the subreaper process, and when the orphan terminates, it is the subreaper process that will receive a SIGCHLD signal and will be able to wait(2) on the process to discover its termination status.
Upvotes: 5
Reputation: 58524
Create a pipe in the original process, and have all descendants inherit the write-end of the pipe. Termination of a descendant will implicitly close its inherited file descriptor. When the last file descriptor for the write-end is closed, the read-end of the pipe will be immediately readable for EOF.
So, when the original process is ready to wait for all descendants' terminations, it closes its write-end and then awaits an EOF on the read-end:
#ifdef FOR_REAL
# include <error-checking.h>
#endif
int p[2];
pipe(p); // Parent
if (! fork()) { // Child
close(p[0]); // Child: close inherited p[0] (not needed)
...
if (! fork()) { // Grandchild
...
exit(0); // Grandchild: inherited p[1] implicitly closed
}
...
exit(0); // Child: inherited p[1] implicitly closed
}
...
// Wait for all descendants to finish
char buf[1];
close(p[1]);
read(p[0], &buf, 1); // Returns 0 (EOF). Could also select(2) or poll(2)
Upvotes: 3
Reputation: 29586
That cannot be done easily.
When a process exits, its child processes are reparented to process 1 (init), because that is the only process that can be expected to wait()
on any child process, even ones it does not know -- other processes might not know how to handle SIGCHLD
, or even ignore processes they did not start, thus creating zombie processes.
There is no way to declare that your process wants to clean up any grandchildren, while cleaning up children is always expected of you.
The way to go would be to either have Beta go into a "waiting for children" state instead of terminating, or to start all child processes from Alpha.
Upvotes: 3