Python 프로그램 종료시 하위 프로세스가 종료되었는지 확인
생성 된 모든 하위 프로세스가 Python 프로그램 종료시 종료되도록하는 방법이 있습니까? 하위 프로세스 란 subprocess.Popen ()으로 만든 것을 의미합니다.
그렇지 않다면 모든 킬을 반복 한 다음 -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!
참고 -이 프로세스 (상위 프로세스)가 종료되면 등록 된 기능이 실행되지 않습니다.
다음 Windows 메소드는 더 이상 Python> = 2.6에 필요하지 않습니다.
다음은 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
* 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에서 책을 읽습니다. 입력을 닫는 것은 아이에게 가게를 닫고 나가야한다는 큰 힌트입니다.
아이에게 벌레가 있고 죽지 않으면 죽여야 할 수도 있습니다. 이 버그를 수정해야합니다.
자식이 "serve-forever"루프이고 종료하도록 설계되지 않은 경우이를 종료하거나 강제 종료 할 입력 또는 메시지를 제공해야합니다.
편집하다.
표준 OS에는 os.kill( PID, 9 )
. Kill -9는 가혹합니다, BTW. SIGABRT (6?) 또는 SIGTERM (15)로 그들을 죽일 수 있다면 더 정중합니다.
Windows OS에서는 os.kill
작동 하지 않습니다 . Windows에서 프로세스를 종료하려면 이 ActiveState 레시피 를 참조하십시오.
WSGI 서버 인 자식 프로세스가 있습니다. 그들을 종료하기 위해 우리는 특별한 URL에서 GET을합니다. 이로 인해 아이가 청소하고 종료됩니다.
경고 : Linux 전용! 부모가 사망하면 자녀에게 신호를 보내도록 할 수 있습니다.
먼저 python-prctl == 1.5.0을 설치 한 다음 부모 코드를 변경하여 다음과 같이 자식 프로세스를 시작하십시오.
subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
이것이 말하는 것은 :
- 하위 프로세스 시작 : 절전 100
- 분기 후 하위 프로세스를 실행하기 전에 자식은 "부모가 종료되면 SIGKILL을 보내주세요"를 등록합니다.
설문 조사 ()
자식 프로세스가 종료되었는지 확인합니다. returncode 속성을 반환합니다.
이 문제의 작은 변형이 필요했습니다 (하위 프로세스를 정리하지만 Python 프로그램 자체를 종료하지 않음). 여기에 다른 답변 중에서 언급되지 않았기 때문에
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
새 세션에서 프로그램을 실행하여 새 프로세스 그룹을 해당 프로그램과 하위에 할당합니다. os.killpg
따라서 호출 해도 자신의 파이썬 프로세스가 중단되지 않습니다.
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를 사용하여이 작업을 수행 할 수 있지만 예외 및 비 예외 사례를 별도로 처리해야합니다.
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))
생성 된 모든 하위 프로세스가 Python 프로그램 종료시 종료되도록하는 방법이 있습니까? 하위 프로세스 란 subprocess.Popen ()으로 만든 것을 의미합니다.
캡슐화를 위반 하고 모든 Popen 프로세스가 다음을 수행하여 종료되었는지 테스트 할 수 있습니다.
subprocess._cleanup()
print subprocess._active == []
그렇지 않다면 모든 킬을 반복 한 다음 -9를 죽여야합니까? 더 깨끗한 거?
나가서 모든 생존자를 죽이지 않고는 모든 하위 프로세스가 죽었는지 확인할 수 없습니다. 그러나이 문제가 있다면 아마도 더 깊은 디자인 문제가 있기 때문일 것입니다.
Windows에 대한 해결책은 win32 작업 API를 사용하는 것입니다. 예를 들어 Windows에서 자식 프로세스를 자동으로 제거하려면 어떻게합니까?
다음은 기존 파이썬 구현입니다.
https://gist.github.com/ubershmekel/119697afba2eaecc6330
실제로이 작업을 수행해야했지만 원격 명령을 실행해야했습니다. 서버에 대한 연결을 닫아 프로세스를 중지 할 수 있기를 원했습니다. 또한 예를 들어 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()
Thanks to Malcolm Handley for the initial design. Done with python2.7 on linux.
This is what I did for my posix app:
When your app exists call the kill() method of this class: http://www.pixelbeat.org/libs/subProcess.py
Example use here: http://code.google.com/p/fslint/source/browse/trunk/fslint-gui#608
help for python code: http://docs.python.org/dev/library/subprocess.html#subprocess.Popen.wait
You can try subalive
, a package I wrote for similar problem. It uses periodic alive ping via RPC, and the slave process automatically terminates when the master stops alive pings for some reason.
https://github.com/waszil/subalive
Example for master:
from subalive import SubAliveMaster
# start subprocess with alive keeping
SubAliveMaster(<path to your slave script>)
# do your stuff
# ...
Example for slave subprocess:
from subalive import SubAliveSlave
# start alive checking
SubAliveSlave()
# do your stuff
# ...
ReferenceURL : https://stackoverflow.com/questions/320232/ensuring-subprocesses-are-dead-on-exiting-python-program
'IT Share you' 카테고리의 다른 글
Subversion을위한 더 나은 병합 도구 (0) | 2020.12.15 |
---|---|
C ++ 클래스의 멤버는 어떻게 주문해야합니까? (0) | 2020.12.15 |
i : nil =“true”는 무엇을 의미합니까? (0) | 2020.12.15 |
Xcode 경고 "속성 액세스 결과가 사용되지 않음-getter를 부작용에 사용해서는 안 됨" (0) | 2020.12.14 |
vim에서 공백이 아닐 때까지 어떻게 삭제합니까? (0) | 2020.12.14 |