J'ai un script Python qui fonctionne comme un processus d'arrière-plan exécutant toutes les 60 secondes. Une partie de celle-ci est un appel à Subprocess.popen pour obtenir la sortie de PS . p> Après avoir exécuté pendant quelques jours, l'appel est erroné avec: p> Cependant, la sortie de Gratuit sur le serveur est: P>
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
9 Réponses :
Vous voudrez peut-être réellement attendre que tous ces processus PS se terminent avant d'ajouter de l'espace d'échange. P>
Ce n'est pas du tout clair ce que "fonctionne comme un processus d'arrière-plan exécutant toutes les 60 secondes" signifie. P>
Mais votre appel à Subprocess.Popen est en train de préparer un nouveau processus à chaque fois. p>
Je suppose que vous quittez d'une manière ou d'une autre, laissant tous ces processus fonctionnant ou suspendus dans un état de zombie. Cependant, le communiquer code> méthode doit em> nettoyer les sous-processus engendrés. P>
"Exécution de processus d'arrière-plan exécutant toutes les 60 secondes" signifie que le code est appelé toutes les 60 secondes dans le cadre d'un processus continuellement en cours d'exécution. Si je n'appelle pas communiquer (), je ne peux pas réellement obtenir la sortie du PS.
Communiquer () CODE> Attend que le processus génétiqué de terminer et de mettre en place des threads qui lisent ses flux STDOUT et STDERR.
@DAVIDM: "Le code"? "est appelé"? Quel code? Le sous-processus.popen? Il fourche un nouveau processus toutes les 60 secondes? Est-ce ce que vous dites? Et il n'attend jamais qu'un seul enfant finisse?
@Vinay Sajip: Bien que la communication prétendument attend la sous-processus, je ne suis pas facilement convaincu que c'est la même chose que le bon attendre la méthode code>. L'application ressemble à une dépassement du système avec des sous-processus.
Oui, "le code" comme dans la seule ligne de code figurant dans ma question initiale. Ma compréhension est qu'une fois les retours PS, ayant appelé Communicate (), la sous-traité se ferme. Peut-être que vous pourriez fournir quelques lignes de code d'échantillon montrant comment vous allez implémenter cela alors?
@S. Lott: J'ai vérifié le code source de Python 2.4.6 sur Ubuntu - communiquer code> fait i> appelle
self.wait () code>. N'est-ce pas le bon
attendre la méthode code>?
@Vinay Sajip: J'avais des doutes, mais vous avez la preuve. Communiquer appelle à l'attente. Je n'ai aucune idée de ce qui ne va pas. Je vais supprimer cette réponse.
Je ne pense pas que les circonstances données dans l'article de Zenoss que vous avez liées est la seule cause de ce message, ce n'est donc pas clair pourtant que l'espace d'échange est définitivement le problème. Je conseillerais de vous connecter de plus en plus d'informations sur des appels réussi, de sorte que vous puissiez voir l'état de mémoire libre à chaque fois avant de faire le Une dernière chose - si vous spécifiez PS CODE> appel. P>
shell = true code> dans l'appel popen, voyez-vous un comportement différent? P>
strace code>
pour voir exactement lequel Les appels système échouent. P>
Je peux ajouter la coquille = vrai po. Qu'est-ce que cela fait exactement? La documentation indique "si Shell est true, la commande spécifiée sera exécutée via la coque." Mais cela n'explique pas vraiment quelle est la différence.
Lorsque vous spécifiez shell = true code>, le programme Shell (par exemple,
bash code> sur Linux,
cmd.exe code> sous Windows) est généré qui tourne à son tour le Programme réel que vous souhaitez frayer. Ceci n'est pas suggéré comme un itinéraire pour réduire l'utilisation de la mémoire - mais plutôt comme un outil de diagnostic supplémentaire pour voir comment le comportement change. Je m'attendrais à voir plus d'informations utiles des conditions de mémoire de journalisation sur chaque reproduction et de voir comment les appels échoués et les appels réussi se corrélent avec l'état de mémoire, Swap, etc.
Avez-vous des suggestions pour savoir comment enregistrer l'utilisation de la mémoire lorsque le script fonctionne? J'ai trouvé code.AcreState.com/recipes/286222 qui semble faire le travail.
Il ne s'agit pas de la quantité de mémoire le processus Python utilise - il s'agit de la journalisation de ce que free -m code> retourne pour tous les pages de PS. Vous pouvez utiliser
sous-processus code> sur SPAWN
free -m code> et enregistrer les résultats dans un fichier.
Je mets dans un appel à mem = subprocess.popen (['free', '-m'], stdout = sous-processus.pipe) .Communicate () [0] et enregistrer la sortie avant et après chaque appel popen et l'utilisation de la mémoire Semble rester assez constant, c'est-à-dire que la mémoire ne se déploie pas lentement. C'est toujours environ 894/344/549 (total / utilisé / gratuit). L'échange reste toujours 0 mais il est attendu de manière apaire et il y a une swap disponible, il n'est tout simplement pas affiché dans la sortie libre.
Le démon est en cours d'exécution avec la strace attachée maintenant. Commentera quand il se bloque ensuite (prend quelques jours).
Avez-vous regardé votre processus au fil du temps? p>
Tout devrait donner des informations intéressantes. Je pense que le processus attache des ressources qui devraient être libérées. Y a-t-il une chance qu'il s'agisse d'attacher des poignées de ressources (blocs de mémoire, flux, poignées de fichier, fil ou poignées de processus)? STDIN, STDOUT, STDERR du "PS" engendré. Poignées de mémoire, ... à partir de nombreuses petites allocations incrémentielles. Je serais très intéressé à voir quelles commandes ci-dessus s'affichent pour votre processus lorsqu'il vient de terminer le lancement et la course pour la première fois et après 24 heures de «séance», lancez le sous-processus régulièrement. P>
Étant donné que cela meurt après quelques jours, vous pourriez le faire courir pendant quelques boucles, puis le redémarrer une fois par jour comme solution de contournement. Cela vous aiderait entre-temps. P>
jacob p>
Cette réponse de l'espace d'échange est faux. Historiquement Unix Systems souhaitait un espace d'échange disponible comme celui-là, mais ils ne fonctionnent plus de cette façon (et Linux n'a jamais fonctionné de cette façon). Vous n'êtes même pas proche de manquer de mémoire, ce n'est pas probablement le problème réel - vous manquez d'une autre ressource limitée. P>
donné où l'erreur se produit (_get_handles appelle OS.Pipe () pour créer des tuyaux à l'enfant), le seul problème réel que vous puissiez être exécuté n'est pas assez de descripteurs de fichiers libres. Je chercherais à la place des fichiers non décrits (LSOF -P sur le PID du processus faisant la popentie). Si votre programme doit vraiment conserver beaucoup de fichiers ouverts à la fois, augmentez la limite d'utilisateur et / ou la limite du système pour les descripteurs de fichier ouverts. P>
Vous avez peut-être une fuite de mémoire délimitée par certaines limite de ressource A> ( Que faites-vous avec la variable ... et PS AUX EM> peut être verbeux sur un système occupé ... p> update strong> p> < P> Vous pouvez vérifier les rlimites de votre script Python à l'aide du ressource module: p> si ces renvoient "illimité" - Voir aussi rlimit_data code>,
rlimit_as code>?) hérité par votre script Python. Vérifiez votre * ulimit (1) * s avant d'exécuter votre script et profilez l'utilisation de la mémoire du script, car d'autres ont suggéré.
PS code> après la Code Snippet Vous nous montrez? Strong> Gardez-vous une référence à cela, ne jamais être libéré? Citant le
Sous-processus Code> Module Docs
: P>
(- 1, -1) code> - alors mon hypothèse est incorrecte et que vous pouvez déplacer sur! p>
ressource.getrusion < / code>
, esp. Les champs ru _ ?? RSS code>, qui peut vous aider à instrument pour la consommation de mémoire à partir du script Python, sans bombarder à un programme externe. P> P>
J'ai mis à jour la question à inclure d'autres détails sur l'appel de fonctions qui appelle finalement le popen. Rien de spécifique n'est effectué à la variable PS après l'extrait de code - la fonction renvoie avec le résultat traité.
@Davidm, merci pour la mise à jour. Cela pousse ma question à une couche - qu'est-ce qui arrive alors processus code> est-il déjà détruit, etc.? Je vais actuellement mettre à jour avec une voie plus pythonique pour vérifier les limites de ressources ...
Les rlimits ont montré (-1, -1) sur RLIMIT_DATA et RLIMIT_AS. Les processus sont retournés, puis utilisés pour envoyer ces données à un système de surveillance. Il n'est pas détruit. J'ai mis à jour le Q avec quelques informations supplémentaires sur tout le démon.
Vous devez à des ressources libres. p> Remarque: cela ne fonctionne pas sous Windows. P> P>
Popen.communicate () appelle popen.wait () qui appelle Os.waitpid () pour vous. Il n'est pas nécessaire d'appeler manuellement Os.Waitpid () manuellement.
Si vous exécutez un processus d'arrière-plan, il est probable que vous avez redirigé vos processus STDIN / STDOUT / STDERR. P>
Dans ce cas, appendez l'option "Close_fds = true" à votre appel popen, qui empêchera le processus d'enfant d'hériter de votre sortie redirigée. Cela peut être la limite que vous changez. P>
Lorsque vous utilisez popen, vous devez remettre à proximité.fds = true si vous souhaitez que les descripteurs de fichiers supplémentaires. p>
Création d'un nouveau tuyau, qui se produit dans la fonction _get_handles à partir de la trace arrière, crée 2 descripteurs de fichier, mais votre code actuel ne les ferme jamais et que vous frappiez finalement votre système FD limite maximale. p>
Je ne sais pas pourquoi l'erreur que vous obtenez indique une condition de mémoire hors mémoire: il devrait s'agir d'une erreur de descripteur de fichier car la valeur de retour de tuyau () code> a un code d'erreur pour ce problème. < / p>
Je pense que ce n'est que pour fermer des descripteurs supplémentaires pendant que le sous-processus est en cours d'exécution. Lorsque le sous-processus sort, il fermera tous ses descripteurs de toute façon, n'est-ce pas?
@Vinay Sajip, oui, cette réponse semble hors base. "Fermer_fds" a trait à la FDS héritée des sous-processus (comme ^ f de Perl) et le module de sous-processus / communication () s'occupe de la fermeture du tuyau entre le parent et l'enfant intelligemment. Il semble également improbable que votre enomème soit réellement enfile / Emfile dans le déguisement.
semblait plus profondément dans le code et les FD de la pipe sont fermées correctement. Lorsque la fourchette survient avec FRANK_FDS = FAUX, toutes les FD du processus parent sont copiées dans l'enfant, dans ce cas, tous les FD du processus Python, car ce code fait partie d'un script plus important, il pourrait y avoir des lots ouverts. Selon POSIX, celles-ci devraient être fermées lorsque le processus d'enfant se ferme, mais il est assez courant de ne pas se produire que cela ne se produise pas (la recherche rapide de Google pour FD FUK fournira des références). Je pense toujours que les FD sont le problème. Pourrait-on confirmer que cela a résolu le problème?
Cela n'a pas résolu le problème. J'ai republié la question à
La mémoire virtuelle importe !!! p>
J'ai rencontré le même problème avant d'ajouter un échange à mon système d'exploitation. La formule de la mémoire virtuelle est généralement comme: swapsize + 50% * PhysicalMemorySize. J'obtiens enfin ce résolu en ajoutant plus de mémoire physique ou en ajoutant un disque de swap. fermer_fds ne fonctionnera pas dans mon cas. P>
Si vous exécutez le haut, voyez-vous que votre processus d'arrière-plan consommer de plus grandes quantités de mémoire? Compte tenu du code où il échoue, je me méfierais de manquer de descripteurs de fichiers (bien que cela soit différent d'Erno). Quels autres types de choses faites-vous toutes les 60 secondes?
Après avoir enregistré la sortie de Free -M avant et après chaque appel popen, la mémoire reste la même. Comment puis-je vérifier les descripteurs de fichier? Divers autres processus sont également lancés, mais ils sont également enregistrés et la mémoire n'est pas "utilisée" au fil du temps.
J'ai mis à jour ma réponse avec une autre suggestion.