2009-11-06 12 views
33

Muszę ograniczyć ilość czasu i procesora pobieranego przez zewnętrzne aplikacje wiersza poleceń, które odradzam z procesu Pythona używając subprocess.call, głównie dlatego, że czasami proces spawn utknął i przypina procesorowi 99%.Python: ulimit i nice dla podprocesu.call/subprocess.Popen?

ładne i ulimit wydają się rozsądnymi sposobami, aby to zrobić, ale nie jestem pewien, w jaki sposób interakcje z podprocesu.

  • Limity wyglądać mniej więcej tak:
    • zabić proces, jeśli bierze więcej niż 60 sekund
    • Limit to 20% cpu
  • Chcę zastosować zasób ograniczając do podprocesu, a nie do procesu Pythona, który odradza podprocesy.

Czy istnieje sposób zastosowania ładnej i ulimit do procesu potomnego podprocesu? Czy istnieją lepsze alternatywy w języku Pythona?

To jest na systemie Linux (ubuntu).

+1

Być może zechcesz zaakceptować odpowiedź z najwyższym wynikiem głosowania zamiast odpowiedzi. Jest znacznie lepszy niż mój. –

Odpowiedz

10

Można ustawić limity dla podprocesów z poleceń powłoki z ulimit i nice jak ten:

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

ten biegnie cpuhog z limitem 60 sekund czasu procesora i korekty uprzejmość z 15. pamiętać, że istnieje nie ma prostego sposobu na ustawienie przepustnicy CPU 20% jako takiej. Proces wykorzysta 100% procesora, chyba że inny (mniej przyjemny) proces również potrzebuje procesora.

+0

Dzięki Ville, przepustnica procesora, którą opisujesz działa świetnie. Czy wiesz, czy można zrobić to samo, określając polecenie za pomocą składni nawiasów, a nie jako ciąg? – Parand

+0

O ile mi wiadomo, musisz przekazać całą komendę powłoki w jednym ciągu, aby coś takiego działało. –

+1

To naprawdę nie jest rozwiązanie, które powinno zostać oznaczone jako zaakceptowana odpowiedź. W połączeniu z parametrami dostarczanymi przez użytkownika może to łatwo doprowadzić do otwarcia luki bezpieczeństwa. –

86

Parametr preexec_fn służy do podprocesu.Popen i modułu zasobów. Przykład:

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ę talerza w nowym procesie. W nowym procesie wywoła on setlimits(), a następnie exec child.py. Oznacza to, że zasoby będą ograniczone w procesie potomnym, ale nie w elemencie nadrzędnym.

wyjścia programu podczas uruchamiania:

./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) 

Jest to w wielu przypadkach lepszym rozwiązaniem niż próby użycia ulimit, ponieważ nie zawsze jest dobrym pomysłem na tarło podproces poprzez powłoki, zwłaszcza, że ​​często powoduje brzydki parametr cytując kłopoty.

+0

Dzięki Erik. Wygląda na to, że określa limity w procesie Pythona, a nie w procesie zewnętrznym? – Parand

+0

Proces Pythona i jego wszystkie dzieci. (To jest sposób, w jaki powinno być.;)) Ze strony podręcznika: Ograniczenia zużycia zasobów systemowych według bieżącego procesu i każdy proces, który tworzy, można uzyskać za pomocą wywołania getrlimit() i ustawić za pomocą setrlimit() połączenie. –

+2

Tak, pakiet zasobów określa limit procesu Pythona (przez setrlimit) - ale w moim przykładzie ustawia limit podprocesu utworzonego przez podprocesy.Popen, przed wywołaniem exec() w celu uruchomienia potomka. W tym przykładzie nie ma to wpływu na limity procesu wywoływania, a jedynie na ograniczenia dziecka. –

6

Erik robione to dla mnie łatwe, ale on zapomniał nice część, która Rich zauważył. Uważam, że pakiet psutil jest przyjemny (kalambur przeznaczony), ale niestety mniej przenośny.Oto moje zdanie na pytanie:

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 używany shell=True do którego jestem jakoś uczulony. Być może jestem po prostu stary i zrzędliwy, ale staram się tego uniknąć!

+3

Po co miałbyś 'psutil' gdy wbudowany w Python' os' ma już 'ładny'? – WGH

+0

Prawdopodobnie dlatego, że nie możesz przekazać PID do 'os.nice', ale możesz" psutil ". –