pythonsubprocesskillzombie-process

Ensuring subprocesses are dead on exiting Python program


Is there a way to ensure all created subprocess are dead at exit time of a Python program? By subprocess I mean those created with subprocess.Popen().

If not, should I iterate over all of the issuing kills and then kills -9? anything cleaner?


Solution

  • You can use atexit for this, and register any clean up tasks to be run when your program exits.

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

    In your cleanup process, you can also implement your own wait, and kill it when a your desired timeout occurs.

    >>> 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!
    

    Note -- Registered functions won't be run if this process (parent process) is killed.

    The following windows method is no longer needed for python >= 2.6

    Here's a way to kill a process in windows. Your Popen object has a pid attribute, so you can just call it by success = win_kill(p.pid) (Needs pywin32 installed):

        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