Pergunta

Existe uma maneira para garantir que todos subprocess criado estão mortos em tempo de saída de um programa de Python? Por subprocess quero dizer aqueles criados com subprocess.Popen ().

Se não, eu deveria iterar sobre todas as destruições de emissão e depois mata -9? nada mais limpo?

Foi útil?

Solução

Você pode usar atexit para isso, e registo quaisquer tarefas limpa até ser executados quando o programa termina.

atexit.register (func [, * args [, ** Kargs]])

Em seu processo de limpeza, você também pode implementar seu próprio espera, e matá-lo quando um o seu tempo limite desejado ocorre.

>>> 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!

Nota -. Funções registadas não será executado se este processo (processo pai) é morto

O seguinte método de janelas não é mais necessário para python> = 2,6

Aqui está uma maneira de matar um processo no Windows. Seu objeto Popen tem um atributo pid, assim você pode simplesmente chamá-lo pelo sucesso = win_kill (p.pid) (Necessidades pywin32 instalado):

    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

Outras dicas

Em * nix de, talvez usando grupos de processos pode ajudá-lo -. Você pode pegar subprocessos gerados por seus subprocessos bem

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

Outra consideração é a escalar os sinais: de SIGTERM (sinal padrão para kill) para SIGKILL (a.k.a kill -9). Espere um curto espaço de tempo entre os sinais para dar o processo de uma chance para sair de forma limpa antes de kill -9-lo.

O subprocess.Popen.wait() é a única maneira de garantir que eles estão mortos. Na verdade, do POSIX OS exigem que você espera em seus filhos. Muitos de * nix irá criar um processo de "zombie":. Uma criança morta para que o pai não esperou

Se a criança está razoavelmente bem escrito, ele termina. Muitas vezes, as crianças lêem do cachimbo de. Fechando a entrada é uma grande dica para a criança que deve fechar a loja e sair.

Se a criança tem erros e não terminar, você pode ter que matá-lo. Você deve corrigir esse bug.

Se a criança é um loop "servir-sempre", e não é projetado para terminar, você deve quer matá-lo ou fornecer alguma entrada ou mensagem que irá forçá-lo a terminar.


Editar.

No do sistema operacional padrão, você tem os.kill( PID, 9 ). Kill -9 é dura, Aliás. Se você pode matá-los com SIGABRT (6?) Ou SIGTERM (15) que é mais educado.

No Windows OS, você não tem um os.kill que funciona. Olhada neste ActiveState Receita para encerrar um processo no Windows.

Temos processos filhos que são servidores WSGI. Para finalizá-las fazemos um GET em uma URL especial; isso faz com que a criança a limpar e sair.

Atenção: Linux-only! Você pode fazer o seu filho receber um sinal quando seus moldes originais.

Primeiro instale python-prctl == 1.5.0, em seguida, alterar o código de pai para lançar seus processos filhos da seguinte maneira

subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))

O que isto diz é:

  • lançamento subprocess: sono 100
  • após a bifurcação e antes de exec do subprocesso, a criança registros achados para "envie-me um SIGKILL quando meus termina pai".

poll ()

Verifique se o processo filho terminar. Devoluções ReturnCode atributo.

Eu precisava de uma pequena variação desse problema (limpeza subprocessos, mas sem sair do programa Python em si), e uma vez que não é mencionado aqui, entre outras respostas:

p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)

setsid irá executar o programa em uma nova sessão, atribuindo assim um novo grupo de processos a ele e seus filhos. chamando os.killpg sobre ele, portanto, não vai derrubar o seu próprio processo de python também.

resposta

O orip é útil, mas tem a desvantagem de que ele mata o seu processo e retorna um código de erro seu pai. Evitei que como esta:

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

E, em seguida:

  with CleanChildProcesses():
    # Do your work here

Claro que você pode fazer isso com try / exceto / finalmente, mas você tem que lidar com os casos excepcionais e não-excepcionais separadamente.

Existe uma maneira para garantir que todos subprocess criado estão mortos em tempo de saída de um programa de Python? Por subprocess quero dizer aqueles criados com subprocess.Popen ().

Você poderia violar o encapsulamento e test que todos os processos Popen ter terminado fazendo

subprocess._cleanup()
print subprocess._active == []

Se não, eu deveria iterar sobre todas as destruições de emissão e depois mata -9? nada mais limpo?

Você não pode garantir que todos os subprocessos são mortos sem sair e matar todos os sobreviventes. Mas se você tem esse problema, provavelmente é porque você tem um problema de design mais profundo.

Uma solução para Windows pode ser usar a api trabalho win32 por exemplo Como posso automaticamente destruir processos filho no Windows?

Aqui está uma implementação python existente

https://gist.github.com/ubershmekel/119697afba2eaecc6330

Na verdade, eu precisava fazer isso, mas envolveu a execução de comandos remotos. Queríamos ser capaz de parar os processos, fechando a conexão com o servidor. Além disso, se, por exemplo, você está executando no repl python, você pode selecionar a correr como o primeiro plano se você quiser ser capaz de usar Ctrl-C para sair.

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()

Graças a Malcolm Handley para o projeto inicial. Feito com python2.7 no Linux.

Encontrar uma solução para Linux (sem instalar 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)) 

Isto é o que eu fiz para meu aplicativo POSIX:

Quando seu aplicativo existe chamar o método kill () desta classe: http://www.pixelbeat.org/libs/subProcess.py

Exemplo usar aqui: http://code.google.com/p/ fslint / fonte / browse / trunk / fslint-gui # 608

Você pode tentar subalive, um pacote que escrevi para problema semelhante. Ele usa de ping vivo periódica via RPC, eo processo escravo termina automaticamente quando o mestre pára pings vivos por algum motivo.

https://github.com/waszil/subalive

Exemplo para mestre:

from subalive import SubAliveMaster

# start subprocess with alive keeping
SubAliveMaster(<path to your slave script>)

# do your stuff
# ...

Exemplo para subprocesso escravo:

from subalive import SubAliveSlave

# start alive checking
SubAliveSlave()

# do your stuff
# ...
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top