J'ai rencontré un problème étrange dans lequel une ps -o args -p <pid>
commande échoue très occasionnellement à trouver le processus en question, même s'il s'exécute définitivement sur le serveur en question. Les processus en question sont des scripts wrapper de longue durée utilisés pour lancer certaines applications Java.
Les « dans la nature » occurrences de la question semblent toujours arriver tôt le matin, donc il y a des preuves qu'il est lié à la charge du disque sur le serveur en question, car ils sont assez fortement chargés alors, mais en exécutant l' ps
en question dans une boucle serrée, je peux éventuellement reproduire le problème - une fois toutes les quelques centaines d'exécutions, j'obtiens une erreur.
En exécutant le script bash suivant, j'ai réussi à générer une sortie strace pour une exécution échouée et réussie:
while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>
En comparant la sortie de fail.out
et good.out
, je peux voir que l' getdents
appel système lors de l'exécution qui échoue renvoie en quelque sorte un nombre beaucoup plus petit que le nombre réel de processus sur le système (de l'ordre de ~ 500 par rapport à ~ 1100)
grep getdents good.out
getdents(5, /* 1174 entries */, 32768) = 32760
getdents(5, /* 31 entries */, 32768) = 992
getdents(5, /* 0 entries */, 32768) = 0
grep getdents fail.out
getdents(5, /* 673 entries */, 32768) = 16728
getdents(5, /* 0 entries */, 32768) = 0
... et cette liste plus courte n'inclut pas le pid en question, il n'est donc pas trouvé.
Vous pouvez ignorer cette section, les erreurs ENOTTY sont expliquées par le commentaire de dave_thompson ci-dessous, et ne sont pas liées
En outre, l'exécution a échoué obtient des
ENOTTY
erreurs qui n'apparaissent pas dans l'exécution réussie. Vers le début de la sortie je voisioctl (1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (ioctl inapproprié pour le périphérique) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (ioctl inapproprié pour le périphérique)
Et à la fin, je vois un seul
ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (ioctl inapproprié pour le périphérique)
L'échec
ioctl
à la fin se produit juste avant lesps
retours, mais il se produit après que leps
a déjà imprimé un ensemble de résultats vide, donc je ne suis pas sûr s'ils sont liés. Je sais qu'ils sont cohérents dans toutes les sorties d'échec échouées que j'ai, mais n'apparaissent pas dans celles réussies.
Je n'ai absolument aucune idée pourquoi getdents
ne trouverait pas occasionnellement la liste complète des processus, et j'ai maintenant atteint le point où je vais juste gifler un pansement sur le tout en changeant le script de contrôle qui vérifie le script wrapper en question d'appeler ps
une deuxième fois si la première échoue, mais je serais intéressé de savoir si quelqu'un a des idées sur ce qui se passe ici.
Le système en question exécute le noyau 4.16.13-1.el7.elrepo.x86_64 sur CentOS 7 et la version procps-ng 3.3.10-17.el7_5.2.x86_64
>/dev/null
sur l'invocation «échec» (dans la boucle) mais pas sur l'invocation «bonne», d'où l'ENOTTY sur le fd 1.Réponses:
Pensez à lire les informations dont vous avez besoin directement depuis le
/proc
système de fichiers plutôt que via un outil tel queps
. Vous trouverez les informations que vous recherchez ("args") dans le fichier/proc/$pid/cmdline
, uniquement séparées par des octets NUL au lieu d'espaces.Vous pouvez utiliser ce
sed
one-liner pour obtenir les arguments du processus$pid
:Cette commande équivaut à:
(L'utilisation de
args=
inps
supprimera l'en-tête.)La
sed
commande recherchera d'abord le dernier octet NUL de fin et le remplacera par une nouvelle ligne, puis remplacera tous les autres octets NUL (séparant les arguments individuels) par des espaces, pour finalement produire le même format que celui que vous voyezps
.Concernant les processus de listage dans le système, le
ps
fait-il en listant les répertoires/proc
, mais il existe des conditions de concurrence inhérentes à cette procédure, car les processus démarrent et se terminent pendant l'ps
exécution, donc ce que vous obtenez n'est pas vraiment un instantané mais une approximation. En particulier, il est possible queps
les processus soient déjà terminés au moment où ils affichent leurs résultats, ou omettent les processus qui ont démarré pendant son exécution (mais qui n'ont pas été retournés par le noyau lors de la liste du contenu de/proc
.)J'ai toujours supposé que si un processus est là avant le
ps
début et est toujours là après qu'il soit terminé, il ne le manquera pas, j'ai supposé le noyau garantirait que ceux-ci seraient toujours inclus, même s'il y a beaucoup de désabonnement d'autres processus en cours de création et de destruction. Ce que vous décrivez implique que ce n'est pas le cas. Je suis toujours sceptique à ce sujet, mais étant donné qu'il existe des conditions de course connues dans la façon dont celaps
fonctionne, je suppose qu'il est au moins plausible que la liste des PID/proc
puisse en manquer une existante en raison de ces conditions de course.Il serait possible de vérifier cela en vérifiant la source du noyau Linux, mais je ne l'ai pas (encore) fait, donc je ne peux pas vraiment dire avec certitude si une telle condition de concurrence existe qui manquerait un processus de longue durée, comme tu décris.
L'autre partie est la façon dont
ps
fonctionne. Même si vous lui passez un seul PID avec l'-p
argument, il répertorie toujours tous les PID existants, même si vous n'êtes intéressé que par ce seul. Il pourrait certainement prendre un raccourci dans ce cas et ignorer la liste des entrées/proc
et aller directement à/proc/$pid
.Je ne peux pas dire pourquoi il a été mis en œuvre de cette façon. Peut-être parce que la plupart des
ps
options sont des "filtres" sur les processus, donc l'implémentation de-p
la même manière était plus facile, prendre un raccourci pour aller directement/proc/$pid
pourrait impliquer un chemin de code séparé ou une duplication de code ... Une autre hypothèse est que certains cas, y compris-p
plus d'options supplémentaires, seraient finissent par nécessiter une inscription, il est donc peut-être complexe de déterminer quels cas exacts permettraient de prendre le raccourci et lesquels ne le permettraient pas.Ce qui nous amène à la solution de contournement, en allant directement à
/proc/$pid
, sans répertorier l'ensemble complet des PID du système, en évitant toutes les races connues et en obtenant simplement les informations dont vous avez besoin directement à la source.C'est un peu moche, mais le problème que vous décrivez existe bel et bien, ce devrait être un moyen fiable de récupérer ces informations.
la source