哪些函数在Python中可重复用于信号库处理

时间:2011-01-05 13:38:02

标签: python

在我的脑海中讨论了Signal handlers and logging in Python哪些函数在Python中可重入的问题。

提到signal library

  

虽然Python信号处理程序是   就异步而言是异步调用的   对Python用户而言,他们可以   只发生在原子之间   Python的说明   翻译。这意味着信号   在长时间的计算中到达   纯粹用C语言实现(如   正则表达式匹配大   文本的主体可能会延迟   任意的时间。

logging library

指出了重新入口不典型
  

如果您正在实现异步   信号处理程序使用信号   模块,您可能无法使用   从这些处理程序中记录。   这是因为锁实现   在线程模块中并不总是如此   重入,所以不能被调用   来自这些信号处理人员。

我有点困惑,因为信号库将GIL(全局解释器锁)称为“..原子指令之间......”。在这种情况下,一旦GIL离开/ 解锁,信号就会被推迟并执行。一种信号队列。

这是有道理的,但延迟信号处理程序调用的函数是否可重入并不重要,因为它们不会在 real POSIX信号处理程序中被调用“re-entrant” “-limitation:

  

仅定义POSIX C列表   函数被声明为可重入   并且可以在POSIX中调用   信号处理器。 IEEE Std 1003.1列出   您找到118个可重入的UNIX函数   在https://www.opengroup.org/(登录   必需的)。

2 个答案:

答案 0 :(得分:2)

我认为使日志记录模块不可重入的原因是它使用threading.Lock(而不是RLock)来同步记录到相同处理程序的多个线程(因此消息不会得到交织)。

这意味着如果已获取锁定的日志记录调用被信号处理程序中断并且信号处理程序尝试记录它将永远死锁,等待先前的acquire被释放。

顺便说一句,这些锁与GIL无关,它们是“用户创建”的锁,以便它采用某种方式,GIL是解释器使用的锁(实现细节)。

答案 1 :(得分:0)

某些人可能更喜欢使用pselect()/ ppoll()/ Linux signalfd收听信号。但是,pselect()/ ppoll()在python select模块中不可用。

一些事件循环声称支持信号。如果您考虑使用事件循环,则可以查看其文档。例如:https://docs.python.org/3/library/asyncio-eventloop.html#unix-signals

某些事件循环,例如内置的asyncio模块,当前使用signal.set_wakeup_fd()实现。这是越野车。请参阅下面的标题。

否则,请回答您的问题字母:os.write()。然后,您可以使用self-pipe trick

import os
import fcntl
import errno

(sigint_write_pipe, sigint_read_pipe) = os.pipe()
fcntl.fcntl(sigint_write_pipe, fcntl.SET_FL,
            os.O_NONBLOCK | os.O_CLOEXEC)

def handle_sigint():
    try:
        os.write(sigint_write_pipe, b'\0')
    except IOError as e:
        if e.errno = errno.EWOULDBLOCK:
            pass  # pipe is already full. no problem.
        else:
            raise

signal.signal(signal.SIGINT, handle_sigint)

# Now listen to sigint_read_pipe, using your preferred
# select() / poll() / event loop etc
...

有几种方法可以实现异步信号安全性。 os.write()是最有可能满足以下第一个条件的功能:

  1. 纯在C中实现的函数。因为python级信号处理程序不会中断C函数。
  2. 不访问可变全局变量的Python函数。
  3. 访问可变全局变量的Python函数,这些变量的“不变式”永远不会被临时破坏。例如。没有不变式的单个变量。

在许多情况下,异步信号安全将被视为私有实现细节,而不是对未来行为的公共保证。即使在C语言中也是如此。官方python文档并未提及您的关注。因此,我们不应该在这里信任python文档。

signal.set_wakeup_fd()

如果您仍然相信python文档,那么有一个second option是“常用”的。将管道传递到signal.set_wakeup_fd(),并轮询管道的另一端。这使您可以检测程序何时被信号中断。它不允许您检测信号是什么,因为可能存在多个信号,并且它们可能溢出管道缓冲区并丢失。