2009-11-06 11 views
33

Ho bisogno di limitare la quantità di tempo e CPU acquisita da app di riga di comando esterne che creo da un processo python usando subprocess.call, principalmente perché a volte il processo generato si blocca e attacca la CPU al 99%.Python: ulimit e nice per subprocess.call/subprocess.Popen?

nice e ulimit sembrano modi ragionevoli per farlo, ma non sono sicuro di come interagirebbero con il sottoprocesso.

  • I limiti di simile a:
    • uccidere il processo, se si sta prendendo più di 60 secondi
    • limitarla al 20% della cpu
  • voglio applicare la risorsa limitante al sottoprocesso, non al processo Python che genera i sottoprocessi.

C'è un modo per applicare nice e ulimit al processo subprocess.call generato? Ci sono alternative migliori a Python?

Questo è su un sistema linux (ubuntu).

+1

È possibile accettare la risposta con il voto più alto anziché la risposta. È molto meglio del mio –

risposta

10

È possibile impostare limiti per sottoprocessi con i comandi ulimit e nice shell come questo:

import subprocess 
subprocess.Popen('ulimit -t 60; nice -n 15 cpuhog', shell=True) 

Questo corre cpuhog con un limite di 60 secondi di tempo di CPU ed una regolazione di priorità di 15. Si noti che è nessun modo semplice per impostare un acceleratore della CPU del 20% come tale. Il processo utilizzerà il 100% della CPU a meno che un altro (meno carino) processo richieda anche la CPU.

+0

Grazie Ville, la cpu che descrivi funziona magnificamente. Sai se è possibile fare la stessa cosa specificando il comando con la sintassi della parentesi anziché come una stringa? – Parand

+0

Per quanto ne so, devi passare l'intero comando shell in una stringa per far funzionare qualcosa di simile. –

+1

Questa non è la soluzione che dovrebbe essere contrassegnata come risposta accettata. In combinazione con i parametri forniti dall'utente, questo può facilmente finire per aprire un buco di sicurezza. –

86

Utilizzare il parametro preexec_fn per sottoprocesso.Popen e il modulo di risorse. Esempio:

parent.py:

#!/usr/bin/env python 

import os 
import sys 
import resource 
import subprocess 

def setlimits(): 
    # Set maximum CPU time to 1 second in child process, after fork() but before exec() 
    print "Setting resource limit in child (pid %d)" % os.getpid() 
    resource.setrlimit(resource.RLIMIT_CPU, (1, 1)) 

print "CPU limit of parent (pid %d)" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU) 
p = subprocess.Popen(["./child.py"], preexec_fn=setlimits) 
print "CPU limit of parent (pid %d) after startup of child" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU) 
p.wait() 
print "CPU limit of parent (pid %d) after child finished executing" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU) 

child.py:

#!/usr/bin/env python 

import os 
import sys 
import resource 

print "CPU limit of child (pid %d)" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU) 

parent.py si biforca in un nuovo processo. Nel nuovo processo chiamerà setlimits(), quindi exec child.py. Ciò significa che la risorsa sarà limitata nel processo figlio, ma non nel genitore.

output del programma durante l'esecuzione:

./parent.py 
CPU limit of parent (pid 17404) (-1, -1) 
Setting resource limit in child (pid 17405) 
CPU limit of parent (pid 17404) after startup of child (-1, -1) 
CPU limit of child (pid 17405) (1, 1) 
CPU limit of parent (pid 17404) after child finished executing (-1, -1) 

Questo è in molti casi una soluzione migliore che cercare di usare ulimit, dato che non è sempre una buona idea per deporre le uova sottoprocesso via shell, soprattutto dal momento che è spesso causa di brutto parametro citando problemi.

+0

Grazie Erik. Sembra che questo limiti i limiti del processo Python, non del processo esterno? – Parand

+0

Il processo Python e tutti i suoi figli. (Ovvero, come dovrebbe essere.;)) Dalla pagina man: Limiti sul consumo di risorse di sistema dal processo corrente e ogni processo creato può essere ottenuto con la chiamata getrlimit() e impostato con il setrlimit() chiama. –

+2

Sì, il pacchetto di risorse imposta il limite sul processo python (tramite setrlimit) - ma nel mio esempio, imposta il limite sul sottoprocesso creato da subproces.Popen, prima di chiamare exec() per eseguire il figlio. Quindi, nell'esempio, i limiti del processo chiamante non sono influenzati, ma solo i limiti del bambino. –

6

Erik ha reso facile per me, ma ha dimenticato la parte nice che Rich Ha sottolineato. Trovo il pacchetto psutil carino (gioco di parole) ma sfortunatamente meno portatile.Ecco il mio prendere la questione:

import os 
import psutil 
import resource 
import subprocess 

def preexec_fn(): 
    pid = os.getpid() 
    ps = psutil.Process(pid) 
    ps.set_nice(10) 
    resource.setrlimit(resource.RLIMIT_CPU, (1, 1)) 

print "mother pid", os.getpid() 
p = subprocess.Popen(["./cpuhog.sh"], preexec_fn=preexec_fn) 
p.wait() 
print "mother still alive with pid", os.getpid() 

Ville usato il shell=True a cui io sono in qualche modo allergico. Forse sono solo vecchio e scontroso qui, ma cerco di evitarlo!

+3

Perché dovresti aver bisogno di 'psutil' quando l''OS' di Python ha già' nice'? – WGH

+0

Probabilmente perché non puoi passare un PID a 'os.nice', ma puoi' psutil'. –