如何解释在信号处理程序中打印导致的重入运行时错误?

hsf*_*xjy 16 python signals reentrancy race-condition python-3.x

码:

# 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)
Run Code Online (Sandbox Code Playgroud)
# 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

我正在使用Python 3.6.2.

J_H*_*J_H 1

您可能更愿意禁止向子进程传递 SIGINT,这样就不会出现竞争,也许可以将其放入不同的进程组中,或者让它忽略信号。那么只有来自父级的 SIGTERM 才重要。

要显示中断的位置,请使用以下命令:

    sig_num, frame = args
    print(dis.dis(frame.f_code.co_code))
    print(frame.f_lasti)
Run Code Online (Sandbox Code Playgroud)

左边距中的字节码偏移量对应于最后执行的指令的偏移量。

其他感兴趣的项目包括 frame.f_linenoframe.f_code.co_filenameframe.f_code.co_names

这个问题在 python 3.7.3 中变得毫无意义,不再表现出该症状。

  • 除了我在记录时在 celery 中的 Python 3.9 中看到它 (2认同)