重置Python SIGINT为默认信号处理器

27 投票
1 回答
16076 浏览
提问于 2025-04-18 01:43

版本信息:

  • 操作系统:Windows 7
  • Python版本:3.3.5

下面是一段我在测试的小代码。我的目的是在某段代码执行时忽略按下的CTRL-C,执行完后再恢复CTRL-C的正常功能。

import signal 
import time

try:
    # marker 1
    print('No signal handler modifications yet')
    print('Sleeping...')
    time.sleep(10)

    # marker 2
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    print('Now ignoring CTRL-C')
    print('Sleeping...')
    time.sleep(10)

    # marker 3
    print('Returning control to default signal handler')
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    print('Sleeping...')
    time.sleep(10)

except KeyboardInterrupt:
    print('Ha, you pressed CTRL-C!')

在测试过程中,我观察到以下几点:

  • 标记1标记2之间按下的CTRL-C会被异常处理程序处理(这是预期的结果)。
  • 标记2标记3之间按下的CTRL-C会被忽略(这也是预期的结果)。
  • 标记3之后按下的CTRL-C会被处理,但不会跳转到异常处理程序。相反,Python会立即终止

另外,请考虑以下内容:

>>>import signal

>>>signal.getsignal(signal.SIGINT)
<built-in function default_int_handler>

>>> signal.getsignal(signal.SIGINT) is signal.SIG_DFL
False

>>> signal.signal(signal.SIGINT, signal.SIG_DFL)
<built-in function default_int_handler>

>>> signal.getsignal(signal.SIGINT) is signal.SIG_DFL
True

所以最开始,当信号处理程序被视为默认信号处理程序时,它似乎与SIG_DFL定义的处理程序不同。

如果有人能对此提供一些解释,特别是在将信号处理程序恢复为SIG_DFL后,为什么异常处理程序会被忽略。

1 个回答

42

Python会安装自己的处理器,以便在你按下键盘中断(比如Ctrl+C)时抛出异常。如果你把信号设置为,这并不会恢复Python的处理器,而是会使用系统自带的“标准”处理器,这样会直接结束Python解释器的运行。

所以,你需要先保存原来的处理器,然后在完成操作后再把它恢复回来:

original_sigint_handler = signal.getsignal(signal.SIGINT)

# Then, later...
signal.signal(signal.SIGINT, original_sigint_handler)

正如kindall在评论中所说的,你可以把这个过程写成一个上下文管理器

from contextlib import contextmanager

@contextmanager
def sigint_ignored():
    original_sigint_handler = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    try:
        print('Now ignoring CTRL-C')
        yield
    except:
        raise  # Exception is dropped if we don't reraise it.
    finally:
        print('Returning control to default signal handler')
        signal.signal(signal.SIGINT, original_sigint_handler)

你可以这样使用它:

# marker 1
print('No signal handler modifications yet')
print('Sleeping...')
time.sleep(10)

# marker 2
with sigint_ignored():
    print('Sleeping...')
    time.sleep(10)

# marker 3
print('Sleeping...')
time.sleep(10)

撰写回答