Python sous-processus.Popen erroring avec OSError: [Errno 12] Ne peut pas allouer de la mémoire après la période de temps
Note: Cette question a été re-demandé un résumé de tous les débogage tentatives ici.
J'ai un script Python qui s'exécute comme un processus d'arrière-plan de l'exécution de toutes les 60 secondes. C'est en partie un appel à sous-processus.Popen pour obtenir la sortie de ps.
ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]
Au bout de quelques jours, l'appel est erroring avec:
Fichier "/home/admin/sd-agent/checks.py", à la ligne 436, dans getProcesses Le fichier "/usr/lib/python2.4/subprocess.py", à la ligne 533, dans __init__ Le fichier "/usr/lib/python2.4/subprocess.py" de ligne, 835, dans _get_handles OSError: [Errno 12] Impossible d'allouer de la mémoire
Cependant la sortie de gratuit sur le serveur:
$ libres -m total free shared buffers cached Mem: 894 345 549 0 0 0 -/+ buffers/cache: 345 549 Swap: 0 0 0
J'ai cherché partout pour le problème et trouvé cet article qui dit:
Solution est d'ajouter de l'espace de swap à votre serveur. Lorsque le noyau est un fork de commencer à le modeler ou de processus de découverte, il s'assure d'abord il y a assez d'espace disponible sur le swap de stocker le nouveau processus si nécessaire.
Je remarque qu'il n'y a pas de swap disponible à partir de la sortie ci-dessus. Est cela le problème et/ou ce que d'autres solutions pourrait-il être?
Mise à jour 13 Août 09 Le code ci-dessus est appelé toutes les 60 secondes dans le cadre d'une série de fonctions de surveillance. Le processus est automatiquement et que la vérification soit planifiée à l'aide sched. Le code correspondant à la fonction ci-dessus est:
def getProcesses(self):
self.checksLogger.debug('getProcesses: start')
# Memory logging (case 27152)
if self.agentConfig['debugMode'] and sys.platform == 'linux2':
mem = subprocess.Popen(['free', '-m'], stdout=subprocess.PIPE).communicate()[0]
self.checksLogger.debug('getProcesses: memory before Popen - ' + str(mem))
# Get output from ps
try:
self.checksLogger.debug('getProcesses: attempting Popen')
ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]
except Exception, e:
import traceback
self.checksLogger.error('getProcesses: exception = ' + traceback.format_exc())
return False
self.checksLogger.debug('getProcesses: Popen success, parsing')
# Memory logging (case 27152)
if self.agentConfig['debugMode'] and sys.platform == 'linux2':
mem = subprocess.Popen(['free', '-m'], stdout=subprocess.PIPE).communicate()[0]
self.checksLogger.debug('getProcesses: memory after Popen - ' + str(mem))
# Split out each process
processLines = ps.split('\n')
del processLines[0] # Removes the headers
processLines.pop() # Removes a trailing empty line
processes = []
self.checksLogger.debug('getProcesses: Popen success, parsing, looping')
for line in processLines:
line = line.split(None, 10)
processes.append(line)
self.checksLogger.debug('getProcesses: completed, returning')
return processes
Cela fait partie d'une classe plus appelé vérifications qui est initialisé une fois lorsque le démon est lancé.
L'ensemble des contrôles de la classe peut être trouvé à http://github.com/dmytton/sd-agent/blob/82f5ff9203e54d2adeee8cfed704d09e3f00e8eb/checks.py avec le getProcesses fonction définie à partir de la ligne de 442. Ceci est appelé par doChecks (), commençant à la ligne 520.
Après avoir connecté la sortie de free-m avant et après chaque Popen appel, la mémoire est rester le même. Comment puis-je vérifier les descripteurs de fichier? Divers autres processus sont en cours de lancement, mais elles sont également en cours de la session et de la mémoire n'est pas "utilisé" au fil du temps.
J'ai mis à jour ma réponse avec une autre suggestion.
OriginalL'auteur DavidM | 2009-08-01
Vous devez vous connecter pour publier un commentaire.
lorsque vous utilisez popen vous avez besoin de main dans la close_fds=True si vous souhaitez que la fermeture de l'extra descripteurs de fichiers.
la création d'un nouveau canal, qui se produit dans le _get_handles fonction de la trace, crée 2 descripteurs de fichiers, mais votre code actuel ne ferme jamais et votre finalement frapper vos systèmes de max fd limite.
Ne sais pas pourquoi l'erreur que vous obtenez indique une erreur de mémoire insuffisante: il doit être un descripteur de fichier d'erreur comme valeur de retour de
pipe()
a un code d'erreur pour ce problème.Sajip, oui, cette réponse semble hors de la base. "close_fds" a à voir avec le sous-processus' a hérité de fds (comme Perl $^F), et le sous-processus module/communiquer() se charge de la fermeture de la canalisation entre le parent et l'enfant de façon intelligente. Il semble également improbable que votre ENOMEM est effectivement ENFILE/EMFILE dans le déguisement.
regardé plus en profondeur dans le code et la conduite FD sont fermés correctement. Lorsque la fourche se produit avec close_fds=False toutes les FD du processus parent sont copiés dans l'enfant, dans ce cas, tous les FD est du python processus, que ce code fait partie de certains des plus grands script il pourrait y avoir beaucoup ouvert. Selon POSIX ces doit être fermé lorsque le processus enfant sort mais plutôt commune pour quelque chose à l'origine de ce pas se produire (recherche rapide sur google pour fd fuite de fournir des références). Je pense toujours que fd est le problème. Pourrait OP confirmer si cela a résolu le problème?
Cela ne résout pas le problème. J'ai renvoyé la question à stackoverflow.com/questions/1367373/...
OriginalL'auteur Mark
Vous avez peut-être eu une fuite de mémoire délimitée par certains plafond de ressources (
RLIMIT_DATA
,RLIMIT_AS
?) héritée par votre script python. Vérifiez votre *ulimit(1)*s avant d'exécuter le script, de profil et le script de l'utilisation de la mémoire, comme d'autres l'ont suggéré.Que faites-vous avec la variable
ps
après l'extrait de code que vous nous montrer? Possédez-vous une référence à elle, de ne jamais être libéré? Citant lesous-processus
module docs:... et ps aux peut être en clair sur un système occupé...
Mise à jour
Vous pouvez vérifier rlimits à partir de votre script python à l'aide de la ressources module:
Si ces retour "illimité" --
(-1, -1)
-- alors, mon hypothèse est incorrecte et vous pouvez vous déplacer sur!Voir aussi
ressource.getrusage
, esp. leru_??rss
champs, qui peut vous aider à instrument pour la consommation de mémoire de avec le script python, sans les bombardements à un programme externe.merci pour la mise à jour. Qui pousse ma question en une seule couche -- ce qui arrive alors à
processes
, est-il jamais détruit, etc.? Je vais actuellement mise à jour avec une plus pythonic moyen de vérifier les limites des ressources...Le rlimits a montré (-1, -1) sur les deux RLIMIT_DATA et RLIMIT_AS. processus est retourné et utilisé pour envoyer ces données à un système de surveillance. Il n'est pas détruit. J'ai mis à jour le Q avec un peu plus d'infos sur l'ensemble du démon.
OriginalL'auteur pilcrow
Que l'espace de swap réponse est faux. Historiquement, les systèmes Unix voulais de l'espace de swap disponible comme ça, mais ils ne travaillent pas plus de cette façon (et Linux n'a jamais travaillé de cette façon). Vous n'êtes même pas près de manquer de mémoire, ce n'est donc pas probable que le problème réel - vous allez manquer de quelque autre ressource limitée.
Donné où l'erreur se produit (_get_handles appels os.pipe() pour créer des pipes à l'enfant), le seul vrai problème que vous pourriez être en cours d'exécution n'est pas assez libre des descripteurs de fichiers. Je voudrais plutôt chercher ouvrez les fichiers (lsof-p sur le PID du processus de faire le popen). Si votre programme a vraiment besoin de garder un grand nombre de fichiers ouverts en même temps, puis augmenter la limite de l'utilisateur et/ou le système de limite pour les descripteurs de fichiers ouverts.
OriginalL'auteur
Si vous êtes en cours d'exécution d'un processus en arrière-plan, les chances sont que vous avez redirigé vers votre processus de stdin/stdout/stderr.
Dans ce cas, ajouter l'option "close_fds=True" à votre Popen d'appel, ce qui permettra d'éviter le processus de l'enfant d'hériter de votre redirigé sortie. Cela peut être la limite que vous cogner.
OriginalL'auteur jmanning2k
Vous pourriez vraiment envie de l'attendre pour tous ces PS processus de terminer avant d'ajouter de l'espace de swap.
Ce n'est pas du tout clair ce que "exécute en tant que processus d'arrière-plan de l'exécution de toutes les 60 secondes".
Mais votre appel à la sous-processus.Popen est un fork d'un nouveau processus à chaque fois.
Mise à jour.
Je suppose que vous êtes en quelque sorte en laissant tous les processus en cours d'exécution ou suspendu dans un état zombie. Cependant, la
communicate
méthode devrait nettoyer le pondu sous-processus.communicate()
attend donné naissance à la fin du processus et lance les threads qui lire son stdout et stderr ruisseaux.le code"? "est appelé"? Quel code? Le processus secondaire.Popen? Fourche d'un nouveau processus de toutes les 60 secondes? Est-ce que vous êtes en train de dire? Et il n'attend jamais pour un seul enfant à la fin?
Sajip: Tout Communiquer prétendument attend pour le sous-processus, je ne suis pas facilement convaincu que c'est la même chose que le bon
wait
méthode. L'application semble saturer le système avec les sous-processus.Lott: j'ai vérifié le code source de Python 2.4.6 sur Ubuntu -
communicate
t appelself.wait()
. N'est-ce pas le bonwait
méthode?OriginalL'auteur S.Lott
Avez-vous regardé votre processus au fil du temps?
Tous devraient donner des informations intéressantes. Je pense que le processus est accapare des ressources qui devraient être libérés. Est-il une chance que c'est d'immobiliser des ressources poignées (blocs de mémoire, les ruisseaux, les descripteurs de fichiers, thread ou processus poignées)? stdin, stdout, stderr de l'a engendré "ps". Poignées de mémoire, ... à partir de nombreuses petites différentiels allocations. Je serais très intéressé de voir ce que les commandes ci-dessus l'écran de votre processus lorsqu'il a juste fini de lancement et d'exploitation pour la première fois et après 24 heures de la "assis" il y lance le sous-processus régulièrement.
Puisqu'il meurt au bout de quelques jours, vous pouvez l'exécuter pour seulement quelques boucles, puis le redémarrer une fois par jour comme une solution de contournement. Qui pourrait vous aider dans l'intervalle.
Jacob
OriginalL'auteur TheJacobTaylor
Vous devez
pour libérer des ressources.
Remarque: cela ne fonctionne pas sur Windows.
OriginalL'auteur Jonas Byström
Je ne pense pas que les circonstances données dans le Zenoss l'article vous-même liée à la seule cause de ce message, il n'est donc pas encore tout à fait clair que l'espace d'échange est certainement le problème. Je vous conseille de journalisation des informations un peu plus, même autour de réussite des appels, de sorte que vous pouvez voir l'état de la liberté de la mémoire à chaque fois juste avant de vous faire le
ps
appel.Une chose de plus - si vous spécifiez
shell=True
dans le Popen appel, voyez-vous des différences de comportement?Mise à jour: Si pas de mémoire, la prochaine coupable possible est, en effet, les descripteurs de fichiers. Je vous conseille de l'exécution de l'échec d'une commande sous
strace
de voir exactement ce qui appels système ne sont pas.Lorsque vous spécifiez
shell=True
, le programme shell (par exemple,bash
sur Linux,cmd.exe
sur Windows) est engendré qui exécute à son tour le programme que vous voulez lancer. Ce n'est pas proposé comme une route à plus faible utilisation de la mémoire -, mais plutôt comme un outil diagnostique supplémentaire pour voir comment les changements de comportement. Je m'attends à voir plus de contribution utile à partir de la journalisation des conditions de mémoire sur chaque spawn et de voir comment échec des appels et des appels fructueux en corrélation avec l'état de la mémoire, swap, etc.Avez-vous des suggestions pour savoir comment connecter l'utilisation de la mémoire que le script s'exécute? J'ai trouvé code.activestate.com/recipes/286222 qui semble faire le travail.
Ce n'est pas à propos de la mémoire de l'Python processus - c'est à propos de la journalisation ce
free -m
les déclarations de tous les pontes du ps. Vous pouvez utilisersubprocess
pour frayerfree -m
et de consigner les résultats dans un fichier.Je l'ai mis dans un appel à mem = sous-processus.Popen(['libre', 'm'], stdout=sous-processus.PIPE).communiquer()[0] et de la journalisation de la sortie avant et après chaque Popen appel et l'utilisation de la mémoire semble rester assez constante, c'est à dire la mémoire n'a pas lentement s'épuise. C'est toujours autour de 894/344/549 (total/utilisé/libre). Swap reste toujours à 0 mais aparrantly ce qui est attendu et il est en fait un swap disponible, c'est juste pas montré dans la libre sortie.
OriginalL'auteur Vinay Sajip
De la Mémoire virtuelle questions!!!
J'ai rencontré le même problème avant que je ajouter de swap de mon OS. La formule pour la mémoire virtuelle est généralement comme: SwapSize + 50% * PhysicalMemorySize. Je reçois enfin ce résolu par l'ajout de mémoire physique ou l'ajout d'un disque d'échange. close_fds ne fonctionne pas dans mon cas.
OriginalL'auteur Haoran