28

Given the following code:

try:
  subprocess.Popen(ExternalProcess, stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True).communicate()
except KeyboardInterrupt:
  exit(0)

If during the execution of ExternalProcess(which is not a python script) one presses the Ctrl+C command, what exactly is going on?

Can I be sure for a 100% that in this scope, if I press the Ctrl+C, it will always get into the 'except' even if it happens during the execution of ExternalProcess?

Or it depends on how the external process deals with it?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
izac89
  • 3,790
  • 7
  • 30
  • 46
  • Here is a minimal example of process group and Ctrl + C for the C API which might give some insight: https://stackoverflow.com/questions/6108953/how-does-ctrl-c-terminate-a-child-process/52042970#52042970 – Ciro Santilli OurBigBook.com Aug 27 '18 at 21:29

2 Answers2

20

As far as I understand, once you fork/exec a process, it inherits the parent's process group. That means, that SIGINT (which is the result of Ctrl+C and the cause of KeyboardInterrupt) will be sent to both the child and the parent.

Here is an example:

File a.py:

import subprocess

try:
    subprocess.Popen("python b.py".split()).communicate()
except KeyboardInterrupt:
    print "a.py got ctrl-c"

File b.py

try:
    while True: pass
except KeyboardInterrupt:
    print "b.py got ctrl-c"

Now you run it and stop:

> python a.py
^Cb.py got ctrl-c
a.py got ctrl-c
bereal
  • 32,519
  • 6
  • 58
  • 104
  • 1
    That's not accurate. What happens is that `SIGINT` is sent to child when parent exits, not when you press `Ctrl+C`. There is no propagation. For example if you put a loop after `except KeyboardInterrupt` in parent, then `SIGINT` won't reach the child. I'm not sure about process group though. – freakish Nov 06 '13 at 09:04
  • 1
    @freakish I've just added `while True: pass` to the end of `a.py`, child is still receiving `SIGINT`. Besides, if `SIGINT` is deliveried after the parent's exit, why is `b.py got ctrl-c` printed first? – bereal Nov 06 '13 at 09:06
  • I assume that this might be a feature of Python's `.communicate`? For example it won't propagate in the case when you do `pr = subprocess.Popen(...)` (without `.communicate`) then loop with `try: time.sleep(1) except: pass`. – freakish Nov 06 '13 at 09:10
  • @freakish propagates fine for me. If you check `Popen.communicate()` source, there is nothing in it regarding the signals propagation. See also [this answer](http://unix.stackexchange.com/a/2917). – bereal Nov 06 '13 at 09:16
  • 2
    @freakish: The terminal sends the signal to all members of the same process group. See http://www.cs.ucsb.edu/~almeroth/classes/W99.276/assignment1/signals.html – Aaron Digulla Nov 06 '13 at 09:17
2

I assume you are using a Unix variant in my answer. I have no deeper knowledge about the Windows world.

If everything is configured normally, then the C-c will be interpreted by the terminal (xterm, gnome-terminal, …). This terminal will send a SIGINT (see kill -l to find out what your system's number for this is, typically it is 2) to all processes of the processgroup attached to the tty device associated with this terminal. That is either the shell or the program the shell started (and all its children because process groups get inherited).

A child can, however, leave its process group voluntarily and create a new one. Daemon processes typically do this to avoid being killed accidentally by a Ctrl-C pressed in the father program.

Alfe
  • 56,346
  • 20
  • 107
  • 159