Python: как запретить подпроцессам получать CTRL-C/Control-C/SIGINT

В настоящее время я работаю над оболочкой для выделенного сервера, работающего в оболочке. Оболочка порождает серверный процесс через подпроцесс, наблюдает и реагирует на его выходные данные.

Выделенному серверу должна быть явно дана команда для корректного завершения работы. Таким образом, CTRL-C не должен достигать серверного процесса.

Если я перехватываю исключение KeyboardInterrupt или перезаписываю обработчик SIGINT в python, серверный процесс все равно получает CTRL-C и немедленно останавливается.

Итак, мой вопрос: как предотвратить получение подпроцессами CTRL-C/Control-C/SIGINT?


person robert    schedule 18.02.2011    source источник
comment
Меня интересует обходной путь, было бы неплохо, если бы вы его опубликовали!   -  person Gleno    schedule 18.02.2011
comment
Какую операционную систему ты используешь?   -  person ʇsәɹoɈ    schedule 19.02.2011
comment
Выделенный сервер работает в системе Linux (Debian).   -  person robert    schedule 19.02.2011
comment
Кто-нибудь знает решение для Windows?   -  person Michael Herrmann    schedule 15.06.2013


Ответы (5)


Кто-то из IRC-канала #python (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.

person robert    schedule 19.02.2011

Объединение некоторых других ответов, которые помогут - никакой сигнал, отправленный в основное приложение, не будет перенаправлен в подпроцесс.

import os
from subprocess import Popen

def preexec(): # Don't forward signals.
    os.setpgrp()

Popen('whatever', preexec_fn = preexec)
person Marek Sapota    schedule 27.03.2011
comment
+1 Функция preexec не нужна, Popen(args, preexec_nf=os.setpgrp) тоже классная. - person Peter Sutton; 07.11.2014
comment
preexec_nf? Лучше попробуй Popen(args, preexec_fn=os.setpgrp) ;-) - person Marcus; 24.01.2017

вы можете сделать что-то подобное, чтобы заставить его работать в 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)
person jpastell    schedule 26.08.2016
comment
Когда я использую ваш creationflags, основной процесс невозможно убить с помощью Ctrl + C в Windows. Идеи? - person Fuzzyma; 22.11.2016

Попробуйте настроить SIGINT так, чтобы он игнорировался перед запуском подпроцесса (после сброса его поведения по умолчанию).

Если это не сработает, вам нужно прочитать управление заданиями и узнайте, как поместить процесс в свою собственную группу фоновых процессов, чтобы ^C даже не заставляло ядро ​​посылать ему сигнал. (Может быть невозможно в Python без написания помощников C.)

См. также этот старый вопрос.

person zwol    schedule 18.02.2011
comment
Я пробовал это, но это не сработало (игнорирование SIGINT до создания подпроцесса, т.е. не управление заданием). На данный момент у меня есть обходной путь, который я представлю позже сегодня или завтра. - person robert; 18.02.2011

После часа различных попыток это работает для меня:

process = subprocess.Popen(["someprocess"], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP)

Это решение для окон.

person Danil Shaykhutdinov    schedule 01.08.2020