2009-11-06 11 views
33

Je dois limiter la quantité de temps et de cpu pris par les applications de ligne de commande externes que je génère à partir d'un processus python utilisant subprocess.call, principalement parce que parfois le processus engendré est coincé et épingle le cpu à 99%. Nice et ulimit semblent être des moyens raisonnables de le faire, mais je ne sais pas comment ils interagiraient avec les sous-processus.Python: ulimit et sympa pour subprocess.call/subprocess.Popen?

  • Les limites ressembler à quelque chose comme:
    • tuer le processus si elle prend plus de 60 secondes
    • limite à 20% de cpu
  • Je veux appliquer la ressource limite au sous-processus, pas au processus python qui engendre les sous-processus.

Existe-t-il un moyen d'appliquer nice et ulimit au processus spawned subprocess.call? Y a-t-il de meilleures alternatives python-natives?

Ceci est sur un système Linux (Ubuntu).

+1

Vous souhaiterez peut-être accepter la réponse la mieux votée au lieu de ma réponse. C'est beaucoup mieux que le mien. –

Répondre

10

Vous pouvez définir des limites pour les sous-processus avec les commandes shell ulimit et nice comme celui-ci:

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

Cela va cpuhog avec une limite de 60 secondes de temps CPU et un réglage de niceness de 15. Notez qu'il existe pas de moyen simple de régler un accélérateur de CPU de 20% en tant que tel. Le processus utilisera 100% CPU sauf si un autre processus (moins agréable) a également besoin du CPU.

+0

Merci ville, le cpu étranglement que vous décrivez fonctionne très bien. Savez-vous s'il est possible de faire la même chose en spécifiant la commande avec la syntaxe de parenthèse au lieu d'une chaîne? – Parand

+0

Pour autant que je sache, vous devez passer toute la commande shell dans une chaîne pour que quelque chose comme ça fonctionne. –

+1

Ce n'est vraiment pas la solution qui devrait être marquée comme réponse acceptée. En combinaison avec les paramètres fournis par l'utilisateur, cela peut facilement ouvrir un trou de sécurité. –

86

Utilisez le paramètre preexec_fn pour le sous-processus.Popen et le module de ressources. Exemple:

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 se fourchette dans un nouveau processus. Dans le nouveau processus, il appellera setlimits(), puis exec child.py. Cela signifie que la ressource sera limitée dans le processus enfant, mais pas dans le parent.

sortie lorsque le programme en cours d'exécution:

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

Ceci est dans de nombreux cas, une meilleure solution que d'essayer de ulimit utiliser, car il est pas toujours une bonne idée de se reproduire sous-processus via le shell, en particulier car il provoque souvent le paramètre laid citer des problèmes.

+0

Merci Erik. Il semble que cela fixe les limites du processus python, pas sur le processus externe? – Parand

+0

Le processus Python et tous ses enfants. ;)) À partir de la page de manuel: Les limites de consommation des ressources système par le processus en cours et chaque processus qu'il crée peuvent être obtenues avec l'appel getrlimit() et définies avec setrlimit() appel. –

+2

Oui, le paquet de ressources définit la limite sur le processus python (via setrlimit) - mais dans mon exemple, il définit la limite du sous-processus créé par subproces.Popen, avant d'appeler exec() pour exécuter l'enfant. Ainsi, dans l'exemple, les limites du processus appelant ne sont pas affectées, seulement les limites de l'enfant. –

6

Erik a rendu facile pour moi, mais il a oublié la partie nice qui Rich Souligné. Je trouve le package psutil sympa (jeu de mots) mais malheureusement moins portable.Voici mon opinion à la question:

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 utilisé le shell=True auquel je suis en quelque sorte allergique. Peut-être que je suis juste vieux et grincheux ici, mais j'essaie de l'éviter!

+3

Pourquoi auriez-vous besoin de 'psutil' quand' os' de Python a déjà 'nice'? – WGH

+0

Probablement parce que vous ne pouvez pas passer un PID à 'os.nice', mais vous pouvez à' psutil'. –