Код:
# 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>'>
случайном порядке. Из документации я узнаю, что обработчики сигналов вызываются асинхронно, и в этом случае два сигнала SIGINT (действие Ctrl+C
) и SIGTERM (p.terminate()
) отправляются почти одновременно, вызывая состояние гонки.
Однако из этого поста я узнаю, что signal
модуль не выполняет обработчик сигнала внутри низкоуровневого (C) обработчика. Вместо этого он устанавливает флаг, и интерпретатор проверяет флаг между инструкциями байт-кода, а затем вызывает обработчик сигнала python. Другими словами, хотя обработчики сигналов могут испортить поток управления в главном потоке, инструкция байт-кода всегда атомарна.
Это, кажется, противоречит результату моего примера программы. Насколько мне известно, print
и неявный _io.BufferedWriter
реализованы на чистом C, и, следовательно, вызов функции print
должен потреблять только одну инструкцию байт-кода (CALL_FUNCTION
). Я запутался: как одна функция может быть повторно введена в одной непрерывной инструкции в одном потоке?
Я использую Python 3.6.2.