确保子进程在退出Python程序时结束
有没有办法确保在Python程序退出时,所有创建的子进程都已经结束?这里的子进程是指用subprocess.Popen()创建的那些。
如果没有的话,我是不是应该先尝试结束所有的进程,然后再用kill -9强制结束?有没有更简单的方法?
15 个回答
subprocess.Popen.wait()
是确保子进程已经结束的唯一方法。实际上,POSIX 操作系统要求你必须等待你的子进程结束。如果你不等待,很多类 Unix 系统会产生一个“僵尸”进程:也就是一个已经结束但父进程没有等待的子进程。
如果子进程写得比较好,它会正常结束。通常,子进程会从管道(PIPE)中读取数据。关闭输入是一个很好的提示,告诉子进程该结束了。
如果子进程有问题没有结束,你可能需要强制结束它。你应该修复这个问题。
如果子进程是一个“永远运行”的循环,并且设计上不打算结束,你要么杀掉它,要么提供一些输入或消息,强制它结束。
补充说明。
在标准操作系统中,你可以使用 os.kill( PID, 9 )
来结束进程。需要注意的是,杀死进程的方式 -9 是比较粗暴的。如果能用 SIGABRT (6) 或 SIGTERM (15) 来结束,那就显得更礼貌一些。
在 Windows 操作系统中,没有一个有效的 os.kill
方法。你可以查看这个 ActiveState 的教程,了解如何在 Windows 中结束一个进程。
我们有一些子进程是 WSGI 服务器。为了结束它们,我们会对一个特殊的 URL 发送 GET 请求;这样会让子进程进行清理并退出。
在*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
另一个需要考虑的事情是升级信号:从SIGTERM(这是kill
的默认信号)到SIGKILL(也就是kill -9
)。在发送信号之间稍等一会儿,这样可以给进程一个机会,干净地退出,然后再用kill -9
。
你可以使用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!
注意 -- 如果这个进程(父进程)被杀掉,注册的函数将不会被执行。
对于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