IT Share you

Python 프로그램 종료시 하위 프로세스가 종료되었는지 확인

shareyou 2020. 12. 15. 20:23
반응형

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

반응형