I am currently trying to learn how to use Linux prctl(PR_SET_CHILD_SUBREAPER), and prctl(PR_GET_CHILD_SUBREAPER).
Unfortunately, I don't seem to understand what is going on when I use these functions. Can someone help me to spot a slip in my understanding?
I've set the main process as subreaper. Then I tried to use fork() to create a child process and did it again to obtain a grandchild process. Then I killed child process, to see what happened with the grandchild, but I am unable to check it.
int main(void) {
int p = fork();
prctl(PR_SET_CHILD_SUBREAPER, 1);
if(p < 0)
return EXIT_FAILURE;
if(p > 0){
//Main process
printf("I am the MainProcess, My PID: %d and PPID: %d\n", getpid(), getppid());
}
else{
printf("I am the Child, My PID: %d and PPID: %d\n", getpid(), getppid());
int p2 = fork();
if(p2 < 0)
return EXIT_FAILURE;
if(p2 > 0){
//still Child process
}
else{
int *reaper = NULL;
prctl(PR_GET_CHILD_SUBREAPER, reaper);
printf("I am the Grandchild, My PID: %d and PPID: %d\n", getpid(), getppid());
printf("Reaper ID: %d\n", *reaper);
kill(getppid(), SIGKILL);
printf("I am the Grandchild, My PID: %d and PPID: %d\n", getpid(), getppid());
prctl(PR_GET_CHILD_SUBREAPER, reaper);
printf("Reaper ID: %d\n", *reaper);
}
return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
Output:
I am the MainProcess, My PID: 9088 and PPID: 23010
I am the Child, My PID: 9089 and PPID: 9088
I am the Grandchild, My PID: 9090 and PPID: 9089
To my surprise some printf() occurrences (in granchild part of code) were not called on runtime. What is the cause?
Here is a longer example of how PR_SET_CHILD_SUBREAPER works (based on Sunil Kumar's short answer):
#include <stdio.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
// Make a grandchild process, which sleep(2)s and then exits
int doGrandchild(void) {
int pid;
if ((pid = fork()) != 0) {
return pid;
}
printf(" GRANDCHILD(%d): before sleep, parent is %d\n", getpid(), getppid());
sleep(2); // Wait for CHILD to report and exit
printf(" GRANDCHILD(%d): after sleep, parent is %d\n", getpid(), getppid());
printf(" GRANDCHILD(%d): exiting\n", getpid());
exit(0);
// Will never return
}
// Make a child process, which makes a grandchild process, sleep(1)s, and then exits
int doChild(void) {
int pid;
if ((pid = fork()) != 0) {
return pid;
}
sleep(1); // Wait for PARENT to report fork
printf(" CHILD(%d): parent is %d\n", getpid(), getppid());
pid = doGrandchild();
printf(" CHILD(%d): forked grandchild %d\n", getpid(), pid);
sleep(1); // Wait for GRANDCHILD to report
printf(" CHILD(%d): exiting\n", getpid());
exit(0); // Exit before GRANDCHILD exits
// Will never return
}
// Wait for all child descendents to exit
void waitDescendents(void) {
printf("PARENT(%d): waiting for descendents to exit\n", getpid());
while(1) {
// Wait for any descendant process to exit
int pid = wait(NULL);
if(pid == -1) {
printf("PARENT(%d): no more descendants\n", getpid());
break;
} else {
printf("PARENT(%d): pid %d exited\n", getpid(), pid);
}
}
}
// Run the test
int main(void) {
int pid;
printf("PARENT(%d): parent is %d\n", getpid(), getppid());
prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0);
printf("PARENT(%d): ===== Run test with CHILD_SUBREAPER set to 1 =====\n", getpid());
pid = doChild();
printf("PARENT(%d): forked child %d\n", getpid(), pid);
waitDescendents();
prctl(PR_SET_CHILD_SUBREAPER, 0, 0, 0, 0);
printf("PARENT(%d): ===== Run test with CHILD_SUBREAPER set to 0 =====\n", getpid());
pid = doChild();
printf("PARENT(%d): forked child %d\n", getpid(), pid);
waitDescendents();
printf("PARENT(%d): ===== Exiting =====\n", getpid());
return 0;
}
Compiling and running:
griscom@nob:~/tmp$ cc -Wall subreaper_test.c -o subreaper_test
griscom@nob:~/tmp$ echo "My PID is $$"
My PID is 1001
griscom@nob:~/tmp$ ./subreaper_test
PARENT(1002): parent is 1001
PARENT(1002): ===== Run test with CHILD_SUBREAPER set to 1 =====
PARENT(1002): forked child 1003
PARENT(1002): waiting for descendents to exit
CHILD(1003): parent is 1002
CHILD(1003): forked grandchild 1004
GRANDCHILD(1004): before sleep, parent is 1003
CHILD(1003): exiting
PARENT(1002): pid 1003 exited
GRANDCHILD(1004): after sleep, parent is 1002
GRANDCHILD(1004): exiting
PARENT(1002): pid 1004 exited
PARENT(1002): no more descendants
PARENT(1002): ===== Run test with CHILD_SUBREAPER set to 0 =====
PARENT(1002): forked child 1005
PARENT(1002): waiting for descendents to exit
CHILD(1005): parent is 1002
CHILD(1005): forked grandchild 1006
GRANDCHILD(1006): before sleep, parent is 1005
CHILD(1005): exiting
PARENT(1002): pid 1005 exited
PARENT(1002): no more descendants
PARENT(1002): ===== Exiting =====
griscom@nob:~/tmp$ GRANDCHILD(1006): after sleep, parent is 1
GRANDCHILD(1006): exiting
The sequence:
Here is a small program of how PR_SET_CHILD_SUBREAPER works
#include <stdio.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
int *status;
int i=0;
prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0);
perror("PARENT:Set");
printf("PARENT: %d : my dad : %d\n", getpid(), getppid());
if(fork() != 0)
{
while(1)
{
wait(status);
if(++i == 2)
{
break;
}
}
int p = 1;
prctl(PR_GET_CHILD_SUBREAPER, &p);
printf("PARENT : %d\n",p);
printf("PARENT Exiting\n");
}
else
{
printf("Before CHILD: %d: my dad %d\n",getpid(), getppid());
if(fork() == 0)
{
int p = 1;
printf("Before grandchild: %d: my dad %d\n",getpid(), getppid());
sleep(2);
printf("After grandchild: %d: my dad %d\n",getpid(), getppid());
prctl(PR_GET_CHILD_SUBREAPER, &p);
printf("After grandchild : %d\n",p);
printf("Grandchild Exiting\n");
exit(0);
}
else
{
int p = 1;
prctl(PR_GET_CHILD_SUBREAPER, &p);
printf("After CHILD : %d\n",p);
printf("After CHILD: %d: my dad %d\n",getpid(), getppid());
printf("CHILD Exiting\n");
exit(1);
}
}
return 0;
}
Output:
PARENT:Set: Success
PARENT: 4002 : my dad : 2222
Before CHILD: 4003: my dad 4002
After CHILD : 0
After CHILD: 4003: my dad 4002
CHILD Exiting
Before grandchild: 4004: my dad 4003
After grandchild: 4004: my dad 4002
After grandchild : 0
Grandchild Exiting
PARENT : 1
PARENT Exiting
Observation:
The PARENT process(4002) has become the sub reaper by setting PR_SET_CHILD_SUBREAPER
The PARENT process(4002) has forked and created CHILD process(4003)
The CHILD process(4003) has forked and created Grandchild process(4004)
The CHILD process(4003) tries to use PR_GET_CHILD_SUBREAPER and receives 0. Since, prctl() has only instance it will not retain in the forked processes. Does the CHILD_SUBREAPER bit persist across fork()?
The CHILD process(4003) terminates making Grandchild process(4004) orphan process
Since, The PARENT process(4002) has been set as SUBREAPER Grandchild process(4004) becomes child of PARENT process(4002) and exit status of Grandchild process(4004) is received by PARENT process(4002) since, Child process has terminated
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With