python et limiter les ressources

Publié le 10 mai 2008 par Mikebrant

Tu pousses le bouchon un peu trop loin Maurice

On va voir comment limiter la mémoire ainsi que le temps processeur dans notre programme.

Pour ce faire, voici les 2 fonctions que nous utiliserons, elles proviennent du module resource :

getrlimit(maRessource) -> (minimum,maximum) : retourne un tuple contenant la limite souple et la limite stricte de maRessource .

  • limite souple : sa valeur peut être entre 0 et la limite stricte. C'est la limite actuelle de maRessource pour notre programme, lequel pourra l'augmenter ou la baisser à sa guise pour éviter une consommation excessive de maRessource .
  • limite stricte : sa valeur doit être supérieure à la limite souple. C'est en gros la valeur maximale que peut atteindre maRessource .

setrlimit(maRessource, (minimum,maximum) ) : pour limiter maRessource .

maRessource peut prendre diverses valeurs (cf man getrlimit ) .
Nous allons utiliser uniquement :

RLIMIT_AS : la taille en mémoire que peut prendre notre programme, en octet.
Si la limite souple atteint la limite stricte une exception MemoryError est lancée.
Un simple exemple (juste pour la 'syntaxe'):

#-*- coding:Utf-8 -*-
import resource
resource.setrlimit(resource.RLIMIT_AS,(104857600,209715200) )
liste=[]
while 1:
   try:
   liste.append('ok')
  
   except MemoryError,e:
   print "MemoryError"
   else:
   pass


RLIMIT_CPU : limite le temps CPU utilisé par notre programme, en seconde. Utile pour éviter des freezes (une boucle infinie par exemple) .
Dès  que la limite stricte est atteinte un signal SIGXCPU est lancé (pensez à importer le module signal ) .
Un autre exemple à 2 balles :

#-*- coding:Utf-8 -*-
import resource
import signal
def jeSuisFatigue(numeroSignal,frame):
   print "J'en peux plus, Bye"
   exit()
resource.setrlimit(resource.RLIMIT_CPU,(2,10) )
signal.signal(signal.SIGXCPU,jeSuisFatigue)
while 1:
   print "ok"
  

La méthode signal() sert à associer notre signal SIGXCPU à notre fonction jeSuisFatigue . Notre fonction doit contenir au moins 2 paramètres :
le numéro du signal et l'adresse courante de la pile ou un truc du style, j'ai pas bien compris, et puis je m'en sers pas.

Voilà pour finir ce petit billet, on va faire deux classes qui vont limiter tout ca :

#-*- coding:Utf-8 -*-
import resource
import signal
class Meta(type):
   """juste pour tester la validité des arguments"""
  
   def __call__(instance,*args):
  
   print("call est appelé")
  
   if len(args)!=2:
   #si le tuple ne fait pas 2 éléments
   return type.__call__(instance,*(-1,-1) )
   #on retourne -1 (on se casse pas la tête)
  
   if type( args[0] ) != int or type( args[1] ) != int:
   #si le tuple ne contient pas que des int
   return type.__call__(instance,*(-1,-1) )
   #on retourne -1 (idem ...)
  
   if args[0] > args[1]:
   #si la limite souple est plus grande que la limite stricte
   return type.__call__(instance,*(-1,-1) )
   #on retourne -1 (idem ...)
  
   return type.__call__(instance,*args )
  
class TropCEstTrop(object):
   """pour limiter la mémoire"""
   __metaclass__=Meta 
  
   def __init__(self,minimum=-1,maximum=-1):
   self.minimum=minimum
   self.maximum=maximum
   self.jAuraisVoulu()
  
   def etMaintenant(self):
   """que vais-je faire?"""
   print("La limite souple est de : " + str(resource.getrlimit(resource.RLIMIT_AS)[0] ) )
   #on prend le 1er élément du tuple
   print("La limite stricte est de : " + str(resource.getrlimit(resource.RLIMIT_AS)[1] ) )
   #on prend le 2° élément du tuple
  
   def jAuraisVoulu(self):
   """être un artiste"""
   resource.setrlimit(resource.RLIMIT_AS,(self.minimum,self.maximum) )  
class TEnAMisDuTemps(object):
   """pour le temps CPU """
   __metaclass__=Meta 
  
   def __init__(self,minimum=-1,maximum=-1):
   self.minimum=minimum
   self.maximum=maximum
   resource.setrlimit(resource.RLIMIT_CPU,(self.minimum,self.maximum) )
   signal.signal(signal.SIGXCPU,self.jeVeuxDormir)
  
   def jeVeuxDormir(self,numeroSignal,frame):
   print "Bonne nuit les petits"
   exit()
     
  
if __name__=='__main__':
   #hop=TropCEstTrop(209715200,"chaine")
   #hop=TropCEstTrop(69)
   hop=TropCEstTrop(104857600,209715200)
   hop.etMaintenant()
  
   #hip=TEnAMisDuTemps("chaine",10)
   #hip=TEnAMisDuTemps(69)
   hip=TEnAMisDuTemps(2,6)
   # while 1:
   #    print(" ...")

Voilà,
Je ne vais pas expliquer tout le code.
On a juste créer Meta (cf l'article dessus) qui va tester les paramètres : vous pouvez essayer en décommentant les lignes.
Le reste c'est juste du copier-coller du dessus.
Mais ce qui est bien sont que ces classes sont prêtes à l'emploi (enfin presque... commencez par faire un __call__() un peu plus rigoureux) .

Ce petit billet ( enfin j'ai pas réussi à faire plus court) est terminé, si des questions subsistent, et bien c'est con pour vous.

ps : bleu et violet/rose ca va pas du tout ensemble...