在我的脑海中讨论了Signal handlers and logging in Python哪些函数在Python中可重入的问题。
指出了重新入口不典型虽然Python信号处理程序是 就异步而言是异步调用的 对Python用户而言,他们可以 只发生在原子之间 Python的说明 翻译。这意味着信号 在长时间的计算中到达 纯粹用C语言实现(如 正则表达式匹配大 文本的主体可能会延迟 任意的时间。
如果您正在实现异步 信号处理程序使用信号 模块,您可能无法使用 从这些处理程序中记录。 这是因为锁实现 在线程模块中并不总是如此 重入,所以不能被调用 来自这些信号处理人员。
我有点困惑,因为信号库将GIL(全局解释器锁)称为“..原子指令之间......”。在这种情况下,一旦GIL离开/ 解锁,信号就会被推迟并执行。一种信号队列。
这是有道理的,但延迟信号处理程序调用的函数是否可重入并不重要,因为它们不会在 real POSIX信号处理程序中被调用“re-entrant” “-limitation:
仅定义POSIX C列表 函数被声明为可重入 并且可以在POSIX中调用 信号处理器。 IEEE Std 1003.1列出 您找到118个可重入的UNIX函数 在https://www.opengroup.org/(登录 必需的)。
答案 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()
是最有可能满足以下第一个条件的功能:
在许多情况下,异步信号安全将被视为私有实现细节,而不是对未来行为的公共保证。即使在C语言中也是如此。官方python文档并未提及您的关注。因此,我们不应该在这里信任python文档。
如果您仍然相信python文档,那么有一个second option是“常用”的。将管道传递到signal.set_wakeup_fd()
,并轮询管道的另一端。这使您可以检测程序何时被信号中断。它不允许您检测信号是什么,因为可能存在多个信号,并且它们可能溢出管道缓冲区并丢失。