Ctrl-C和SIGINT有什么区别?

52

我一直在调试一个Python程序,该程序在接收到KeyboardInterrupt异常后崩溃。通常可以通过在shell中按下Ctrl+C来完成此操作。为了测试某个代码更改是否修复了错误,我有一个小型 shell 脚本,在启动后随机时间向程序发送 SIGINT 信号。我的问题是,发送 Ctrl+C 看起来对程序产生的影响与发送信号 SIGINT 不同,因此没有导致错误出现,所以我很想知道两种行为之间的区别。

该程序根本不捕获任何键盘动作,只是一个带有一些线程/进程的 Python 程序。它不安装任何信号处理程序(虽然 Python 会),并且 运行 stty -a 命令会显示 intr = ^C。我怀疑可能是 Ctrl+C 向所有子进程/线程发送 SIGINT,而 kill -INT 只发送给主要进程,但这是我怀疑的范围。

这是发送 kill -INT 的 shell 脚本:

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

我不确定这会有多大的区别,但是可能是ctrl+c发送的是SIGTERM而不是SIGINT。此外,在处理异常时,您是否正确清理了子进程/线程?Python处理线程的方式我不认为会导致段错误,但使用子进程可能是可能的。 - David
代码中有多线程吗? - Casey
你的意思是如果你按ctrl + C而不是发送信号-INT,线程会受到影响吗?我之所以说线程,是因为我正在使用多进程管理器,而Python实现它的方式使用了线程库。 - Belorn
@Belorn:要小心,尽管接口相似,但多进程不同于线程。使用多进程时,Python会创建真正的进程,而不是线程。 - Cédric Julien
你可以尝试调试一下,看看段错误出现在哪里:http://docs.python.org/library/pdb.html 如果你使用的是Eclipse,那么很容易就能看到它在哪里发生了段错误,并且查看正在发生什么的堆栈跟踪。 - David
显示剩余2条评论
2个回答

46

^C会向前台进程组中的所有进程发送一个SIGINT信号。要使用kill实现相同的功能,应该将信号发送到进程组(操作系统级别的概念):

kill -SIGINT -<pid>

或者对于工作(shell-level概念,流水线以 & 结束):

kill -SIGINT %

3
抱歉我有点迟钝,但在这个上下文中 '%' 是什么意思? - Kevin Cantwell
1
@KevinCantwell:它指的是当前作业,即最后一个后台作业(无论是在后台启动还是在前台停止并因此转为后台)。 - ninjalj
1
@balu:SIGINT由行规程处理,请参见termios(3)VINTR。另请参阅stty(1),以更改行规程设置的用户级命令。因此,shell只需正确配置行规程,并使用tcsetpgrp(3)设置前台进程组即可。 - ninjalj
1
@ninjalj 这是我第一次听到“行纪律”这个术语,所以这真的很有帮助!感谢你的指引! - balu
1
附注:我发现https://www.linusakesson.net/programming/tty/(“示例”部分)非常有帮助,可以理解线路纪律在现代桌面系统中的作用。 - balu
显示剩余2条评论

9

此处所述

Python默认安装了少量信号处理程序:忽略SIGPIPE(因此可以将管道和套接字上的写错误报告为普通的Python异常),并将SIGINT转换为KeyboardInterrupt异常。所有这些都可以被覆盖。

因此,发送SIGINT和Ctrl + c应该具有相同的行为。

但是,您必须小心处理KeyboardInterrupt,如果在代码中的某个地方出现了

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

这将“吃掉” KeyboardInterrupt 异常。


谢谢你的建议。我找到了一行代码可以解决一个无关的错误,但即使将其注释掉,主要的segfault问题仍然存在,而且更奇怪的是,发送kill -INT并不能触发它,但ctrl + c却能够。 - Belorn
3
我喜欢这个答案,因为它清楚地说明了 SIGINT 和 KeyboardInterrupt 之间的翻译发生在哪里。至于你的警告:出于这个原因,我总是建议使用 try: ... except Exception: ... 作为“常规”异常的捕获方式,以免意外捕获到像 KeyboardInterruptSystemExit 这样不寻常的异常,请参考 链接1链接2 - balu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接