2010-11-11 16 views
16

Mi piacerebbe eseguire ripetutamente un sottoprocesso il più velocemente possibile. Tuttavia, a volte il processo richiederà troppo tempo, quindi voglio ucciderlo. Io uso signal.signal (...) come di seguito:Uccidere o terminare un sottoprocesso durante il timeout?

ppid=pipeexe.pid 
signal.signal(signal.SIGALRM, stop_handler) 

signal.alarm(1) 
..... 
def stop_handler(signal, frame): 
    print 'Stop test'+testdir+'for time out' 
    if(pipeexe.poll()==None and hasattr(signal, "SIGKILL")): 
     os.kill(ppid, signal.SIGKILL) 
     return False 

ma a volte questo codice cercherò di fermare il passaggio del turno da eseguire. Stop test/home/lu/workspace/152/treefit/test2per timeout /bin/sh:/home/lu/workspace/153/squib_driver: non trovato --- questa è l'esecuzione successiva; il programma lo interrompe in modo errato.

Qualcuno sa come risolvere questo? Voglio fermarmi in tempo non eseguire 1 secondo il time.sleep (n) spesso attendere n secondi. Non voglio che io possa eseguire meno di 1 secondo

+0

Quindi, in sostanza, se il sottoprocesso viene eseguito su 1 secondo, si desidera eliminarlo e avviare quello successivo? È corretto? –

+0

Come si crea il sottoprocesso? perché assomiglia all'espressione __ppid = pipeexe.pid__ sta ottenendo il successivo sottoprocesso che verrà eseguito !!! – mouad

+0

Quindi, in sostanza se il sottoprocesso viene eseguito su 1 secondo, si desidera eliminarlo e avviare quello successivo? È corretto? sì, giusto – user504909

risposta

35

Si potrebbe fare qualcosa di simile:

import subprocess as sub 
import threading 

class RunCmd(threading.Thread): 
    def __init__(self, cmd, timeout): 
     threading.Thread.__init__(self) 
     self.cmd = cmd 
     self.timeout = timeout 

    def run(self): 
     self.p = sub.Popen(self.cmd) 
     self.p.wait() 

    def Run(self): 
     self.start() 
     self.join(self.timeout) 

     if self.is_alive(): 
      self.p.terminate()  #use self.p.kill() if process needs a kill -9 
      self.join() 

RunCmd(["./someProg", "arg1"], 60).Run() 

L'idea è di creare un thread che esegue il comando e di ucciderlo se il timeout è superiore a un certo valore adatto, in questo caso 60 secondi.

+1

+1. Questo metodo è ciò che utilizzo nel mio sistema di build per testare gli eseguibili. – Macke

+5

Anche +1. Questo sembra essere uno dei metodi più puliti che ho visto finora. Con modifiche minori funziona anche su Python <2.6 su linux usando os.kill (self.p.pid, signal.SIGKILL) (o SIGTERM seguito da SIGKILL). Può anche sostituire self.p.wait() con self.out, self.err = self.p.communicate() per evitare di bloccare il sottoprocesso se riempie le pipe stdout/stderr – FredL

+0

Ho incollato questo codice e il mio comando appare sta iniziando correttamente - tuttavia, ho bisogno di eseguire un thread alla volta. Avvia un processo, fallo terminare in modo naturale o uccidilo se richiede troppo tempo, quindi ricomincia. –

0

Immagino che questo sia un problema di sincronizzazione comune nella programmazione orientata agli eventi con thread e processi.

Se è sempre necessario eseguire solo un processo secondario, assicurarsi che il sottoprocesso corrente venga interrotto prima di eseguire quello successivo. In caso contrario, il gestore di segnale potrebbe ottenere un riferimento all'ultima esecuzione del sottoprocesso e ignorare il precedente.

Supponiamo che il sottoprocesso A sia in esecuzione. Prima di gestire il segnale di allarme, viene avviato il sottoprocesso B. Subito dopo, il gestore del segnale di allarme tenta di uccidere un sottoprocesso. Poiché il PID corrente (o l'attuale oggetto pipe subprocess) è stato impostato su B all'avvio del sottoprocesso, B viene ucciso e A continua a essere in esecuzione.

La mia ipotesi è corretta?

Per rendere più comprensibile il codice, includo la parte che crea un nuovo sottoprocesso subito dopo la parte che elimina il sottoprocesso corrente. Ciò renderebbe chiaro che esiste un solo processo parziale in esecuzione in qualsiasi momento. Il gestore di segnale potrebbe eseguire sia il processo secondario che l'uccisione e il lancio, come se fosse il blocco di iterazione che viene eseguito in un ciclo, in questo caso guidato dall'evento con il segnale di allarme ogni 1 secondo.

2

Ecco qualcosa che ho scritto come watchdog per l'esecuzione di sottoprocessi. Io lo uso ormai molto, ma io non sono così sperimentato così forse ci sono alcuni difetti in esso:

import subprocess 
import time 

def subprocess_execute(command, time_out=60): 
    """executing the command with a watchdog""" 

    # launching the command 
    c = subprocess.Popen(command) 

    # now waiting for the command to complete 
    t = 0 
    while t < time_out and c.poll() is None: 
     time.sleep(1) # (comment 1) 
     t += 1 

    # there are two possibilities for the while to have stopped: 
    if c.poll() is None: 
     # in the case the process did not complete, we kill it 
     c.terminate() 
     # and fill the return code with some error value 
     returncode = -1 # (comment 2) 

    else:     
     # in the case the process completed normally 
     returncode = c.poll() 

    return returncode 

utilizzo:

return = subprocess_execute(['java', '-jar', 'some.jar']) 

Commenti:

  1. qui, il il timeout del watchdog è in secondi; ma è facile passare a qualsiasi cosa sia necessaria cambiando il valore time.sleep(). Il time_out dovrà essere documentato di conseguenza;
  2. secondo quanto necessario, qui forse è più adatto a sollevare qualche eccezione.

Documentazione: ho faticato un po 'con la documentazione del modulo subprocess per capire che subprocess.Popen non blocchi; il processo è eseguito in parallelo (forse non uso la parola corretta qui, ma penso che sia comprensibile).

Ma come quello che ho scritto è lineare nella sua esecuzione, devo davvero aspettare il completamento del comando, con un timeout per evitare errori nel comando di sospendere l'esecuzione notturna dello script.

0

Ecco quello che uso:

class KillerThread(threading.Thread): 
    def __init__(self, pid, timeout, event): 
    threading.Thread.__init__(self) 
    self.pid = pid 
    self.timeout = timeout 
    self.event = event 
    self.setDaemon(True) 
    def run(self): 
    self.event.wait(self.timeout) 
    if not self.event.isSet() : 
     try: 
     os.kill(self.pid, signal.SIGKILL) 
     except OSError, e: 
     #This is raised if the process has already completed 
     pass  

def runTimed(dt, dir, args, kwargs): 
    event = threading.Event() 
    cwd = os.getcwd() 
    os.chdir(dir) 
    proc = subprocess.Popen(args, **kwargs) 
    os.chdir(cwd) 
    killer = KillerThread(proc.pid, dt, event) 
    killer.start() 

    (stdout, stderr) = proc.communicate() 
    event.set()  

    return (stdout,stderr, proc.returncode) 
0

Un po 'più complesso, ho aggiunto un answer to solve a similar problem: Catturare stdout, l'alimentazione stdin, ed essere in grado di interrompere dopo qualche tempo di inattività e/o dopo un certo tempo di esecuzione globale.

Problemi correlati