Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there some short example of how to use prctl() when it comes to setting subreapers?

Tags:

c

linux

process

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?

like image 575
Fly_37 Avatar asked Oct 18 '25 18:10

Fly_37


2 Answers

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:

  • The PARENT sets its CHILD_SUBREAPER to 1
  • The PARENT forks a CHILD, which forks a GRANDCHILD, with CHILD as parent
  • The CHILD exits, leaving the GRANDCHILD an orphan with PARENT as parent
  • The GRANDCHILD exits, and PARENT is notified that its last child has exited
  • The PARENT sets its CHILD_SUBREAPER to 0
  • The PARENT forks a CHILD, which forks a GRANDCHILD, with CHILD as parent
  • The CHILD exits, leaving the GRANDCHILD an orphan with process 1 (the init process) as parent
  • The PARENT sees that its last child has exited, and itself exits
  • The GRANDCHILD exits after the main program has returned
like image 69
Daniel Griscom Avatar answered Oct 20 '25 09:10

Daniel Griscom


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

like image 42
Sunil Kumar Avatar answered Oct 20 '25 08:10

Sunil Kumar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!