Python: как предотвратить подпроцессы от приема CTRL-C/Control-C/SIGINT
В настоящее время я работаю над оболочкой для выделенного сервера, работающего в оболочке. Обертка запускает процесс сервера через подпроцесс и наблюдает и реагирует на его вывод.
На выделенном сервере должна быть явно задана команда для изящества. Таким образом, CTRL-C не должен доходить до серверного процесса.
Если я захватил исключение KeyboardInterrupt или перезаписал обработчик SIGINT в python, серверный процесс все равно получит CTRL-C и немедленно остановится.
Итак, мой вопрос:
Как предотвратить подпроцессы от приема CTRL-C/Control-C/SIGINT?
Ответы
Ответ 1
Кто-то из #python IRC-канала (Freenode) помог мне, указав параметр preexec_fn subprocess.Popen(...):
Если preexec_fn установлен на вызываемый объекта, этот объект будет вызываться в дочерний процесс непосредственно перед выполняется ребенок. (Только для Unix)
Таким образом, следующий код решает проблему (только для UNIX):
import subprocess
import signal
def preexec_function():
# Ignore the SIGINT signal by setting the handler to the standard
# signal handler SIG_IGN.
signal.signal(signal.SIGINT, signal.SIG_IGN)
my_process = subprocess.Popen(
["my_executable"],
preexec_fn = preexec_function
)
Примечание: Сигналу на самом деле не мешает достичь подпроцесса. Вместо этого preexec_fn выше перезаписывает обработчик по умолчанию сигнала, так что сигнал игнорируется. Таким образом, это решение может не работать, если подпроцесс снова перезаписывает обработчик SIGINT.
Другое примечание: Это решение работает для всех видов подпроцессов, то есть оно не ограничено также и субпроцессами, написанными на Python. Например, выделенный сервер, на котором я пишу свою обертку, на самом деле написан на Java.
Ответ 2
Сочетание некоторых других ответов, которые будут делать трюк - ни один сигнал, отправленный в основное приложение, не будет перенаправлен на подпроцесс.
import os
from subprocess import Popen
def preexec(): # Don't forward signals.
os.setpgrp()
Popen('whatever', preexec_fn = preexec)
Ответ 3
Попробуйте отключить SIGINT перед тем, как развернуть подпроцесс (reset по умолчанию).
Если это не сработает, вам нужно будет прочитать контроль заданий и узнать, как поставить процесс самостоятельно фоновая группа процессов, так что ^C даже не приводит к тому, что ядро посылает сигнал на него в первую очередь. (Может быть невозможно в Python без написания C-помощников.)
См. также этот старый вопрос.
Ответ 4
вы можете сделать что-то подобное, чтобы заставить его работать в windows и unix:
import subprocess
import sys
def pre_exec():
# To ignore CTRL+C signal in the new process
signal.signal(signal.SIGINT, signal.SIG_IGN)
if sys.platform.startswith('win'):
#https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
#CREATE_NEW_PROCESS_GROUP=0x00000200 -> If this flag is specified, CTRL+C signals will be disabled
my_sub_process=subprocess.Popen(["executable"], creationflags=0x00000200)
else:
my_sub_process=subprocess.Popen(["executable"], preexec_fn = pre_exec)