有沒有一種方法可以確保在Python程序退出時所有創建的子進程都已死亡?子進程我的意思是用subprocess.Popen()創建的那些子進程。確保子進程在退出Python程序時死亡
如果不是,我是否應該遍歷所有發出的殺戮,然後殺死-9?什麼更乾淨?
有沒有一種方法可以確保在Python程序退出時所有創建的子進程都已死亡?子進程我的意思是用subprocess.Popen()創建的那些子進程。確保子進程在退出Python程序時死亡
如果不是,我是否應該遍歷所有發出的殺戮,然後殺死-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
這裏的一個方式殺死窗口的處理。您POPEN對象有一個pid的屬性,所以你可以把它通過成功(安裝需要pywin32)= win_kill(p.pid):
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
你能解釋一下你在Windows代碼中做了些什麼嗎? – 2014-05-28 07:25:50
爲什麼'win_kill'需要,因爲p.kill()存在?它是否爲2.6以前的python用戶? – 2016-03-09 21:45:27
是的,我當時相信,2.5仍然被廣泛使用,並且p.kill()在windows中不可用。 – monkut 2016-03-10 01:17:51
民意調查()
檢查子進程已經終止。 返回returncode屬性。
有沒有一種方法,以確保所有已創建子進程正處在一個Python程序的退出時間死了嗎?子進程我的意思是用subprocess.Popen()創建的那些子進程。
你可以破壞封裝和測試所有POPEN進程已經做
subprocess._cleanup()
print subprocess._active == []
終止如果不是這樣,我應該遍歷所有發行殺死,然後殺死-9?什麼更乾淨?
你不能確保所有的子過程沒有出去和殺死每個倖存者都死了。但是如果你有這個問題,那可能是因爲你有更深的設計問題。
subprocess.Popen.wait()
是確保他們死亡的唯一方法。事實上,POSIX OS需要你等待你的孩子。許多* nix會創建一個「殭屍」進程:父母沒有等待的死亡孩子。
如果孩子寫得很好,它會終止。孩子們經常從PIPE讀書。關閉輸入對孩子來說是一個很大的暗示,它應該關閉商店並退出。
如果孩子有缺陷並且沒有終止,你可能不得不殺死它。你應該修復這個錯誤。
如果孩子是一個「永遠服務」循環,並且不是爲了終止而設計的,你應該殺掉它或者提供一些輸入或消息來強制終止。
編輯。
在標準操作系統中,您有os.kill(PID, 9)
。殺-9是苛刻的,順便說一句。如果你可以用更有禮貌的SIGABRT(6?)或SIGTERM(15)殺死他們。
在Windows操作系統中,您沒有可用的os.kill
。查看ActiveState Recipe終止Windows中的進程。
我們有WSGI服務器的子進程。要終止它們,我們在特定的URL上執行GET;這會導致孩子清理並退出。
這是我爲我的POSIX的應用程序:
當你的應用存在調用這個類的殺()方法: http://www.pixelbeat.org/libs/subProcess.py
示例使用此: http://code.google.com/p/fslint/source/browse/trunk/fslint-gui#608
在* 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(a.k.a kill -9
)。在信號之間等待一段時間,讓該過程有機會在你清除之前退出。
我需要這個問題的微小變化(清理子進程,但不退出的Python程序本身),而且因爲它是這裏沒有提到其他的答案中:
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
將在新會話中運行該程序,從而爲其及其子節點分配新的進程組。調用os.killpg
就不會造成你自己的python進程。
警告:僅限Linux!當其父母死亡時,您可以讓您的孩子接收到一個信號。
首先安裝python-prctl == 1.5。0,那麼改變你的父代碼來啓動你的孩子的過程如下
subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
它說的是:
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 /做到這一點,除了/最後,但你必須單獨處理的特殊和非特殊情況下。
windows的解決方案可能是使用win32作業api,例如, How do I automatically destroy child processes in 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。
找出一個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))
相關:如何終止蟒蛇子與殼牌推出=真(http://stackoverflow.com/q/4789837/ 4279) – jfs 2014-03-22 19:52:50
相關:[Python:當父母死亡時如何殺死子進程?](http://stackoverflow.com/q/23434842/4279) – jfs 2016-02-24 20:31:53