【问题标题】:Python - Trap all signalsPython - 捕获所有信号
【发布时间】:2011-01-10 01:42:17
【问题描述】:

在 Linux 下的 python 2.6 中,我可以使用以下方法来处理一个 TERM 信号:

import signal
def handleSigTERM():
    shutdown()
signal.signal(signal.SIGTERM, handleSigTERM)    

除了一次设置一个信号之外,还有什么方法可以为进程接收到的所有信号设置一个处理程序?

【问题讨论】:

  • 我相信答案是“否”,甚至无法捕获某些信号 (SIGKILL)。

标签: python linux signal-handling


【解决方案1】:

您可以循环访问信号模块中的信号并进行设置。

for i in [x for x in dir(signal) if x.startswith("SIG")]:
  try:
    signum = getattr(signal,i)
    signal.signal(signum,sighandler)
  except (OSError, RuntimeError) as m: #OSError for Python3, RuntimeError for 2
    print ("Skipping {}".format(i))

【讨论】:

  • 不客气。感谢您修复程序中的错误。 :)
  • 那应该是 RuntimeError,而不是 RunTimeError。无法编辑,因为只有一个字符更改。
  • 您的列表理解将包括SIG_IGN,这是一个动作而不是一个信号。由于此操作“值”对应于1,即SIGHUP 的值,因此您将设置此信号两次。对于这种无关紧要的特定情况,但是此代码可能会因其他类似目的而崩溃。
  • 此答案不适用于 Python 3,因为尝试设置不可阻止的信号,例如 SIGKILL,例如现在抛出 OSError 而不是 RuntimeError
  • @BryceGuinta 已修复。这现在应该适用于 Python 2 和 3。
【解决方案2】:

如果您想摆脱尝试,只需忽略无法捕获的信号即可。

#!/usr/bin/env python
# https://stackoverflow.com/questions/2148888/python-trap-all-signals
import os
import sys
import time
import signal

SIGNALS_TO_NAMES_DICT = dict((getattr(signal, n), n) \
    for n in dir(signal) if n.startswith('SIG') and '_' not in n )


def receive_signal(signum, stack):
    if signum in [1,2,3,15]:
        print 'Caught signal %s (%s), exiting.' % (SIGNALS_TO_NAMES_DICT[signum], str(signum))
        sys.exit()
    else:
        print 'Caught signal %s (%s), ignoring.' % (SIGNALS_TO_NAMES_DICT[signum], str(signum))

def main():
    uncatchable = ['SIG_DFL','SIGSTOP','SIGKILL']
    for i in [x for x in dir(signal) if x.startswith("SIG")]:
        if not i in uncatchable:
            signum = getattr(signal,i)
            signal.signal(signum,receive_signal)
    print('My PID: %s' % os.getpid())
    while True:
        time.sleep(1)
main()

【讨论】:

  • 代码没有正确缩进,但我无法编辑它,因为不要让我做那么小的更改。所有的 for 都应该减少一级缩进。
【解决方案3】:

该代码在当前版本的 python 中不起作用。有许多以 SIG 开头的变量具有相同的值。例如,SIGHUP 和 SIG_UNBLOCK 都是 1。我能想到的获得实际信号列表的唯一方法就是自己制作。

from signal import *    
signals = {
        SIGABRT: 'SIGABRT',
        SIGALRM: 'SIGALRM',
        SIGBUS: 'SIGBUS',
        SIGCHLD: 'SIGCHLD',
        SIGCONT: 'SIGCONT',
        SIGFPE: 'SIGFPE',
        SIGHUP: 'SIGHUP',
        SIGILL: 'SIGILL',
        SIGINT: 'SIGINT',
        SIGPIPE: 'SIGPIPE',
        SIGPOLL: 'SIGPOLL',
        SIGPROF: 'SIGPROF',
        SIGQUIT: 'SIGQUIT',
        SIGSEGV: 'SIGSEGV',
        SIGSYS: 'SIGSYS',
        SIGTERM: 'SIGTERM',
        SIGTRAP: 'SIGTRAP',
        SIGTSTP: 'SIGTSTP',
        SIGTTIN: 'SIGTTIN',
        SIGTTOU: 'SIGTTOU',
        SIGURG: 'SIGURG',
        SIGUSR1: 'SIGUSR1',
        SIGUSR2: 'SIGUSR2',
        SIGVTALRM: 'SIGVTALRM',
        SIGXCPU: 'SIGXCPU',
        SIGXFSZ: 'SIGXFSZ',
        }

for num in signals:
    signal(num, h)

【讨论】:

  • 没必要。所有信号仍然以SIG 开头,但您需要添加仔细检查以确保忽略SIG_
【解决方案4】:

从 Python 3.5 开始,信号常量为 defined as an enum,实现了更好的方法:

import signal

catchable_sigs = set(signal.Signals) - {signal.SIGKILL, signal.SIGSTOP}
for sig in catchable_sigs:
    signal.signal(sig, print)  # Substitute handler of choice for `print`

【讨论】:

  • 投反对票的人可以发表评论吗?这个答案有问题吗?
  • 这其实挺有用的。谢谢!您能否确认这是否适用于 Windows?
  • 我不知道信号在 Windows 上是如何工作的,但看起来核心功能可用:docs.python.org/3/library/signal.html#signal.signal 请注意,尽管信号函数只接受某些值,但它看起来像 Signals 枚举是用平台的适当信号构造的:github.com/python/cpython/blob/master/Lib/signal.py#L8-L13 所以我猜这种方法会起作用,可能需要一些小的调整。但同样,我不确定信号是如何工作的,甚至不确定它们在 Windows 上的含义,所以它可能根本不起作用。
  • 像魅力一样工作
【解决方案5】:

这是一种 2/3 兼容的方式,它不像其他方式那样有那么多陷阱:

from itertools import count
import signal

def set_all_signal_signals(handler):
    """Set all signals to a particular handler."""
    for signalnum in count(1):
        try:
            signal.signal(signalnum, handler)
            print("set {}".format(signalnum))
        except (OSError, RuntimeError):
            # Invalid argument such as signals that can't be blocked
            pass
        except ValueError:
            # Signal out of range
            break

由于 signalnum 只是一个数字,因此迭代 1 到超出范围,将信号设置为特定句柄。

【讨论】:

    【解决方案6】:

    适用于 Windows 10 和 Python 3.7:

    import signal
    import time
    
    def sighandler(signal,frame):
        print("signal",sig,frame)
        return
    
    catchable_sigs = set(signal.Signals)
    for sig in catchable_sigs:
        try:
            signal.signal(sig, sighandler)
            print("Setting ",sig)
            print ("value {}".format(sig))
        except (ValueError, OSError, RuntimeError) as m:
            print("Skipping ",sig)
            print ("Value {}".format(sig))
    
    
    # press some keys or issue kill
    x = 0
    while x < 5:
        time.sleep(4)
        x += 1
    

    结果:

    Skipping  Signals.CTRL_C_EVENT
    Value 0
    Skipping  Signals.CTRL_BREAK_EVENT
    Value 1
    Setting  Signals.SIGINT
    value 2
    Setting  Signals.SIGILL
    value 4
    Setting  Signals.SIGFPE
    value 8
    Setting  Signals.SIGSEGV
    value 11
    Setting  Signals.SIGTERM
    value 15
    Setting  Signals.SIGBREAK
    value 21
    Setting  Signals.SIGABRT
    value 22
    

    【讨论】:

      【解决方案7】:

      对于 Python 3:

      for sig in signal.Signals:
          try:
              signal.signal(sig, sighandler)
          except OSError:
              print('Skipping', sig)
      

      【讨论】:

        【解决方案8】:

        在 Python3.8 中,我们有了一个新函数 signal.valid_signals() https://docs.python.org/3/library/signal.html#signal.valid_signals

        import signal
        for sig in signal.valid_signals():
            print(f"{sig:2d}",sig)
        

        【讨论】:

          猜你喜欢
          • 2020-12-08
          • 1970-01-01
          • 1970-01-01
          • 2014-06-01
          • 1970-01-01
          • 2021-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多