确保子程序在退出Python程序时已经死了

pup*_*eno 59 python subprocess kill zombie-process

有没有办法确保所有创建的子进程在Python程序的退出时间都死了?通过子进程,我指的是使用subprocess.Popen()创建的.

如果没有,我应该迭代所有发出的杀戮然后杀死-9?什么更干净?

mon*_*kut 45

您可以使用atexit,并注册在程序退出时要运行的任何清理任务.

atexit.register(func [,*args [,**kargs]])

在清理过程中,您还可以实现自己的等待,并在发生所需的超时时将其终止.

>>> import atexit
>>> import sys
>>> import time
>>> 
>>> 
>>>
>>> def cleanup():
...     timeout_sec = 5
...     for p in all_processes: # list of your processes
...         p_sec = 0
...         for second in range(timeout_sec):
...             if p.poll() == None:
...                 time.sleep(1)
...                 p_sec += 1
...         if p_sec >= timeout_sec:
...             p.kill() # supported from python 2.6
...     print 'cleaned up!'
...
>>>
>>> atexit.register(cleanup)
>>>
>>> sys.exit()
cleaned up!
Run Code Online (Sandbox Code Playgroud)

- 如果此进程(父进程)被终止,则不会运行已注册的函数.

python> = 2.6不再需要以下windows方法

这是一种在Windows中杀死进程的方法.你的Popen对象有一个pid属性,所以你可以通过success = win_kill(p.pid)调用它(需要安装pywin32):

    def win_kill(pid):
        '''kill a process by specified PID in windows'''
        import win32api
        import win32con

        hProc = None
        try:
            hProc = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
            win32api.TerminateProcess(hProc, 0)
        except Exception:
            return False
        finally:
            if hProc != None:
                hProc.Close()

        return True
Run Code Online (Sandbox Code Playgroud)


ori*_*rip 33

在*nix上,也许使用进程组可以帮助你 - 你也可以捕获子进程产生的子进程.

if __name__ == "__main__":
  os.setpgrp() # create new process group, become its leader
  try:
    # some code
  finally:
    os.killpg(0, signal.SIGKILL) # kill all processes in my group
Run Code Online (Sandbox Code Playgroud)

另一个考虑因素是升级信号:从SIGTERM(默认信号kill)到SIGKILL(aka kill -9).在信号之间等待一会儿,让过程有机会在你之前干净地退出kill -9.


S.L*_*ott 14

subprocess.Popen.wait()是确保他们死亡的唯一方法.实际上,POSIX OS要求您等待您的孩子.许多*nix将创建一个"僵尸"过程:一个死孩子,父母没有等待.

如果孩子写得合理,它就会终止.孩子们常常从PIPE上读到.关闭输入是一个很大的暗示,孩子应该关闭购物和退出.

如果孩子有虫子并且没有终止,你可能不得不杀死它.你应该修复这个bug.

如果孩子是"永远服务"循环,并且不是为了终止,你应该杀死它或者提供一些输入或消息来强制它终止.


编辑.

在标准操作系统中,你有os.kill( PID, 9 ).Kill -9很苛刻,BTW.如果你能用SIGABRT(6?)或SIGTERM(15)杀死它们,那就更有礼貌了.

在Windows操作系统中,您没有os.kill可行的功能.查看此ActiveState配方以终止Windows中的进程.

我们有子进程是WSGI服务器.要终止它们,我们在特殊URL上进行GET; 这导致孩子清理并退出.


小智 6

orip 的答案很有帮助,但有一个缺点,它会终止您的进程并向您的父进程返回错误代码。我这样避免了:

class CleanChildProcesses:
  def __enter__(self):
    os.setpgrp() # create new process group, become its leader
  def __exit__(self, type, value, traceback):
    try:
      os.killpg(0, signal.SIGINT) # kill all processes in my group
    except KeyboardInterrupt:
      # SIGINT is delievered to this process as well as the child processes.
      # Ignore it so that the existing exception, if any, is returned. This
      # leaves us with a clean exit code if there was no exception.
      pass
Run Code Online (Sandbox Code Playgroud)

进而:

  with CleanChildProcesses():
    # Do your work here
Run Code Online (Sandbox Code Playgroud)

当然你可以使用 try/ except/finally 来做到这一点,但是你必须分别处理异常和非异常情况。


Pat*_*ick 6

找出linux的解决方案(不安装prctl):

def _set_pdeathsig(sig=signal.SIGTERM):
    """help function to ensure once parent process exits, its childrent processes will automatically die
    """
    def callable():
        libc = ctypes.CDLL("libc.so.6")
        return libc.prctl(1, sig)
    return callable


subprocess.Popen(your_command, preexec_fn=_set_pdeathsig(signal.SIGTERM)) 
Run Code Online (Sandbox Code Playgroud)


ber*_*rio 5

我需要这个问题的一个小变化(清理子进程,但不退出Python程序本身),因为这里在其他答案中没有提到它:

p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
Run Code Online (Sandbox Code Playgroud)

setsid将在新会话中运行该程序,从而为其及其子进程分配一个新的进程组。因此调用os.killpg它也不会关闭你自己的 python 进程。


Car*_*uin 5

警告:仅限 Linux!您可以让您的孩子在其父母去世时收到信号。

首先安装 python-prctl==1.5.0 然后更改您的父代码以启动您的子进程,如下所示

subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
Run Code Online (Sandbox Code Playgroud)

这说的是:

  • 启动子进程: sleep 100
  • 在分叉之后和子进程执行之前,子进程注册“当我的父进程终止时向我发送一个 SIGKILL”。