파이썬 프로그램 종료에서 하위 프로세스가 사망했는지 확인합니다
-
11-07-2019 - |
문제
Python 프로그램의 출구 시간에 생성 된 모든 하위 프로세스가 죽을 수있는 방법이 있습니까? 하위 프로세스로는 하위 프로세스로 생성 된 것을 의미합니다 .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!
메모 -이 프로세스 (부모 프로세스)가 사망 한 경우 등록 기능이 실행되지 않습니다.
Python> = 2.6에는 다음 Windows 방법이 더 이상 필요하지 않습니다.
Windows에서 프로세스를 죽이는 방법은 다음과 같습니다. Popen 객체에는 PID 속성이 있으므로 전화 할 수 있습니다. 성공 = 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 's에서 프로세스 그룹을 사용하면 도움이 될 수 있습니다. 하위 프로세스에서도 산란 한 하위 프로세스를 잡을 수 있습니다.
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
또 다른 고려 사항은 신호를 에스컬레이션하는 것입니다. kill
) Sigkill에 (일명 kill -9
). 신호 사이에 짧은 시간을 기다리려면 프로세스가 당신 앞에 깨끗하게 빠져 나갈 수있는 기회를 제공합니다. kill -9
그것.
그만큼 subprocess.Popen.wait()
그들이 죽었는지 확인하는 유일한 방법입니다. 실제로 Posix OS는 자녀를 기다릴 것을 요구합니다. 많은 *닉스는 부모가 기다리지 않은 죽은 아이 인 "좀비"과정을 만들 것입니다.
아이가 합리적으로 잘 작성된 경우 종료됩니다. 종종 아이들은 파이프를 읽습니다. 입력을 닫는 것은 어린이가 상점을 닫고 나가야한다는 큰 힌트입니다.
아이가 벌레가 있고 종료되지 않으면 죽여야 할 수도 있습니다. 이 버그를 수정해야합니다.
아이가 "서브 프레버"루프이고 종료되도록 설계되지 않은 경우, 그것을 죽이거나 입력을 강요 할 수있는 입력이나 메시지를 제공해야합니다.
편집하다.
표준 OS에서는 있습니다 os.kill( PID, 9 )
. 살인 -9는 거칠다, btw. Sigabrt (6?) 또는 Sigterm (15)으로 그들을 죽일 수 있다면 더 정중합니다.
Windows OS에는 없습니다 os.kill
작동합니다. 이거 봐요 레시피를 활성화하십시오 Windows에서 프로세스를 종료합니다.
WSGI 서버 인 아동 프로세스가 있습니다. 그들을 종료하기 위해 우리는 특별한 URL을 얻습니다. 이로 인해 아이가 청소하고 나가게됩니다.
경고 : Linux 전용! 부모가 죽을 때 자녀가 신호를받을 수 있습니다.
먼저 Python-PRCTL == 1.5.0을 설치 한 다음 부모 코드를 변경하여 다음과 같이 자녀 프로세스를 시작하십시오.
subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
이것이 말하는 것은 다음과 같습니다.
- Subprocess 발사 : Sleep 100
- 포킹 후 및 하위 프로세스의 실행 전에, 아동은 "부모가 종료 될 때 Sigkill을 보내주십시오"를 등록합니다.
설문 조사 ()
아동 프로세스가 종료되었는지 확인하십시오. returncode 속성을 반환합니다.
이 문제의 작은 변형이 필요했지만 (하위 프로세서를 청소하지만 Python 프로그램 자체를 종료하지는 않음) 다른 답변 중에서 언급되지 않았기 때문입니다.
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
새로운 세션에서 프로그램을 실행하여 새로운 프로세스 그룹을 IT와 어린이에게 할당합니다. 부름 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
물론 시도/제외/최종적 으로이 작업을 수행 할 수 있지만 예외적이고 비 예외적인 사례를 별도로 처리해야합니다.
Python 프로그램의 출구 시간에 생성 된 모든 하위 프로세스가 죽을 수있는 방법이 있습니까? 하위 프로세스로는 하위 프로세스로 생성 된 것을 의미합니다 .popen ().
캡슐화를 위반할 수 있습니다 테스트 모든 Popen 프로세스는 수행하여 종료되었습니다
subprocess._cleanup()
print subprocess._active == []
그렇지 않다면 발행 된 모든 살인을 반복 한 다음 -9를 죽여야합니까? 더 깨끗한 것이 있습니까?
모든 서브 프로세스가 외출하거나 모든 생존자를 죽이지 않고 죽었는지 확인할 수는 없습니다. 그러나이 문제가 있다면 디자인 문제가 더 심해서 아마도 일 것입니다.
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로 완료되었습니다.
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))
이것이 제가 POSIX 앱에서 한 일입니다.
앱이 존재하면이 클래스의 kill () 메소드를 호출합니다.http://www.pixelbeat.org/libs/subprocess.py
예제 여기서 사용 :http://code.google.com/p/fslint/source/browse/trunk/fslint-gui#608
당신은 시도 할 수 있습니다 subalive
, 내가 비슷한 문제에 대해 쓴 패키지. RPC를 통해 주기적으로 살아있는 핑을 사용하며, 노예 프로세스는 마스터가 어떤 이유로 살아있는 핑을 멈출 때 자동으로 종료됩니다.
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
# ...