Ответ 1
Хе-хе, я просто сам это выяснил вчера! Предполагая, что вы не можете изменить дочернюю программу:
В Linux prctl(PR_SET_PDEATHSIG, ...)
, вероятно, является единственным надежным выбором. (Если абсолютно необходимо, чтобы дочерний процесс был убит, вам может потребоваться установить сигнал смерти SIGKILL вместо SIGTERM, связанный с ним код использует SIGTERM, но у ребенка есть возможность игнорировать SIGTERM, если он захочет. )
В Windows наиболее надежными параметрами являются объект Job. Идея заключается в том, что вы создаете "Job" (своего рода контейнер для процессов), затем вы помещаете дочерний процесс в Job, и вы устанавливаете волшебную опцию, которая гласит: "когда никто не держит" дескриптор "для этого задания, а затем уничтожить процессы, которые в нем". По умолчанию единственным "дескриптором" задания является тот, который выполняется вашим родительским процессом, и когда родительский процесс умирает, ОС будет проходить и закрывать все свои дескрипторы, а затем замечает, что это означает, что нет открытых дескрипторов для работа. Поэтому он убивает ребенка, как просили. (Если у вас несколько дочерних процессов, вы можете назначить их всем на одно и то же задание.) В этом ответе есть пример кода для этого, используя модуль win32api
. Этот код использует CreateProcess
для запуска дочернего элемента вместо subprocess.Popen
. Причина в том, что им нужно получить "дескриптор процесса" для порожденного ребенка, а CreateProcess
возвращает это по умолчанию. Если вы предпочитаете использовать subprocess.Popen
, то здесь (непроверенная) копия кода из этого ответа использует subprocess.Popen
и OpenProcess
вместо CreateProcess
:
import subprocess
import win32api
import win32con
import win32job
hJob = win32job.CreateJobObject(None, "")
extended_info = win32job.QueryInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation)
extended_info['BasicLimitInformation']['LimitFlags'] = win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
win32job.SetInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation, extended_info)
child = subprocess.Popen(...)
# Convert process id to process handle:
perms = win32con.PROCESS_TERMINATE | win32con.PROCESS_SET_QUOTA
hProcess = win32api.OpenProcess(perms, False, child.pid)
win32job.AssignProcessToJobObject(hJob, hProcess)
Технически, здесь крошечное условие гонки, если ребенок умирает между вызовами Popen
и OpenProcess
, вы можете решить, хотите ли вы об этом беспокоиться.
Один недостаток использования объекта задания заключается в том, что при работе в Vista или Win7, если ваша программа запущена из оболочки Windows (то есть, щелкнув значок), тогда, вероятно, будет уже назначается объект задания, и попытка создания нового объекта задания завершится с ошибкой. Win8 исправляет это (разрешая вложение объектов задания), или если ваша программа запускается из командной строки, тогда это должно быть хорошо.
Если вы можете изменить ребенка (например, при использовании multiprocessing
), то, вероятно, лучшим вариантом является каким-либо образом передать родительский PID дочернему элементу (например, в качестве аргумента командной строки или в аргументе args=
до multiprocessing.Process
), а затем:
В POSIX: создайте поток в дочернем элементе, который иногда вызывает os.getppid()
, и если возвращаемое значение когда-либо перестает сопоставлять pid, переданное из родителя, тогда вызовите os._exit()
. (Этот подход переносим для всех Unix-ов, включая OS X, а трюк prctl
- для Linux.)
В Windows: создайте поток в дочернем элементе, который использует OpenProcess
и os.waitpid
. Пример использования ctypes:
from ctypes import WinDLL, WinError
from ctypes.wintypes import DWORD, BOOL, HANDLE
# Magic value from http://msdn.microsoft.com/en-us/library/ms684880.aspx
SYNCHRONIZE = 0x00100000
kernel32 = WinDLL("kernel32.dll")
kernel32.OpenProcess.argtypes = (DWORD, BOOL, DWORD)
kernel32.OpenProcess.restype = HANDLE
parent_handle = kernel32.OpenProcess(SYNCHRONIZE, False, parent_pid)
# Block until parent exits
os.waitpid(parent_handle, 0)
os._exit(0)
Это позволяет избежать любой из возможных проблем с объектами задания, о которых я говорил.
Если вы хотите быть действительно, действительно уверены, тогда вы можете объединить все эти решения.
Надеюсь, что это поможет!