signal– 为异步事件设置处理程序


这个模块提供了在Python中使用信号处理程序的机制

一般规则

signal.signal()函数允许定义自定义处理程序收到信号时被执行。安装了少量默认处理程序SIGPIPE被忽略(因此管道和套接字上的写入错误可以报告为普通的Python异常)和SIGINT转换为KeyboardInterrupt异常.

特定信号的处理程序一旦设置,一直保持安装,直到它被明确重置(Python模拟BSD样式接口而不管底层实现),除了SIGCHLD的处理程序,底层实现

执行Python信号处理程序

Python信号处理程序不会在低级(C)信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉虚拟机稍后执行相应的Python信号处理(例如在下一个字节码指令处)。这有后果:

  • 捕捉SIGFPESIGSEGV由C代码中的无效操作引起的。Python将从信号处理程序返回到C代码,这可能再次引发相同的信号,导致Python显然挂起。从Python 3.3开始,你可以使用faulthandler报告同步错误的模块.
  • 纯粹在C中实现的长时间运行的计算(例如对大量文本的正则表达式匹配)可以在任何时间内不间断地运行,而不管接收到任何信号。计算完成后将调用Pythonsignal处理程序.

信号和线程

Python信号处理程序总是在主Python线程中执行,即使信号是在另一个线程中接收的。这意味着信号不能用作线程间通信的手段。您可以使用threading模块中的同步原语来代替

除此之外,只允许主线程设置新的信号处理程序.

模块内容

更改版本3.5:信号(SIG *),处理程序(SIG_DFL, SIG_IGN)和sigmaskSIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK)下面列出的相关常量变为enums.getsignal(), pthread_sigmask(), sigpending()sigwait()函数返回人类可读enums.

signal模块中定义的变量是:

signal.SIG_DFL

这是两个标准信号处理选项之一;它只是简单地执行信号的默认功能。例如,在大多数系统中,SIGQUIT的默认操作是转储核心并退出,而SIGCHLD的默认操作是简单地忽略它.

signal.SIG_IGN

这是另一个标准信号处理程序,这将完全忽略givensignal.

SIG*

所有信号编号都是象征性定义的。例如,挂断信号定义为signal.SIGHUP;变量名与C程序中使用的名称相同,如<signal.h>中所示。’signal()‘的Unix手册页列出了现有的信号(在某些系统上这是signal(2),在其他系统上列出的是signal(7))。请注意,并非所有系统都定义了相同的信号名称集;只有系统定义的名称才由本模块定义.

signal.CTRL_C_EVENT

对应于 Ctrl + C 按键事件的信号。这个信号只能用于os.kill().

可用性:Windows.

新版本3.2.

signal.CTRL_BREAK_EVENT

对应的信号Ctrl + Break 按键事件。这个信号只能用于os.kill().

可用性:Windows.

新版本3.2.

signal.NSIG

超过最高信号的数量。

signal.ITIMER_REAL

实时递减间隔计时器,并发送SIGALRM uponexpiration.

signal.ITIMER_VIRTUAL

仅在进程执行时递减间隔计时器,并在到期时递送SIGVTALRM

signal.ITIMER_PROF

在进程执行时以及系统代表进程执行时,递减间隔计时器。与ITIMER_VIRTUAL结合使用,此计时器通常用于分析应用程序在用户和内核空间中花费的时间。SIGPROF在到期时交付.

signal.SIG_BLOCK

的可能值how参数pthread_sigmask()表示要阻挡信号.

3.3版本中的新功能

signal.SIG_UNBLOCK

的可能值how参数pthread_sigmask()指示信号要解锁.

新版本3.3.

signal.SIG_SETMASK

how参数的可能值pthread_sigmask()表示信号掩码是将被替代。

3.3版本中的新功能

signal模块定义了一个例外:

exception signal.ItimerError

举起来表示来自底层的错误setitimer()要么getitimer()实现。如果将无效间隔计时器或负时间传递给setitimer()这个错误是OSError.

版本3.3中的新功能:此错误曾经是IOError,这是OSError.

的分析signal模块定义了以下功能:

signal.alarmtime

如果time非零,此函数请求SIGALRM信号发送到time秒。任何先前安排的警报都被取消(任何时候都只能安排一个警报)。返回的值是之前任何先前设置的警报发出之前的秒数。如果time为零,没有安排警报,任何预定的警报都被取消。如果返回值为零,则当前没有安排警报。(见Unix手册页alarm(2).)

AvailabilityUnix .

signal.getsignalsignalnum

返回信号signalnum的当前信号处理程序。返回的值可以是可调用的Python对象,也可以是特殊值之一signal.SIG_IGN, signal.SIG_DFLNone。这里,signal.SIG_IGN表示信号先前已被忽略,signal.SIG_DFL表示处理信号的默认方式以前是在使用中,并且None意味着以前的信号处理程序没有从Python安装.

signal.pause ( )

导致进程进入睡眠状态,直到收到信号为止;然后将调用适当的处理程序。什么都不返回不在Windows上。(参见Unix手册页signal(2).)

参见sigwait(), sigwaitinfo(), sigtimedwait()sigpending().

signal.pthread_killthread_id, signalnum

发送信号signalnum到线程thread_id,同一进程中的另一个线程作为调用者。目标线程可以执行任何代码(Python或不)。但是,如果目标线程正在执行Pythoninterpreter,那么Python信号处理程序将是由主线程执行。因此,向特定Python线程发送信号的唯一方法是强制正在运行的系统调用失败InterruptedError.

使用threading.get_ident()ident的属性threading.Thread对象得到适合的值thread_id.

如果signalnum为0,则不发送信号,但仍然执行错误检查;这可以用来检查目标线程是否仍在运行.

Availability:Unix(参见手册页pthread_kill(3)进一步查询).

另请参阅os.kill().

3.3版本中的新内容.

signal.pthread_sigmask (how, mask)

获取和/或更改调用线程的信号掩码。信号掩码是当前为呼叫者阻止传送的信号集。将旧信号掩码作为一组信号返回.

呼叫的行为取决于how的值如下所述.

  • SIG_BLOCK:阻塞信号的集合是当前集合和mask论证的结合.
  • SIG_UNBLOCKmask中的信号被删除来自当前的阻塞信号。允许尝试解锁未被阻挡的信号.
  • SIG_SETMASK:阻塞信号设置为maskargument。

mask是一组信号编号(例如{signal.SIGINT,signal.SIGTERM})。使用range(1, signal.NSIG)作为一个完整的面具,包括所有信号.

例如,signal.pthread_sigmask(signal.SIG_BLOCK, [])读取调用线程的信号掩码.

可用性:Unix。见手册页sigprocmask(3)pthread_sigmask(3)更多信息.

参见pause(), sigpending()sigwait().

3.3版本中的新内容.

signal.setitimer (which, seconds, interval=0.0)

设定signal.ITIMER_REAL,signal.ITIMER_VIRTUAL指定的给定间隔计时器(signal.ITIMER_PROFwhich之一)在seconds(浮动被接受,与alarm()不同)之后每个interval秒(如果interval非零)。which指定的间隔定时器可以通过将seconds设置为零来清除.

当间隔定时器触发时,会向进程发送一个信号。发送的信号取决于正在使用的定时器; signal.ITIMER_REAL将传送SIGALRM,signal.ITIMER_VIRTUAL发送SIGVTALRM,和signal.ITIMER_PROF将送出SIGPROF.

旧值作为元组返回:(延迟,间隔).

尝试通过无效的间隔计时器将导致ItimerError.

可用性:Unix.

signal.getitimerwhich

返回由which.

可用性:Unix .

signal.set_wakeup_fdfd, *, warn_on_full_buffer=True

将唤醒文件描述符设置为fd。当接收到信号时,信号编号​​作为单个字节写入fd。这可以被库用来唤醒轮询或选择呼叫,允许信号被完全处理.

返回旧的唤醒fd(如果未启用文件描述符唤醒,则返回-1)。如果fd为-1,则禁用文件描述符唤醒。如果不是-1,fd必须是非阻塞的。在调用poll或者再次选择之前,由库来删除fd中的字节.

启用线程时,只能从主线程调用此函数;尝试从其他线程调用它将导致ValueError异常被引发.

有两种常用方法可以使用此功能。在这两种方法中,当信号到达时你使用fd唤醒,但是然后他们对它们的确定方式有所了解which信号或信号已经到来

在第一种方法中,我们从fd的缓冲区中读取数据,字节值为您提供信号编号。这很简单,但在一些情况下它可能会遇到一个问题:通常fd会有一定量的缓冲区空间,如果太多信号太快到达,那么缓冲区可能会变满,一些信号可能会出现问题。如果你使用这种方法,那么你应该设置warn_on_full_buffer=True当信号丢失时,至少会发出警告打印到stderr的信息.

在第二种方法中,我们使用唤醒fd only用于唤醒,并忽略实际的字节值。在这种情况下,我们关心的是fd的缓冲区是空的还是非空的;一个完整的缓冲区根本不表示存在问题。如果你使用这种方法,那么你应该设置warn_on_full_buffer=False因此,您的用户不会被虚假警告信息混淆.

更改版本3.5:在Windows上,该功能现在还支持套接字句柄.

更改版本3.7:添加warn_on_full_buffer参数

signal.siginterrupt// (signalnum, flag)

更改系统调用重启行为:如果flagFalse,当被信号中断时,系统调用将重新启动signalnum,otherwisesystem调用将被中断。什么都不回报

可用性:Unix(参见手册页siginterrupt(3)了解更多信息)。

请注意,使用signal()通过隐式调用siginterrupt()并使用给定信号的真值flag来重置启动行为以进行中断.

signal.signal (signalnum, handler)

将信号signalnum的处理程序设置为函数handler. handler可以是一个可调用的Python对象,它带有两个参数(见下文),或者一个特殊值signal.SIG_IGN要么 signal.SIG_DFL。将返回先前的信号处理程序(参见的描述)getsignal()以上)。(参见Unix手册页signal(2).)

当启用线程时,只能从主线程调用此函数;尝试从其他线程调用它将导致ValueError要引发的异常

用两个参数调用handler:信号编号和当前堆栈框架(None或框架对象;对于框架对象的描述,看到类型层次结构中的描述或者查看inspect模块).

在Windows上,signal()只能用SIGABRT,SIGFPE, SIGILL, SIGINT, SIGSEGV,SIGTERM, 要么 SIGBREAK。一个 ValueError将在任何其他情况下提出。请注意,并非所有系统都定义相同的信号名称集;如果信号名称没有定义为AttributeError模块级别常量SIG*// (

signal.sigpending,则会引发

检查待传递到呼叫线程的信号集(即,在阻塞时已经引发的信号)。返回这些待处理的信号.

Availability:Unix(参见手册页sigpending(2)了解更多信息).

参见pause(), pthread_sigmask()sigwait().

3.3版本中的新内容.

signal.sigwaitsigset

暂停执行调用线程,直到传递信号集中指定的一个信号sigset。该函数接受信号(将其从待处理的信号列表中删除),并返回信号编号.

Availability:Unix(参见手册页sigwait(3)进一步信息).

参见pause(), pthread_sigmask(), sigpending(),sigwaitinfo()sigtimedwait().

3.3版本中的新内容.

signal.sigwaitinfo (sigset)

暂停执行调用线程直到传递信号集sigset中指定的一个信号。该函数接受信号并将其从待处理的信号列表中删除。如果sigset中的一个信号已经为调用线程挂起,则该函数将立即返回有关该信号的信息。信号处理程序不会被传送信号。如果被InterruptedError中的信号中断,该函数会引发sigset.

返回值是表示siginfo_t结构中包含的数据的对象,即:si_signo, si_code,si_errno, si_pid, si_uid, si_status,si_band.

可用性:Unix(参见手册页sigwaitinfo(2)了解更多信息).

参见pause(), sigwait()sigtimedwait().

版本3.3中的新功能

更改版本3.5:如果被sigset中的信号中断而且信号处理程序没有引发异常,则重试该功能(参见 PEP 475 理由).

signal.sigtimedwaitsigset, timeout

sigwaitinfo()一样,但需要额外的timeout参数指定超时。如果timeout指定为0,则执行轮询。如果发生超时则返回None.

Availability:Unix(参见手册页sigtimedwait(2)进一步查询).

参见pause(), sigwait()sigwaitinfo().

版本3.3.

中的新版本在版本3.5中更改:现在使用重新计算的timeout重试该函数由于不在sigset中的信号而中断并且信号处理程序没有引发异常(参见 PEP 475 的理由).

示例

这是一个最小的示例程序。它使用alarm()函数来限制等待打开文件所花费的时间;如果文件是针对可能无法打开的串行设备,这通常会导致os.open()无限期地挂起来。解决方案是在打开文件之前设置5秒钟的警报;如果操作时间过长,将发送报警信号,并且处理程序引发异常.

import signal, osdef handler(signum, frame):    print("Signal handler called with signal", signum)    raise OSError("Couldn"t open device!")# Set the signal handler and a 5-second alarmsignal.signal(signal.SIGALRM, handler)signal.alarm(5)# This open() may hang indefinitelyfd = os.open("/dev/ttyS0", os.O_RDWR)signal.alarm(0)          # Disable the alarm

注意SIGPIPE

将程序的输出输送到head(1)这样的工具会导致a SIGPIPE当标准输出的接收器提前关闭时,将发送到您的进程的信号。这导致了一个例外BrokenPipeError: [Errno 32] Broken pipe。要处理这个案例,请将您的入口点包装为捕获此异常,如下所示:

import osimport sysdef main():    try:        # simulate large output (your code replaces this loop)        for x in range(10000):            print("y")        # flush output here to force SIGPIPE to be triggered        # while inside this try block.        sys.stdout.flush()    except BrokenPipeError:        # Python flushes standard streams on exit; redirect remaining output        # to devnull to avoid another BrokenPipeError at shutdown        devnull = os.open(os.devnull, os.O_WRONLY)        os.dup2(devnull, sys.stdout.fileno())        sys.exit(1)  # Python exits with error code 1 on EPIPEif __name__ == "__main__":    main()

为了避免SIGPIPE,请不要将SIG_DFL设置为BrokenPipeError。这样做会导致你的程序在程序仍在写入时中断任何套接字连接时意外退出.

评论被关闭。