如何阻止SIGINT传递给Python子进程?

16 投票
3 回答
6073 浏览
提问于 2025-04-16 01:11

我的Python脚本使用信号模块来拦截SIGINT信号,这样可以防止程序提前退出。但是,当我用Popen打开一个子进程时,这个信号会传递给子进程。有没有办法阻止这个信号传递给子进程,这样当用户按下ctrl-c时,子进程也不会提前退出呢?

3 个回答

0

对于使用Python 2的代码来说,subprocess模块是有问题的。

正确的做法是

import subprocess32 as subprocess

可以查看 subprocess32

这是一个将Python 3的subprocess模块移植到Python 2上的版本。这个代码在Windows或其他非POSIX平台上没有经过测试。

16

当你启动一个子进程时,信号处理器会被继承。所以如果你用信号模块来忽略SIGINT(也就是中断信号,signal.signal(signal.SIGINT, signal.SIG_IGN)),那么你的子进程也会自动忽略这个信号。

不过,有两个重要的注意事项:

  • 你必须在创建子进程之前设置忽略信号的处理器
  • 自定义的信号处理器会被重置为默认处理器,因为子进程无法访问处理器的代码来执行它。

所以如果你需要自定义处理SIGINT,而不是简单地忽略它,你可能想在创建子进程时暂时忽略SIGINT,然后再(重新)设置你的自定义信号处理器。

如果你想捕捉SIGINT并设置一个标志,以便在安全的时刻退出,而不是立即退出,记住当你到达那个安全点时,你的代码需要手动清理它的子进程,因为你的子进程和它启动的任何进程都会忽略SIGINT。

2

你可以使用 tty 模块来重新分配 ctrl-c 的功能,这个模块让你可以控制信号的分配。不过要注意,如果你在修改后没有把它们恢复到原来的样子,这些修改会一直持续到整个终端会话结束,即使程序已经退出。

下面是一个简单的代码示例,帮助你入门。这个代码会保存你之前的 tty 设置,把 ctrl-c 的功能改成 ctrl-x,然后在退出时恢复你之前的 tty 设置。

import sys
import tty

# Back up previous tty settings
stdin_fileno = sys.stdin.fileno()
old_ttyattr = tty.tcgetattr(stdin_fileno)

try:
    print 'Reassigning ctrl-c to ctrl-x'

    # Enter raw mode on local tty
    tty.setraw(stdin_fileno)
    raw_ta = tty.tcgetattr(stdin_fileno)
    raw_ta[tty.LFLAG] |= tty.ISIG
    raw_ta[tty.OFLAG] |= tty.OPOST | tty.ONLCR

    # ^X is the new ^C, set this to 0 to disable it entirely
    raw_ta[tty.CC][tty.VINTR] = '\x18'  

    # Set raw tty as active tty
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, raw_ta)

    # Dummy program loop
    import time
    for _ in range(5):
        print 'doing stuff'
        time.sleep(1)

finally:
    print 'Resetting ctrl-c'
    # Restore previous tty no matter what
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, old_ttyattr)

撰写回答