2010-11-11 41 views
16

Je voudrais exécuter plusieurs fois un sous-processus le plus rapidement possible. Cependant, parfois le processus prendra trop de temps, donc je veux le tuer. J'utilise signal.signal (...) comme ci-dessous:Tuer ou mettre fin à un sous-processus lorsque le délai d'attente?

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 

mais parfois ce code vais essayer d'arrêter le prochain tour de l'exécution. Arrêt test/home/lu/espace de travail/152/treefit/test2pour timeout /bin/sh:/home/lu/espace de travail/153/squib_driver: non trouvé --- ceci est la prochaine exécution; le programme l'arrête à tort.

Est-ce que quelqu'un sait comment résoudre ce problème? Je veux arrêter dans le temps ne pas exécuter 1 seconde le temps.sleep (n) attend souvent n secondes. Je ne veux pas que je le souhaite peut s'exécuter moins de 1 seconde

+0

Donc essentiellement si le sous-processus s'exécute sur 1 seconde, vous voulez le tuer et commencer le suivant? Est-ce correct? –

+0

Comment créez-vous votre sous-processus? parce qu'il ressemble à l'expression __ppid = pipeexe.pid__ obtient le prochain sous-processus qui sera exécuté !!! – mouad

+0

Donc essentiellement si le sous-processus s'exécute sur 1 seconde, vous voulez le tuer et commencer le suivant? Est-ce correct? oui, c'est vrai – user504909

Répondre

35

Vous pourriez faire quelque chose comme ceci:

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'idée est que vous créez un thread qui exécute la commande et de le tuer si le délai d'attente dépasse une valeur appropriée, dans ce cas, 60 secondes.

+1

+1. Cette méthode est ce que j'utilise dans mon système de construction pour tester les exécutables. – Macke

+5

Aussi +1. Cela semble être l'une des méthodes les plus propres que j'ai vues jusqu'ici. Avec une modification mineure fonctionne sur Python <2.6 sur Linux aussi en utilisant os.kill (self.p.pid, signal.SIGKILL) (ou SIGTERM suivi de SIGKILL). Peut également remplacer self.p.wait() par self.out, self.err = self.p.communicate() pour éviter de bloquer le sous-processus s'il remplit les tuyaux stdout/stderr – FredL

+0

J'ai collé ce code et ma commande apparaît commencer correctement - cependant, je dois exécuter un fil à la fois. Commencez un processus, laissez-le finir naturellement ou tuez-le si cela prend trop de temps, puis recommencez. –

0

Je suppose qu'il s'agit d'un problème de synchronisation commun dans la programmation orientée événements avec des threads et des processus.

Si vous devez toujours exécuter un seul sous-processus, assurez-vous que le sous-processus en cours est arrêté avant d'exécuter le suivant. Sinon, le gestionnaire de signal peut obtenir une référence à la dernière exécution du sous-processus et ignorer l'ancien.

Supposons que le sous-processus A est en cours d'exécution. Avant le traitement du signal d'alarme, le sous-processus B est lancé. Juste après cela, votre gestionnaire de signal d'alarme tente de tuer un sous-processus. Comme le PID actuel (ou l'objet de canal de sous-processus courant) a été mis à B lors du lancement du sous-processus, B est tué et A continue à fonctionner.

A mon avis, est-ce exact? Pour faciliter la compréhension de votre code, j'inclurais la partie qui crée un nouveau sous-processus juste après la partie qui tue le sous-processus en cours. Cela indiquerait clairement qu'un seul sous-processus est en cours d'exécution à tout moment. Le gestionnaire de signal peut effectuer à la fois la destruction et le lancement du sous-processus, comme s'il s'agissait du bloc d'itération qui s'exécute dans une boucle, dans ce cas piloté par événement avec le signal d'alarme toutes les secondes.

2

Voici quelque chose que j'ai écrit en tant que chien de garde pour l'exécution de sous-processus. Je l'utilise maintenant beaucoup, mais je ne suis pas connu alors peut-être il y a des failles dans ce:

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 

Utilisation:

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

Commentaires:

  1. ici, la délai d'attente de surveillance est en secondes; mais il est facile de changer ce qui est nécessaire en changeant la valeur time.sleep(). Le time_out devra être documenté en conséquence; En fonction de ce qui est nécessaire, il est peut-être plus approprié d'élever une exception dans ce cas.

Documentation: Je lutté un peu avec la documentation de subprocess module pour comprendre que subprocess.Popen ne bloque pas; le processus est exécuté en parallèle (peut-être que je n'utilise pas le mot correct ici, mais je pense que c'est compréhensible).

Mais comme ce que j'ai écrit est linéaire dans son exécution, je dois vraiment attendre la fin de la commande, avec un temps d'arrêt pour éviter les bogues dans la commande pour mettre en pause l'exécution nocturne du script.

0

Voici ce que j'utilise:

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 peu plus complexe, j'ai ajouté un answer to solve a similar problem: Capture stdout, alimentation stdin, et être en mesure de mettre fin après un certain temps d'inactivité et/ou après un certain temps d'exécution global.