What is the difference between Ctrl-C and SIGINT?
Asked Answered
C

2

52

I have been debugging a Python program which segfaults after receiving a KeyboardInterrupt exception. This is normally done by pressing Ctrl+C from the shell. To test if a particular code change fixed the bug, I had a small shell-script that sent SIGINT to the program at random time after start-up. The problem I have is that sending Ctrl+C seems to have a different effect on the program than sending the signal SIGINT and is thus not causing the bug to appear, so I quite wonder what the difference is then between the two actions.

The program does not catch any keyboard actions at all, and is just a python program with some threads/processes in them. It installs no signal handlers (though Python does), and stty -a gives intr = ^C. I suspect it might be that Ctrl+C sends SIGINT to all the sub-processes/threads while kill -INT only sends to the primary process, but that is as far my suspicions go.

Here is the shell script which sends the kill -INT.

wait
while :; do
    seconds="$(python -c 'import random; print random.random()*4')"
    ./mandos --debug --configdir=confdir \
             --statedir=statedir --no-restore --no-dbus &
    pid=$!
    { sleep $seconds; kill -INT $pid; } &
    fg %./mandos
    status=$?
    if [ $status -gt 1 ]; then
        echo "Failed exit $status after $seconds seconds"
        break
    fi
    wait
done
Capparidaceous answered 6/12, 2011 at 11:2 Comment(7)
I'm not sure how much of a difference this would be but its possible ctrl+c is sending SIGTERM instead of SIGINT. Also, when handling the exception are you cleaning up your sub-processes/threads correctly? The way python handles threads I don't believe it would seg-fault, but its probably possible with sub-processes.Chlamys
<CTRL>+C can be configured, so check your stty -a settings, look for intr = ^C, maybe ^C is set for something else as well?Jamestown
is there any multithreading in the code?Exoergic
just saw that you said there were threads in your OP. I would say that could be your problem. The threads aren't being terminated correctly when the script receives the SIGINT.Exoergic
Do you mean threads are effected if you do ctrl + C compared to sending the signal -INT? The reason I said threads is that Im using multiprocessing manager, and the way python implement it uses the threading library.Capparidaceous
@Capparidaceous : be carefull, despite the interfaces are similar, multiprocessing is not the same than threading. With multiprocess, python creates real processes, not threads.Awl
You could always try to debug where the seg fault is: docs.python.org/library/pdb.html If your using eclipse it should be easy to see where it seg faults and to see a stack trace on whats going on.Chlamys
F
46

^C sends a SIGINT to all the processes in the foreground process group. To do the equivalent with kill, you should send the signal to the process group (OS-level concept):

kill -SIGINT -<pid>

or to the job (shell-level concept, the pipeline ended with &):

kill -SIGINT %
Feoffee answered 6/12, 2011 at 20:30 Comment(7)
This was what I was looking for, but for unknown reasons the code still do not segfault when I use the automated script. I found the bug causing python to crash - python library multiprocessing is implemented with threads, and it causes segfaults in gobject if one do not call gobject.threads_init. Still, the original question for me is left as a mystery as why the manually invoked ctrl+c triggered the bug, but the automated script did not.Capparidaceous
Sorry for being obtuse, but what does the '%' mean in this context?Negotiant
@KevinCantwell: it refers to the current job, i.e. the last backgrounded job (either started in the background, or stopped while it was in the foreground, and thus backgrounded).Feoffee
@Feoffee Am I correct in assuming that it is the shell that translates ^C to SIGINT and then sends the latter to the processes in the foreground process group? Or does the Python interpreter partake in this process in any way?Sprite
@balu: SIGINT is handled by the line discipline, see termios(3) VINTR. See also stty(1) for the user level command to change line disciplinr settings. So, the shell just has to configure correctly the line discipline, and set the foreground process group with tcsetpgrp(3).Feoffee
@Feoffee This is the first time I'm hearing of the term line discipline, so this was really helpful! Thanks for the pointer!Sprite
Addendum: I've found linusakesson.net/programming/tty (section "An example") really helpful to understand where the line discipline enters the game on modern desktop systems.Sprite
P
9

As described here :

Python installs a small number of signal handlers by default: SIGPIPE is ignored (so write errors on pipes and sockets can be reported as ordinary Python exceptions) and SIGINT is translated into a KeyboardInterrupt exception. All of these can be overridden.

so, the behaviour should be the same between sending a SIGINT and a Ctrl + c.

But, you have to be carefull with the KeyboardInterrupt, if somewhere in your code you've got a

try:
   ...
except:   # notice the lack of exception class
   pass

this will "eat" the KeyboardInterrupt exception.

Partida answered 6/12, 2011 at 11:16 Comment(2)
Thanks for the advice. found one line doing that, and I think that will fix an unrelated bug. But even with that commented out, the primary issue with segfault is still there, and the more weird one that sending kill -INT do not trigger it but ctrl + c does.Capparidaceous
I like this answer because it makes clear where exactly the translation between SIGINT and KeyboardInterrupt happens. As for your warning: For this reason I would always recommend using try: ... except Exception: ... as a catch-all for "normal" exceptions, so as to not accidentally catch unusual ones like KeyboardInterrupt and SystemExit, compare link 1 and link 2.Sprite

© 2022 - 2024 — McMap. All rights reserved.