有没有办法确保所有创建的子进程在Python程序退出时都消失了?所谓子流程,是指那些用subprocess.Popen()创建的内容。
如果不是,我是否应该遍历所有发出的终止点,然后终止-9?有什么清洁剂吗?
有没有办法确保所有创建的子进程在Python程序退出时都消失了?所谓子流程,是指那些用subprocess.Popen()创建的内容。
如果不是,我是否应该遍历所有发出的终止点,然后终止-9?有什么清洁剂吗?
Answers:
您可以为此使用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
Popen.wait()
接受timeout
参数的Python 3.3+,可以简化此答案。首先循环调用p.teminate()
子流程(请求正常关机)。然后wait()
在设置p.kill()
了超时的情况下调用每个进程,并在抛出超时到期异常时进行调用。
在* 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
处理。
的 subprocess.Popen.wait()
是确保它们已死的唯一方法。实际上,POSIX OS要求您等待孩子。许多* nix会创建一个“僵尸”进程:一个死掉的孩子,父母没有等待。
如果孩子的笔迹合理,则终止。通常,孩子们会从PIPE阅读。关闭输入对孩子来说是一个很大的暗示,它应该关闭商店并退出。
如果孩子有错误并且没有终止,则可能必须杀死它。您应该修复此错误。
如果该子项是“永远保留”循环,并且不旨在终止,则应杀死该子项或提供一些输入或消息以强制其终止。
编辑。
在标准操作系统中, os.kill( PID, 9 )
。杀死-9是残酷的,顺便说一句。如果您可以使用SIGABRT(6?)或SIGTERM(15)杀死它们,那会更礼貌。
在Windows操作系统中,没有os.kill
适用的软件。查看此ActiveState食谱以终止Windows中的进程。
我们有属于WSGI服务器的子进程。要终止它们,我们在特殊的URL上执行GET;这会导致孩子清理并离开。
查找适用于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))
poll()
检查子进程是否已终止。返回returncode属性。
我需要这个问题的一个小变化(清理子进程,但不退出Python程序本身),因为在其他答案中未提及:
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
将在新的会话中运行该程序,从而为其分配一个新的进程组及其子进程。os.killpg
因此,调用它也不会降低您自己的python进程。
What, specifically, is the question asking for? Make sure your answer provides that – or a viable alternative.
解答说:我提供了一个替代方法和一个有用的答案,与此同时,您对我发布的两个答案都持否定态度。如果您停止骚扰试图帮助stackoverflow用户的人们,那么我相信整个社区都会对此表示欢迎。
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
接着:
with CleanChildProcesses():
# Do your work here
当然,您可以使用try / except / finally进行此操作,但是必须分别处理特殊情况和非特殊情况。
Windows的解决方案可能是使用win32作业api,例如,如何在Windows中自动销毁子进程?
这是现有的python实现
我实际上需要执行此操作,但是它涉及运行远程命令。我们希望能够通过关闭与服务器的连接来停止进程。另外,例如,如果您正在python repl中运行,则希望使用Ctrl-C退出时可以选择作为前台运行。
import os, signal, time
class CleanChildProcesses:
"""
with CleanChildProcesses():
Do work here
"""
def __init__(self, time_to_die=5, foreground=False):
self.time_to_die = time_to_die # how long to give children to die before SIGKILL
self.foreground = foreground # If user wants to receive Ctrl-C
self.is_foreground = False
self.SIGNALS = (signal.SIGHUP, signal.SIGTERM, signal.SIGABRT, signal.SIGALRM, signal.SIGPIPE)
self.is_stopped = True # only call stop once (catch signal xor exiting 'with')
def _run_as_foreground(self):
if not self.foreground:
return False
try:
fd = os.open(os.ctermid(), os.O_RDWR)
except OSError:
# Happens if process not run from terminal (tty, pty)
return False
os.close(fd)
return True
def _signal_hdlr(self, sig, framte):
self.__exit__(None, None, None)
def start(self):
self.is_stopped = False
"""
When running out of remote shell, SIGHUP is only sent to the session
leader normally, the remote shell, so we need to make sure we are sent
SIGHUP. This also allows us not to kill ourselves with SIGKILL.
- A process group is called orphaned when the parent of every member is
either in the process group or outside the session. In particular,
the process group of the session leader is always orphaned.
- If termination of a process causes a process group to become orphaned,
and some member is stopped, then all are sent first SIGHUP and then
SIGCONT.
consider: prctl.set_pdeathsig(signal.SIGTERM)
"""
self.childpid = os.fork() # return 0 in the child branch, and the childpid in the parent branch
if self.childpid == 0:
try:
os.setpgrp() # create new process group, become its leader
os.kill(os.getpid(), signal.SIGSTOP) # child fork stops itself
finally:
os._exit(0) # shut down without going to __exit__
os.waitpid(self.childpid, os.WUNTRACED) # wait until child stopped after it created the process group
os.setpgid(0, self.childpid) # join child's group
if self._run_as_foreground():
hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) # ignore since would cause this process to stop
self.controlling_terminal = os.open(os.ctermid(), os.O_RDWR)
self.orig_fore_pg = os.tcgetpgrp(self.controlling_terminal) # sends SIGTTOU to this process
os.tcsetpgrp(self.controlling_terminal, self.childpid)
signal.signal(signal.SIGTTOU, hdlr)
self.is_foreground = True
self.exit_signals = dict((s, signal.signal(s, self._signal_hdlr))
for s in self.SIGNALS)
def stop(self):
try:
for s in self.SIGNALS:
#don't get interrupted while cleaning everything up
signal.signal(s, signal.SIG_IGN)
self.is_stopped = True
if self.is_foreground:
os.tcsetpgrp(self.controlling_terminal, self.orig_fore_pg)
os.close(self.controlling_terminal)
self.is_foreground = False
try:
os.kill(self.childpid, signal.SIGCONT)
except OSError:
"""
can occur if process finished and one of:
- was reaped by another process
- if parent explicitly ignored SIGCHLD
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
- parent has the SA_NOCLDWAIT flag set
"""
pass
os.setpgrp() # leave the child's process group so I won't get signals
try:
os.killpg(self.childpid, signal.SIGINT)
time.sleep(self.time_to_die) # let processes end gracefully
os.killpg(self.childpid, signal.SIGKILL) # In case process gets stuck while dying
os.waitpid(self.childpid, 0) # reap Zombie child process
except OSError as e:
pass
finally:
for s, hdlr in self.exit_signals.iteritems():
signal.signal(s, hdlr) # reset default handlers
def __enter__(self):
if self.is_stopped:
self.start()
def __exit__(self, exit_type, value, traceback):
if not self.is_stopped:
self.stop()
感谢Malcolm Handley的最初设计。在Linux上使用python2.7完成。
这是我为posix应用程序所做的:
当您的应用程序存在时,请调用此类的kill()方法:http : //www.pixelbeat.org/libs/subProcess.py
此处使用示例:http : //code.google.com/p/fslint/source/browse/trunk/fslint-gui#608
您可以尝试subalive
,我为类似问题编写的软件包。它通过RPC使用定期的活动ping,当主服务器出于某种原因停止活动ping时,从属进程会自动终止。
https://github.com/waszil/subalive
大师的例子:
from subalive import SubAliveMaster
# start subprocess with alive keeping
SubAliveMaster(<path to your slave script>)
# do your stuff
# ...
从属子过程的示例:
from subalive import SubAliveSlave
# start alive checking
SubAliveSlave()
# do your stuff
# ...