gpt4 book ai didi

python - 如何解释在信号处理程序中打印导致的可重入 RuntimeError?

转载 作者:太空狗 更新时间:2023-10-29 17:08:49 27 4
gpt4 key购买 nike

代码:

# callee.py
import signal
import sys
import time


def int_handler(*args):
for i in range(10):
print('INTERRUPT', args)
sys.exit()


if __name__ == '__main__':

signal.signal(signal.SIGINT, int_handler)
signal.signal(signal.SIGTERM, int_handler)
while 1:
time.sleep(1)

# caller.py
import subprocess
import sys


def wait_and_communicate(p):
out, err = p.communicate(timeout=1)
print('========out==========')
print(out.decode() if out else '')
print('========err==========')
print(err.decode() if err else '')
print('=====================')


if __name__ == '__main__':

p = subprocess.Popen(
['/usr/local/bin/python3', 'callee.py'],
stdout=sys.stdout,
stderr=subprocess.PIPE,
)
while 1:
try:
wait_and_communicate(p)
except KeyboardInterrupt:
p.terminate()
wait_and_communicate(p)
break
except subprocess.TimeoutExpired:
continue

只需执行 caller.py然后按 Ctrl+C ,程序将提高 RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>'>随机的。来自documentation我了解到信号处理程序是异步调用的,在这种情况下,两个信号 SIGINT( Ctrl+C action) 和 SIGTERM( p.terminate() ) 几乎同时发送,导致竞争条件。

但是,从这个post我了解到 signal模块不在低级 (C) 处理程序中执行信号处理程序。相反,它设置一个标志,解释器检查字节码指令之间的标志,然后调用 python 信号处理程序。换句话说,虽然信号处理程序可能会扰乱主线程中的控制流,但字节码指令始终是原子的。

这似乎与我的示例程序的结果相矛盾。就我而言,print和隐含的 _io.BufferedWriter都是用纯 C 实现的,因此调用 print函数应该只使用一个字节码指令(CALL_FUNCTION)。我很困惑:在一个线程上的一个不间断指令中,一个函数怎么可以重入?

我正在使用 Python 3.6.2。

最佳答案

信号在操作码之间进行处理(参见 eval_frame_handle_pending()在 python 的操作码处理器循环中),但不限于此。 print 就是一个完美的例子。它是基于_io_BufferedWriter_write_impl()实现的,其结构如下

ENTER_BUFFERED() => it locks buffer

PyErr_CheckSignals() => it invoke signal handler

LEAVE_BUFFERED() => it unlocks buffer

通过调用 PyErr_CheckSignals(),它会调用另一个信号处理程序,在本例中它有另一个 print。第二个 print 再次运行 ENTER_BUFFERED(),因为缓冲区已经被第一个信号处理程序中的前一个 print 锁定,所以 可重入 异常被抛出,如下片段所示。

    // snippet of ENTER_BUFFERED
static int
_enter_buffered_busy(buffered *self)
{
int relax_locking;
PyLockStatus st;
if (self->owner == PyThread_get_thread_ident()) {
PyErr_Format(PyExc_RuntimeError,
"reentrant call inside %R", self);
return 0;
}
}

#define ENTER_BUFFERED(self) \
( (PyThread_acquire_lock(self->lock, 0) ? \
1 : _enter_buffered_busy(self)) \
&& (self->owner = PyThread_get_thread_ident(), 1) )


附言

可重入函数 来自 Advanced Programming in the Unix Environment .

单一 UNIX 规范指定了保证可以从信号处理程序中安全调用的函数。这些函数是可重入的,被称为异步信号安全的。大多数不可重入的函数是因为

  1. 众所周知,他们使用静态数据结构,
  2. 他们调用 malloc 或 free
  3. 它们是标准 I/O 库的一部分。大多数标准 I/O 库的实现都以不可重入的方式使用全局数据结构。 Python 中的print 属于此类。

关于python - 如何解释在信号处理程序中打印导致的可重入 RuntimeError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45680378/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com