2010-04-14 1 views
18

sto usando il modulo sottoprocesso su Python 2.5 per deporre le uova un programma Java (il server di selenio, per essere precisi) come segue:Uccidere un sottoprocesso compresi i suoi figli in pitone

import os 
import subprocess 

display = 0 
log_file_path = "/tmp/selenium_log.txt" 
selenium_port = 4455 
selenium_folder_path = "/wherever/selenium/lies" 

env = os.environ 
env["DISPLAY"] = ":%d.0" % display 
command = ["java", 
      "-server", 
      "-jar", 
      'selenium-server.jar', 
      "-port %d" % selenium_port] 
log = open(log_file_path, 'a') 
comm = ' '.join(command) 
selenium_server_process = subprocess.Popen(comm, 
              cwd=selenium_folder_path, 
              stdout=log, 
              stderr=log, 
              env=env, 
              shell=True) 

Questo processo si suppone vengono uccisi una volta terminati i test automatici. Sto utilizzando os.kill per fare questo:

os.killpg(selenium_server_process.pid, signal.SIGTERM) 
selenium_server_process.wait() 

Questo non funziona. Il motivo è che il sottoprocesso della shell genera un altro processo per java, e il pid di quel processo è sconosciuto al mio codice python. Ho provato ad uccidere il gruppo di processi con os.killpg, ma questo uccide anche il processo Python che esegue questo codice in primo luogo. Impostare shell su false, evitando così java di funzionare all'interno di un ambiente shell, è anche fuori questione, a causa di altri motivi.

Come posso eliminare la shell e qualsiasi altro processo generato da esso?

+0

Questo è solo unix, giusto? – Macke

+1

related: [Come terminare un sottoprocesso python lanciato con shell = True] (http://stackoverflow.com/q/4789837/4279) – jfs

risposta

4

La soluzione ovvia in questo caso è quello di non coinvolgere il guscio:

import os 
import subprocess 

display = 0 
log_file_path = "/tmp/selenium_log.txt" 
selenium_port = 4455 
selenium_folder_path = "/wherever/selenium/lies" 

env = os.environ 
env["DISPLAY"] = ":%d.0" % display 
command = ["java", 
      "-server", 
      "-jar", 
      'selenium-server.jar', 
      "-port", 
      str(selenium_port)] 
log = open(log_file_path, 'a') 
selenium_server_process = subprocess.Popen(command, 
              cwd=selenium_folder_path, 
              stdout=log, 
              stderr=subprocess.STDOUT, 
              env=env) 

questo renderà il processo sia direttamente il processo Java. Tieni presente che potrebbe ancora generare processi che non fanno parte del gruppo di processi, quindi os.killpg potrebbe non sapere ancora come eliminarli.

Se hai un motivo per invocare la shell (il codice sopra non lo fa, e ci sono poche cose che non puoi fare senza la shell, ma supponiamo di farlo), dovresti far passare la shell al pid di il processo è iniziato in qualche modo. Fare questo non è semplice e piuttosto situazionale.

+2

Oppure, se si coinvolge la shell per qualsiasi motivo (diciamo che vuoi espansione/sostituzione/qualunque), utilizzare "exec" all'inizio per evitare il biforcazione del guscio. (In particolare, l'aggiunta di "exec" all'inizio della definizione di "comando" avrebbe risolto il problema.) – moshez

+0

@moshez, fantastico! l'ha fatto molte grazie. – afroulas

+1

Hai effettivamente un * motivo * per coinvolgere la shell, però? C'è molto raramente. –

26

per gestire il problema generale:

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

setsid verrà eseguito il programma in una nuova sessione, assegnando così un nuovo gruppo di processi ad esso e dei suoi figli. chiamare os.killpg in questo modo non ridurrà anche il tuo processo Python.

+0

Non c'è bisogno di usare 'setsid'; puoi [chiamare 'os.setsid' in Python] (http://stackoverflow.com/a/4791612/4279) – jfs

+0

No, non puoi ... questo cambierà la sessione del processo stesso, se quello che sei dopo aver ucciso solo i bambini non è quello che vuoi – berdario

+0

rileggere il titolo della domanda: * "Uccidere un sottoprocesso compresi i suoi figli da python" * – jfs