父亲死后,Python:如何杀死子进程?

时间:2014-05-02 18:40:49

标签: python linux windows subprocess kill

子进程以

开头
subprocess.Popen(arg)

当父母异常终止时,有没有办法确保它被杀死?我需要这个在Windows和Linux上都可以工作。我知道这个solution for Linux

修改

如果使用不同的启动流程的方法存在解决方案,则可以放宽使用subprocess.Popen(arg)启动子流程的要求。

2 个答案:

答案 0 :(得分:25)

嘿,我昨天就是在研究这个问题!假设您无法改变子计划:

在Linux上,prctl(PR_SET_PDEATHSIG, ...)可能是唯一可靠的选择。 (如果绝对有必要杀死子进程,那么你可能想要将死亡信号设置为SIGKILL而不是SIGTERM;你链接的代码使用SIGTERM,但子进程可以选择忽略SIGTERM它想。)

在Windows上,最可靠的选项是使用Job object。这个想法是你创造了一个" Job" (一种用于进程的容器),然后将子进程放入Job中,并设置魔术选项,即当没有人拥有一个'句柄时#34;对于这个Job,然后杀死其中的进程"。默认情况下,唯一的'手柄'作业是父进程所持有的作业,当父进程终止时,操作系统将通过并关闭其所有句柄,然后注意这意味着没有打开的作业句柄。所以它按照要求杀死了孩子。 (如果您有多个子进程,则可以将它们全部分配给同一作业。)This answer具有使用win32api模块执行此操作的示例代码。该代码使用CreateProcess启动子代,而不是subprocess.Popen。原因是他们需要获得一个"流程处理"对于生成的子项,CreateProcess默认返回此项。如果您更愿意使用subprocess.Popen,那么这里是该答案代码的(未经测试)副本,该副本使用subprocess.PopenOpenProcess代替{{1} }}:

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来电之间死亡,这里的竞争条件很小,您可以决定是否要担心这一点。

使用作业对象的一个​​缺点是,当在Vista或Win7上运行时,如果您的程序是从Windows shell启动的(即通过单击图标),那么可能会already be a job object assigned并尝试创建新的作业对象将失败。 Win8修复此问题(通过允许嵌套作业对象),或者如果您的程序是从命令行运行的,那么它应该没问题。

如果你可以修改孩子(例如,就像使用OpenProcess时那样),那么最好的选择可能是以某种方式将父母的PID传递给孩子(例如作为命令行参数,或multiprocessing中的args=参数),然后:

在POSIX上:在偶尔调用multiprocessing.Process的子节点中生成一个线程,如果返回值与父节点传入的pid匹配,则调用os.getppid()。 (这种方法可以移植到所有Unix,包括OS X,而os._exit()技巧是特定于Linux的。)

在Windows上:在使用prctlOpenProcess的子项中生成一个帖子。使用ctypes的示例:

os.waitpid

这避免了我提到的作业对象的任何可能问题。

如果您真的非常确定,那么您可以将所有这些解决方案结合起来。

希望有所帮助!

答案 1 :(得分:8)

Popen对象提供了terminate和kill方法。

https://docs.python.org/2/library/subprocess.html#subprocess.Popen.terminate

这些信号会为您发送SIGTERM和SIGKILL信号。 你可以做类似下面的事情:

from subprocess import Popen

p = None
try:
    p = Popen(arg)
    # some code here
except Exception as ex:
    print 'Parent program has exited with the below error:\n{0}'.format(ex)
    if p:
        p.terminate()

更新:

你是对的 - 上面的代码不能防止硬崩溃或有人杀死你的进程。在这种情况下,您可以尝试将子进程包装在类中,并使用轮询模型来监视父进程。 请注意psutil是非标准的。

import os
import psutil

from multiprocessing import Process
from time import sleep


class MyProcessAbstraction(object):
    def __init__(self, parent_pid, command):
        """
        @type parent_pid: int
        @type command: str
        """
        self._child = None
        self._cmd = command
        self._parent = psutil.Process(pid=parent_pid)

    def run_child(self):
        """
        Start a child process by running self._cmd. 
        Wait until the parent process (self._parent) has died, then kill the 
        child.
        """
        print '---- Running command: "%s" ----' % self._cmd
        self._child = psutil.Popen(self._cmd)
        try:
            while self._parent.status == psutil.STATUS_RUNNING:
                sleep(1)
        except psutil.NoSuchProcess:
            pass
        finally:
            print '---- Terminating child PID %s ----' % self._child.pid
            self._child.terminate()


if __name__ == "__main__":
    parent = os.getpid()
    child = MyProcessAbstraction(parent, 'ping -t localhost')
    child_proc = Process(target=child.run_child)
    child_proc.daemon = True
    child_proc.start()

    print '---- Try killing PID: %s ----' % parent
    while True:
        sleep(1)

在这个例子中,我运行将永远运行的'ping -t localhost'b / c。如果您终止父进程,子进程(ping命令)也将被终止。