Will ctrl+c send SIGINT signals to both parent and child processes in Linux?

后端 未结 2 1198
青春惊慌失措
青春惊慌失措 2020-12-31 18:59

In the terminal, I executed a main parent process which will fork a child process. In both the parent and child processes I implemented a SIGINT signal handler. So when I p

相关标签:
2条回答
  • 2020-12-31 19:37

    In both the parent and child processes I implemented a SIGINT signal handler. So when I press "ctrl+c", will both the handlers be called at the same time?

    Yes, they both will receive SIGINT.

    Or do I need to call the child process's signal handler explicitly in the parent process's handler?

    "Calling" another process' signal handler doesn't make sense. If the both the process have a handler installed then they will be called once they receive the signal SIGINT.

    I just didn't quite understand what does "foreground process group" means.

    Typically, a process associated with a controlling terminal is foreground process and its process group is called foreground process group. When you start a process from the command line, it's a foreground process:

    E.g.

    $ ./script.sh # foreground process
    $ ./script & # background process
    

    I suggest you read about tty and The TTY demystified for a detailed explanation.

    0 讨论(0)
  • 2020-12-31 19:43

    setpgid POSIX C process group minimal example

    This illustrates how the signal does get sent to the child, if the child didn't change its process group with setpgid.

    main.c

    #define _XOPEN_SOURCE 700
    #include <assert.h>
    #include <signal.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    volatile sig_atomic_t is_child = 0;
    
    void signal_handler(int sig) {
        char parent_str[] = "sigint parent\n";
        char child_str[] = "sigint child\n";
        signal(sig, signal_handler);
        if (sig == SIGINT) {
            if (is_child) {
                write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
            } else {
                write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
            }
        }
    }
    
    int main(int argc, char **argv) {
        pid_t pid, pgid;
    
        (void)argv;
        signal(SIGINT, signal_handler);
        signal(SIGUSR1, signal_handler);
        pid = fork();
        assert(pid != -1);
        if (pid == 0) {
            /* Change the pgid.
             * The new one is guaranteed to be different than the previous, which was equal to the parent's,
             * because `man setpgid` says:
             * > the child has its own unique process ID, and this PID does not match
             * > the ID of any existing process group (setpgid(2)) or session.
             */
            is_child = 1;
            if (argc > 1) {
                setpgid(0, 0);
            }
            printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
            assert(kill(getppid(), SIGUSR1) == 0);
            while (1);
            exit(EXIT_SUCCESS);
        }
        /* Wait until the child sends a SIGUSR1. */
        pause();
        pgid = getpgid(0);
        printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
        /* man kill explains that negative first argument means to send a signal to a process group. */
        kill(-pgid, SIGINT);
        while (1);
    }
    

    GitHub upstream.

    Compile with:

    gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
    

    Run without setpgid

    Without any CLI arguments, setpgid is not done:

    ./setpgid
    

    Possible outcome:

    child pid, pgid = 28250, 28249
    parent pid, pgid = 28249, 28249
    sigint parent
    sigint child
    

    and the program hangs.

    As we can see, the pgid of both processes is the same, as it gets inherited across fork.

    Then whenever you hit:

    Ctrl + C
    

    It outputs again:

    sigint parent
    sigint child
    

    This shows how:

    • to send a signal to an entire process group with kill(-pgid, SIGINT)
    • Ctrl + C on the terminal sends a kill to the entire process group by default

    Quit the program by sending a different signal to both processes, e.g. SIGQUIT with Ctrl + \.

    Run with setpgid

    If you run with an argument, e.g.:

    ./setpgid 1
    

    then the child changes its pgid, and now only a single sigint gets printed every time from the parent only:

    child pid, pgid = 16470, 16470
    parent pid, pgid = 16469, 16469
    sigint parent
    

    And now, whenever you hit:

    Ctrl + C
    

    only the parent receives the signal as well:

    sigint parent
    

    You can still kill the parent as before with a SIGQUIT:

    Ctrl + \
    

    however the child now has a different PGID, and does not receive that signal! This can seen from:

    ps aux | grep setpgid
    

    You will have to kill it explicitly with:

    kill -9 16470
    

    This makes it clear why signal groups exist: otherwise we would get a bunch of processes left over to be cleaned manually all the time.

    Tested on Ubuntu 18.04.

    0 讨论(0)
提交回复
热议问题