Why is SIGINT not propagated to child process when sent to its parent process?











up vote
53
down vote

favorite
18












Given a shell process (e.g. sh) and its child process (e.g. cat), how can I simulate the behavior of Ctrl+C using the shell's process ID?





This is what I've tried:



Running sh and then cat:



[user@host ~]$ sh
sh-4.3$ cat
test
test


Sending SIGINT to cat from another terminal:



[user@host ~]$ kill -SIGINT $PID_OF_CAT


cat received the signal and terminated (as expected).



Sending the signal to the parent process does not seem to work. Why is the signal not propagated to cat when sent to its parent process sh?



This does not work:



[user@host ~]$ kill -SIGINT $PID_OF_SH









share|improve this question




















  • 1




    The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
    – konsolebox
    Aug 11 '14 at 18:33















up vote
53
down vote

favorite
18












Given a shell process (e.g. sh) and its child process (e.g. cat), how can I simulate the behavior of Ctrl+C using the shell's process ID?





This is what I've tried:



Running sh and then cat:



[user@host ~]$ sh
sh-4.3$ cat
test
test


Sending SIGINT to cat from another terminal:



[user@host ~]$ kill -SIGINT $PID_OF_CAT


cat received the signal and terminated (as expected).



Sending the signal to the parent process does not seem to work. Why is the signal not propagated to cat when sent to its parent process sh?



This does not work:



[user@host ~]$ kill -SIGINT $PID_OF_SH









share|improve this question




















  • 1




    The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
    – konsolebox
    Aug 11 '14 at 18:33













up vote
53
down vote

favorite
18









up vote
53
down vote

favorite
18






18





Given a shell process (e.g. sh) and its child process (e.g. cat), how can I simulate the behavior of Ctrl+C using the shell's process ID?





This is what I've tried:



Running sh and then cat:



[user@host ~]$ sh
sh-4.3$ cat
test
test


Sending SIGINT to cat from another terminal:



[user@host ~]$ kill -SIGINT $PID_OF_CAT


cat received the signal and terminated (as expected).



Sending the signal to the parent process does not seem to work. Why is the signal not propagated to cat when sent to its parent process sh?



This does not work:



[user@host ~]$ kill -SIGINT $PID_OF_SH









share|improve this question















Given a shell process (e.g. sh) and its child process (e.g. cat), how can I simulate the behavior of Ctrl+C using the shell's process ID?





This is what I've tried:



Running sh and then cat:



[user@host ~]$ sh
sh-4.3$ cat
test
test


Sending SIGINT to cat from another terminal:



[user@host ~]$ kill -SIGINT $PID_OF_CAT


cat received the signal and terminated (as expected).



Sending the signal to the parent process does not seem to work. Why is the signal not propagated to cat when sent to its parent process sh?



This does not work:



[user@host ~]$ kill -SIGINT $PID_OF_SH






shell process signals






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 16 '15 at 19:49









muru

35.4k582157




35.4k582157










asked Aug 11 '14 at 18:27









rob87

268136




268136








  • 1




    The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
    – konsolebox
    Aug 11 '14 at 18:33














  • 1




    The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
    – konsolebox
    Aug 11 '14 at 18:33








1




1




The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
– konsolebox
Aug 11 '14 at 18:33




The shell has a way to ignore SIGINT signals not sent from the keyboard or terminal.
– konsolebox
Aug 11 '14 at 18:33










5 Answers
5






active

oldest

votes

















up vote
68
down vote



accepted










How CTRL+C works



The first thing is to understand how CTRL+C works.



When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).

The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty and looking at intr = ^C;.
The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.



What is the foreground process group?



So, now the question is, how do you determine what the foreground process group is?
The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTOP, SIGINT, etc).



Simplest way to determine the process group ID is to use ps:



ps ax -O tpgid


The second column will be the process group ID.



How do I send a signal to the process group?



Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.



This can be done with kill by putting a - in front of the group ID.

For example, if your process group ID is 1234, you would use:



kill -INT -1234


 





Simulate CTRL+C using the terminal number.



So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?



This becomes very easy.



Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).



kill -INT -$(ps h -t $tty -o tpgid | uniq)


This will send a SIGINT to whatever the foreground process group of $tty is.






share|improve this answer



















  • 5




    It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
    – Brian Bi
    Mar 15 '15 at 23:07






  • 3




    +1, for sends a SIGINT to the foreground process group of the terminal.
    – andy
    May 3 '15 at 7:06










  • Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
    – Ciro Santilli 新疆改造中心 六四事件 法轮功
    Aug 27 at 15:42


















up vote
14
down vote













As vinc17 says, there’s no reason for this to happen. 
When you type a signal-generating key sequence (e.g., Ctrl+C),
the signal is sent to all processes that are attached to (associated with) the terminal. 
There is no such mechanism for signals generated by kill.



However, a command like



kill -SIGINT -12345


will send the signal to all processes in process group 12345;
see kill(1)
and kill(2). 
Children of a shell are typically in the shell’s process group
(at least, if they’re not asynchronous),
so sending the signal to the negative of the PID of the shell may do what you want.





Oops



As vinc17 points out, this doesn’t work for interactive shells. 
Here’s an alternative that might work:



kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))


ps -pPID_of_shell gets process information on the shell. 
o tpgid= tells ps to output only the terminal process group ID, with no header. 
If this is less than 10000, ps will display it with leading space(s);
the $(echo …) is a quick trick to strip off leading (and trailing) spaces.



I did get this to work in cursory testing on a Debian machine.






share|improve this answer



















  • 1




    This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
    – vinc17
    Aug 11 '14 at 19:19


















up vote
12
down vote













The question contains its own answer. Sending the SIGINT to the cat process with kill is a perfect simulation of what happens when you press ^C.



To be more precise, the interrupt character (^C by default) sends SIGINT to every process in the terminal's foreground process group. If instead of cat you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C.



When you run any external command without the & background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.



That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat isn't going through the shell, whether it's normal input or a signal-generating special character like ^C. The cat process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat process because it's the foreground process group. The shell has gotten out of the way.



After the cat process dies, the shell will be notified, because it's the parent of the cat process. Then the shell becomes active and puts itself in the foreground again.



Here is an exercise to increase your understanding.



At the shell prompt in a new terminal, run this command:



exec cat


The exec keyword causes the shell to execute cat without creating a child process. The shell is replaced by cat. The PID that formerly belonged to the shell is now the PID of cat. Verify this with ps in a different terminal. Type some random lines and see that cat repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C now?



Answer:




SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.







share|improve this answer





















  • The shell has gotten out of the way. +1
    – Piotr Dobrogost
    Feb 1 '15 at 22:09










  • I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
    – Steven Lu
    Feb 20 at 18:28












  • The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
    – Wumpus Q. Wumbley
    Feb 20 at 20:05


















up vote
3
down vote













There's no reason to propagate the SIGINT to the child. Moreover the system() POSIX specification says: "The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate."



If the shell propagated the received SIGINT, e.g. following a real Ctrl-C, this would mean that the child process would receive the SIGINT signal twice, which may have unwanted behavior.






share|improve this answer























  • The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
    – goldilocks
    Aug 11 '14 at 18:51










  • @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
    – vinc17
    Aug 11 '14 at 19:07


















up vote
0
down vote













setpgid POSIX C process group minimal example



It might be easier to understand with a minimal runnable example of the underlying API.



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 parentn";
char child_str = "sigint childn";
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, %jun", (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, %jun", (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.






share|improve this answer























    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "106"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f149741%2fwhy-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    68
    down vote



    accepted










    How CTRL+C works



    The first thing is to understand how CTRL+C works.



    When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).

    The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty and looking at intr = ^C;.
    The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.



    What is the foreground process group?



    So, now the question is, how do you determine what the foreground process group is?
    The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTOP, SIGINT, etc).



    Simplest way to determine the process group ID is to use ps:



    ps ax -O tpgid


    The second column will be the process group ID.



    How do I send a signal to the process group?



    Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.



    This can be done with kill by putting a - in front of the group ID.

    For example, if your process group ID is 1234, you would use:



    kill -INT -1234


     





    Simulate CTRL+C using the terminal number.



    So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?



    This becomes very easy.



    Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).



    kill -INT -$(ps h -t $tty -o tpgid | uniq)


    This will send a SIGINT to whatever the foreground process group of $tty is.






    share|improve this answer



















    • 5




      It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
      – Brian Bi
      Mar 15 '15 at 23:07






    • 3




      +1, for sends a SIGINT to the foreground process group of the terminal.
      – andy
      May 3 '15 at 7:06










    • Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
      – Ciro Santilli 新疆改造中心 六四事件 法轮功
      Aug 27 at 15:42















    up vote
    68
    down vote



    accepted










    How CTRL+C works



    The first thing is to understand how CTRL+C works.



    When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).

    The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty and looking at intr = ^C;.
    The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.



    What is the foreground process group?



    So, now the question is, how do you determine what the foreground process group is?
    The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTOP, SIGINT, etc).



    Simplest way to determine the process group ID is to use ps:



    ps ax -O tpgid


    The second column will be the process group ID.



    How do I send a signal to the process group?



    Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.



    This can be done with kill by putting a - in front of the group ID.

    For example, if your process group ID is 1234, you would use:



    kill -INT -1234


     





    Simulate CTRL+C using the terminal number.



    So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?



    This becomes very easy.



    Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).



    kill -INT -$(ps h -t $tty -o tpgid | uniq)


    This will send a SIGINT to whatever the foreground process group of $tty is.






    share|improve this answer



















    • 5




      It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
      – Brian Bi
      Mar 15 '15 at 23:07






    • 3




      +1, for sends a SIGINT to the foreground process group of the terminal.
      – andy
      May 3 '15 at 7:06










    • Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
      – Ciro Santilli 新疆改造中心 六四事件 法轮功
      Aug 27 at 15:42













    up vote
    68
    down vote



    accepted







    up vote
    68
    down vote



    accepted






    How CTRL+C works



    The first thing is to understand how CTRL+C works.



    When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).

    The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty and looking at intr = ^C;.
    The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.



    What is the foreground process group?



    So, now the question is, how do you determine what the foreground process group is?
    The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTOP, SIGINT, etc).



    Simplest way to determine the process group ID is to use ps:



    ps ax -O tpgid


    The second column will be the process group ID.



    How do I send a signal to the process group?



    Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.



    This can be done with kill by putting a - in front of the group ID.

    For example, if your process group ID is 1234, you would use:



    kill -INT -1234


     





    Simulate CTRL+C using the terminal number.



    So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?



    This becomes very easy.



    Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).



    kill -INT -$(ps h -t $tty -o tpgid | uniq)


    This will send a SIGINT to whatever the foreground process group of $tty is.






    share|improve this answer














    How CTRL+C works



    The first thing is to understand how CTRL+C works.



    When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).

    The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty and looking at intr = ^C;.
    The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.



    What is the foreground process group?



    So, now the question is, how do you determine what the foreground process group is?
    The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTOP, SIGINT, etc).



    Simplest way to determine the process group ID is to use ps:



    ps ax -O tpgid


    The second column will be the process group ID.



    How do I send a signal to the process group?



    Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.



    This can be done with kill by putting a - in front of the group ID.

    For example, if your process group ID is 1234, you would use:



    kill -INT -1234


     





    Simulate CTRL+C using the terminal number.



    So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?



    This becomes very easy.



    Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).



    kill -INT -$(ps h -t $tty -o tpgid | uniq)


    This will send a SIGINT to whatever the foreground process group of $tty is.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jul 13 '16 at 2:01

























    answered Aug 11 '14 at 20:24









    Patrick

    49.7k11126178




    49.7k11126178








    • 5




      It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
      – Brian Bi
      Mar 15 '15 at 23:07






    • 3




      +1, for sends a SIGINT to the foreground process group of the terminal.
      – andy
      May 3 '15 at 7:06










    • Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
      – Ciro Santilli 新疆改造中心 六四事件 法轮功
      Aug 27 at 15:42














    • 5




      It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
      – Brian Bi
      Mar 15 '15 at 23:07






    • 3




      +1, for sends a SIGINT to the foreground process group of the terminal.
      – andy
      May 3 '15 at 7:06










    • Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
      – Ciro Santilli 新疆改造中心 六四事件 法轮功
      Aug 27 at 15:42








    5




    5




    It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
    – Brian Bi
    Mar 15 '15 at 23:07




    It's worth pointing out that signals that come directly from the terminal bypass permission checking, so Ctrl+C always succeeds in delivering signals unless you turn it off in the terminal attributes, whereas a kill command might fail.
    – Brian Bi
    Mar 15 '15 at 23:07




    3




    3




    +1, for sends a SIGINT to the foreground process group of the terminal.
    – andy
    May 3 '15 at 7:06




    +1, for sends a SIGINT to the foreground process group of the terminal.
    – andy
    May 3 '15 at 7:06












    Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
    – Ciro Santilli 新疆改造中心 六四事件 法轮功
    Aug 27 at 15:42




    Worth mentioning that the process group of the child is the same as the parent after fork. Minimal runnable C example at: unix.stackexchange.com/a/465112/32558
    – Ciro Santilli 新疆改造中心 六四事件 法轮功
    Aug 27 at 15:42












    up vote
    14
    down vote













    As vinc17 says, there’s no reason for this to happen. 
    When you type a signal-generating key sequence (e.g., Ctrl+C),
    the signal is sent to all processes that are attached to (associated with) the terminal. 
    There is no such mechanism for signals generated by kill.



    However, a command like



    kill -SIGINT -12345


    will send the signal to all processes in process group 12345;
    see kill(1)
    and kill(2). 
    Children of a shell are typically in the shell’s process group
    (at least, if they’re not asynchronous),
    so sending the signal to the negative of the PID of the shell may do what you want.





    Oops



    As vinc17 points out, this doesn’t work for interactive shells. 
    Here’s an alternative that might work:



    kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))


    ps -pPID_of_shell gets process information on the shell. 
    o tpgid= tells ps to output only the terminal process group ID, with no header. 
    If this is less than 10000, ps will display it with leading space(s);
    the $(echo …) is a quick trick to strip off leading (and trailing) spaces.



    I did get this to work in cursory testing on a Debian machine.






    share|improve this answer



















    • 1




      This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
      – vinc17
      Aug 11 '14 at 19:19















    up vote
    14
    down vote













    As vinc17 says, there’s no reason for this to happen. 
    When you type a signal-generating key sequence (e.g., Ctrl+C),
    the signal is sent to all processes that are attached to (associated with) the terminal. 
    There is no such mechanism for signals generated by kill.



    However, a command like



    kill -SIGINT -12345


    will send the signal to all processes in process group 12345;
    see kill(1)
    and kill(2). 
    Children of a shell are typically in the shell’s process group
    (at least, if they’re not asynchronous),
    so sending the signal to the negative of the PID of the shell may do what you want.





    Oops



    As vinc17 points out, this doesn’t work for interactive shells. 
    Here’s an alternative that might work:



    kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))


    ps -pPID_of_shell gets process information on the shell. 
    o tpgid= tells ps to output only the terminal process group ID, with no header. 
    If this is less than 10000, ps will display it with leading space(s);
    the $(echo …) is a quick trick to strip off leading (and trailing) spaces.



    I did get this to work in cursory testing on a Debian machine.






    share|improve this answer



















    • 1




      This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
      – vinc17
      Aug 11 '14 at 19:19













    up vote
    14
    down vote










    up vote
    14
    down vote









    As vinc17 says, there’s no reason for this to happen. 
    When you type a signal-generating key sequence (e.g., Ctrl+C),
    the signal is sent to all processes that are attached to (associated with) the terminal. 
    There is no such mechanism for signals generated by kill.



    However, a command like



    kill -SIGINT -12345


    will send the signal to all processes in process group 12345;
    see kill(1)
    and kill(2). 
    Children of a shell are typically in the shell’s process group
    (at least, if they’re not asynchronous),
    so sending the signal to the negative of the PID of the shell may do what you want.





    Oops



    As vinc17 points out, this doesn’t work for interactive shells. 
    Here’s an alternative that might work:



    kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))


    ps -pPID_of_shell gets process information on the shell. 
    o tpgid= tells ps to output only the terminal process group ID, with no header. 
    If this is less than 10000, ps will display it with leading space(s);
    the $(echo …) is a quick trick to strip off leading (and trailing) spaces.



    I did get this to work in cursory testing on a Debian machine.






    share|improve this answer














    As vinc17 says, there’s no reason for this to happen. 
    When you type a signal-generating key sequence (e.g., Ctrl+C),
    the signal is sent to all processes that are attached to (associated with) the terminal. 
    There is no such mechanism for signals generated by kill.



    However, a command like



    kill -SIGINT -12345


    will send the signal to all processes in process group 12345;
    see kill(1)
    and kill(2). 
    Children of a shell are typically in the shell’s process group
    (at least, if they’re not asynchronous),
    so sending the signal to the negative of the PID of the shell may do what you want.





    Oops



    As vinc17 points out, this doesn’t work for interactive shells. 
    Here’s an alternative that might work:



    kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))


    ps -pPID_of_shell gets process information on the shell. 
    o tpgid= tells ps to output only the terminal process group ID, with no header. 
    If this is less than 10000, ps will display it with leading space(s);
    the $(echo …) is a quick trick to strip off leading (and trailing) spaces.



    I did get this to work in cursory testing on a Debian machine.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Aug 11 '14 at 19:59

























    answered Aug 11 '14 at 18:54









    G-Man

    12.8k93164




    12.8k93164








    • 1




      This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
      – vinc17
      Aug 11 '14 at 19:19














    • 1




      This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
      – vinc17
      Aug 11 '14 at 19:19








    1




    1




    This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
    – vinc17
    Aug 11 '14 at 19:19




    This doesn't work when the process is started in an interactive shell (which is what the OP is using). I don't have a reference for this behavior, though.
    – vinc17
    Aug 11 '14 at 19:19










    up vote
    12
    down vote













    The question contains its own answer. Sending the SIGINT to the cat process with kill is a perfect simulation of what happens when you press ^C.



    To be more precise, the interrupt character (^C by default) sends SIGINT to every process in the terminal's foreground process group. If instead of cat you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C.



    When you run any external command without the & background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.



    That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat isn't going through the shell, whether it's normal input or a signal-generating special character like ^C. The cat process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat process because it's the foreground process group. The shell has gotten out of the way.



    After the cat process dies, the shell will be notified, because it's the parent of the cat process. Then the shell becomes active and puts itself in the foreground again.



    Here is an exercise to increase your understanding.



    At the shell prompt in a new terminal, run this command:



    exec cat


    The exec keyword causes the shell to execute cat without creating a child process. The shell is replaced by cat. The PID that formerly belonged to the shell is now the PID of cat. Verify this with ps in a different terminal. Type some random lines and see that cat repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C now?



    Answer:




    SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.







    share|improve this answer





















    • The shell has gotten out of the way. +1
      – Piotr Dobrogost
      Feb 1 '15 at 22:09










    • I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
      – Steven Lu
      Feb 20 at 18:28












    • The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
      – Wumpus Q. Wumbley
      Feb 20 at 20:05















    up vote
    12
    down vote













    The question contains its own answer. Sending the SIGINT to the cat process with kill is a perfect simulation of what happens when you press ^C.



    To be more precise, the interrupt character (^C by default) sends SIGINT to every process in the terminal's foreground process group. If instead of cat you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C.



    When you run any external command without the & background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.



    That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat isn't going through the shell, whether it's normal input or a signal-generating special character like ^C. The cat process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat process because it's the foreground process group. The shell has gotten out of the way.



    After the cat process dies, the shell will be notified, because it's the parent of the cat process. Then the shell becomes active and puts itself in the foreground again.



    Here is an exercise to increase your understanding.



    At the shell prompt in a new terminal, run this command:



    exec cat


    The exec keyword causes the shell to execute cat without creating a child process. The shell is replaced by cat. The PID that formerly belonged to the shell is now the PID of cat. Verify this with ps in a different terminal. Type some random lines and see that cat repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C now?



    Answer:




    SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.







    share|improve this answer





















    • The shell has gotten out of the way. +1
      – Piotr Dobrogost
      Feb 1 '15 at 22:09










    • I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
      – Steven Lu
      Feb 20 at 18:28












    • The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
      – Wumpus Q. Wumbley
      Feb 20 at 20:05













    up vote
    12
    down vote










    up vote
    12
    down vote









    The question contains its own answer. Sending the SIGINT to the cat process with kill is a perfect simulation of what happens when you press ^C.



    To be more precise, the interrupt character (^C by default) sends SIGINT to every process in the terminal's foreground process group. If instead of cat you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C.



    When you run any external command without the & background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.



    That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat isn't going through the shell, whether it's normal input or a signal-generating special character like ^C. The cat process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat process because it's the foreground process group. The shell has gotten out of the way.



    After the cat process dies, the shell will be notified, because it's the parent of the cat process. Then the shell becomes active and puts itself in the foreground again.



    Here is an exercise to increase your understanding.



    At the shell prompt in a new terminal, run this command:



    exec cat


    The exec keyword causes the shell to execute cat without creating a child process. The shell is replaced by cat. The PID that formerly belonged to the shell is now the PID of cat. Verify this with ps in a different terminal. Type some random lines and see that cat repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C now?



    Answer:




    SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.







    share|improve this answer












    The question contains its own answer. Sending the SIGINT to the cat process with kill is a perfect simulation of what happens when you press ^C.



    To be more precise, the interrupt character (^C by default) sends SIGINT to every process in the terminal's foreground process group. If instead of cat you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C.



    When you run any external command without the & background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.



    That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat isn't going through the shell, whether it's normal input or a signal-generating special character like ^C. The cat process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat process because it's the foreground process group. The shell has gotten out of the way.



    After the cat process dies, the shell will be notified, because it's the parent of the cat process. Then the shell becomes active and puts itself in the foreground again.



    Here is an exercise to increase your understanding.



    At the shell prompt in a new terminal, run this command:



    exec cat


    The exec keyword causes the shell to execute cat without creating a child process. The shell is replaced by cat. The PID that formerly belonged to the shell is now the PID of cat. Verify this with ps in a different terminal. Type some random lines and see that cat repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C now?



    Answer:




    SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.








    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Aug 11 '14 at 20:32









    Wumpus Q. Wumbley

    4,6351322




    4,6351322












    • The shell has gotten out of the way. +1
      – Piotr Dobrogost
      Feb 1 '15 at 22:09










    • I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
      – Steven Lu
      Feb 20 at 18:28












    • The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
      – Wumpus Q. Wumbley
      Feb 20 at 20:05


















    • The shell has gotten out of the way. +1
      – Piotr Dobrogost
      Feb 1 '15 at 22:09










    • I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
      – Steven Lu
      Feb 20 at 18:28












    • The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
      – Wumpus Q. Wumbley
      Feb 20 at 20:05
















    The shell has gotten out of the way. +1
    – Piotr Dobrogost
    Feb 1 '15 at 22:09




    The shell has gotten out of the way. +1
    – Piotr Dobrogost
    Feb 1 '15 at 22:09












    I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
    – Steven Lu
    Feb 20 at 18:28






    I don't understand why after exec cat pressing ^C wouldn't just land ^C into cat. Why would it terminate the cat which has now replaced the shell? Since the shell's been replaced, the shell is the thing that implements the logic of sending SIGINT to its children upon receiving ^C.
    – Steven Lu
    Feb 20 at 18:28














    The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
    – Wumpus Q. Wumbley
    Feb 20 at 20:05




    The point is that the shell doesn't send SIGINT to its children. The SIGINT comes from the terminal driver, and is sent to all foreground processes.
    – Wumpus Q. Wumbley
    Feb 20 at 20:05










    up vote
    3
    down vote













    There's no reason to propagate the SIGINT to the child. Moreover the system() POSIX specification says: "The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate."



    If the shell propagated the received SIGINT, e.g. following a real Ctrl-C, this would mean that the child process would receive the SIGINT signal twice, which may have unwanted behavior.






    share|improve this answer























    • The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
      – goldilocks
      Aug 11 '14 at 18:51










    • @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
      – vinc17
      Aug 11 '14 at 19:07















    up vote
    3
    down vote













    There's no reason to propagate the SIGINT to the child. Moreover the system() POSIX specification says: "The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate."



    If the shell propagated the received SIGINT, e.g. following a real Ctrl-C, this would mean that the child process would receive the SIGINT signal twice, which may have unwanted behavior.






    share|improve this answer























    • The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
      – goldilocks
      Aug 11 '14 at 18:51










    • @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
      – vinc17
      Aug 11 '14 at 19:07













    up vote
    3
    down vote










    up vote
    3
    down vote









    There's no reason to propagate the SIGINT to the child. Moreover the system() POSIX specification says: "The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate."



    If the shell propagated the received SIGINT, e.g. following a real Ctrl-C, this would mean that the child process would receive the SIGINT signal twice, which may have unwanted behavior.






    share|improve this answer














    There's no reason to propagate the SIGINT to the child. Moreover the system() POSIX specification says: "The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate."



    If the shell propagated the received SIGINT, e.g. following a real Ctrl-C, this would mean that the child process would receive the SIGINT signal twice, which may have unwanted behavior.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Aug 11 '14 at 19:05

























    answered Aug 11 '14 at 18:40









    vinc17

    8,7891736




    8,7891736












    • The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
      – goldilocks
      Aug 11 '14 at 18:51










    • @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
      – vinc17
      Aug 11 '14 at 19:07


















    • The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
      – goldilocks
      Aug 11 '14 at 18:51










    • @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
      – vinc17
      Aug 11 '14 at 19:07
















    The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
    – goldilocks
    Aug 11 '14 at 18:51




    The shell doesn't have to implement this with system(). But you're right, if it catches the signal (obviously it does) then there's no reason to propagate it downward.
    – goldilocks
    Aug 11 '14 at 18:51












    @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
    – vinc17
    Aug 11 '14 at 19:07




    @goldilocks I've completed my answer, perhaps giving a better reason. Note that the shell cannot know whether the child has already received the signal, hence the problem.
    – vinc17
    Aug 11 '14 at 19:07










    up vote
    0
    down vote













    setpgid POSIX C process group minimal example



    It might be easier to understand with a minimal runnable example of the underlying API.



    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 parentn";
    char child_str = "sigint childn";
    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, %jun", (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, %jun", (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.






    share|improve this answer



























      up vote
      0
      down vote













      setpgid POSIX C process group minimal example



      It might be easier to understand with a minimal runnable example of the underlying API.



      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 parentn";
      char child_str = "sigint childn";
      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, %jun", (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, %jun", (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.






      share|improve this answer

























        up vote
        0
        down vote










        up vote
        0
        down vote









        setpgid POSIX C process group minimal example



        It might be easier to understand with a minimal runnable example of the underlying API.



        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 parentn";
        char child_str = "sigint childn";
        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, %jun", (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, %jun", (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.






        share|improve this answer














        setpgid POSIX C process group minimal example



        It might be easier to understand with a minimal runnable example of the underlying API.



        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 parentn";
        char child_str = "sigint childn";
        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, %jun", (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, %jun", (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.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited yesterday

























        answered Aug 27 at 15:41









        Ciro Santilli 新疆改造中心 六四事件 法轮功

        4,83324039




        4,83324039






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Unix & Linux Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f149741%2fwhy-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Accessing regular linux commands in Huawei's Dopra Linux

            Can't connect RFCOMM socket: Host is down

            Kernel panic - not syncing: Fatal Exception in Interrupt