Comment vérifier si un PID donné fonctionne?

16

J'écris un script Perl qui analyse les fichiers journaux pour collecter les PID, puis vérifie si ce PID est en cours d'exécution. J'essaie de penser à la meilleure façon de faire ce chèque. Évidemment, je pourrais faire quelque chose comme:

system("ps $pid > /dev/null") && print "Not running\n";

Cependant, je préfère éviter l'appel système si possible. J'ai donc pensé pouvoir utiliser le /procsystème de fichiers (la portabilité n'est pas un problème, cela fonctionnera toujours sur un système Linux). Par exemple:

if(! -d "/proc/$pid"){
    print "Not running\n";
}

Est-ce sûr? Puis-je toujours supposer que s'il n'y a pas de /proc/$pid/répertoire, le PID associé ne fonctionne pas? Je m'attends à ce que puisque l'AFAIK pslui-même obtient ses informations de /proctoute façon, mais comme il s'agit du code de production, je veux en être sûr.

Ainsi, peut-il y avoir des cas où un processus en cours d'exécution n'a pas de /proc/PIDrépertoire ou où un /proc/PIDrépertoire existe et le processus n'est pas en cours d'exécution? Y a-t-il une raison de préférer l'analyse psau lieu de vérifier l'existence du répertoire?

terdon
la source
2
il y a aussi la killfonction perl utilisant le signal 0 qui ne tue pas mais dit si vous pouvez le faire (c'est-à-dire que vous avez besoin d'une autorisation pour signaler ce processus).
meuh
1
'/ proc / $ PID' devrait être parfait si vous faites cela sous Linux.
likewhoa
7
@terdon Notez que quelle que soit la méthode que vous utilisez (et kill -0c'est la meilleure), cela vous indique uniquement s'il existe un processus en cours avec le PID donné . Il ne vous dit pas si le processus sera toujours en cours d'exécution une milliseconde plus tard, et il ne vous indique pas si le processus est celui qui vous intéresse ou un processus indépendant qui a reçu le même PID après la mort du processus intéressant . C'est presque toujours une erreur de tester si un PID donné fonctionne : il y a très peu de circonstances où cela n'est pas sujet aux conditions de course.
Gilles 'SO- arrête d'être méchant'
1
@Gilles en effet, je devrais également vérifier le nom du processus. Cependant, dans ce cas, je me fiche qu'il y ait un changement de statut une milliseconde plus tard. J'ai des processus répertoriés comme actifs dans un dB et un fichier correspondant avec les pids. J'ai juste besoin de vérifier si quelque chose que la DB pense être en cours d'exécution ne fonctionne pas réellement et d'avoir suffisamment de contrôle sur la configuration pour savoir qu'elle ne peut pas redémarrer de manière aléatoire.
terdon
3
@MickLH wow, c'est ce qu'on m'a dit. Cela aurait en fait été un commentaire utile au lieu d'une célébration de votre propre éclat si vous aviez pris la peine d'expliquer ce qui est si mauvais et dangereux. Je ne doute pas que vous ayez raison, je n'ai jamais prétendu être programmeur, c'est pourquoi j'ai posé la question, mais vous avez réussi à être à la fois insultant et inutile.
terdon

Réponses:

20

La fonction perl kill(0,$pid) peut être utilisée.

Si le code retour est 1, le PID existe et vous êtes autorisé à lui envoyer un signal.

Si le code retour est 0, vous devez vérifier $ !. Il peut s'agir d'EPERM (autorisation refusée), ce qui signifie que le processus existe ou ESRCH, auquel cas le processus n'existe pas.

Si votre code de vérification est en cours d'exécution, rootvous pouvez simplifier cela en vérifiant simplement le code retour de kill; 0 => erreur, 1 => ok

Par exemple:

% perl -d -e 0

Loading DB routines from perl5db.pl version 1.37
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(-e:1):   0
  DB<1> print kill(0,500)
0
  DB<2> print $!
No such process
  DB<3> print kill(0,1)
0
  DB<4> print $!
Operation not permitted
  DB<5> print kill(0,$$)
1

Cela peut être transformé en une fonction simple

use Errno;

sub test_pid($)
{
  my ($pid)=@_;

  my $not_present=(!kill(0,$pid) && $! == Errno::ESRCH);

  return($not_present);
}

print "PID 500 not present\n" if test_pid(500);
print "PID 1 not present\n" if test_pid(1);
print "PID $$ not present\n" if test_pid($$);
Stephen Harris
la source
FWIW, j'ai ajouté une fonction simple montrant comment vous pouvez le faire.
Stephen Harris
Ouais, je pense que j'irai avec ça. J'ai supprimé mon commentaire car j'ai réalisé que tout ce dont j'avais besoin était if (!kill(0,$pid) && $! =~ /No such process/){ exit; }ou similaire. J'aime Errnomieux votre solution, merci. Bien que j'y vais probablement, j'attendrai un moment au cas où quelqu'un pourrait répondre à la question Linux sous-jacente.
terdon
2
Si /procest alors monté chaque PID visible dans l'espace de noms sera présent, de sorte que votre -d /proc/$pidessai ne fonctionne ... mais il implique de sortir du système de fichiers plutôt que d' utiliser des appels système natif.
Stephen Harris
C'est précisément ce que je voulais éviter, oui.
terdon
2
@terdon: Je viens de réaliser que ma confusion venait du fait que par "appel système" vous vouliez dire " systemappel" - c'est-à-dire un appel à la systemfonction elle-même, pas un "appel système" . Le dernier que vous ne pouvez pas éviter, mais le premier que vous pouvez certainement. Ça a du sens maintenant!
user541686
6
  • Je suis sûr à 99,9% que la vérification de l' existence (et d'un répertoire) est à 98% aussi fiable que la technique. La raison pour laquelle le 98% n'est pas 100% est un point que Stephen Harris a abordé (et rebondi) dans un commentaire - à savoir, le système de fichiers pourrait ne pas être monté. Il peut être valide pour demander qu'un système Linux sans un système endommagé, dégradé - après tout, des choses comme , , et/proc/PIDkill 0/proc/procpstoplsof ne fonctionnera probablement pas - et si cela pourrait ne pas être un problème pour un système de production. Mais il est (théoriquement) possible qu'il n'ait jamais été monté (bien que cela puisse empêcher le système de revenir à un état normal), il est certainement possible qu'il soit démonté (je l'ai testé 1), et je pense qu'il n'y a aucune garantie qu'elle existera (c'est-à-dire qu'elle n'est pas requise par POSIX). Et, à moins que le système ne soit complètement arrosé, killcela fonctionnera.
  • Le commentaire de Stephen parle de «sortir vers le système de fichiers» et «d'utiliser les appels système natifs». Je crois que c'est en grande partie un hareng rouge.
    • Oui, toute tentative d'accès /proc nécessite la lecture du répertoire racine pour trouver le /procsystème de fichiers. Cela est vrai pour toute tentative d'accéder à tout fichier par un chemin absolu, y compris les choses dans /bin, /etcet /dev. Cela se produit si souvent que le répertoire racine est sûrement mis en cache dans la mémoire pendant toute la durée de vie (disponibilité) du système, donc cette étape peut être effectuée sans aucune E / S disque. Et, une fois que vous avez l'inode de /proc, tout ce qui se passe est en mémoire.
    • Comment accédez-vous /proc? Avec stat, open, readdir, etc., qui sont le système natif appelle tout autant que kill.
  • La question parle d'un processus en cours. Ceci est une phrase glissante. Si vous voulez réellement tester si le processus est en cours d'exécution (c'est-à-dire dans la file d'attente d'exécution; peut-être le processus en cours sur un processeur; pas en veille, en attente ou arrêté), vous devrez peut-être faire et lire la sortie , ou regarder . Mais je ne vois aucun indice dans votre question ou vos commentaires que cela vous préoccupe.ps PID /proc/PID/stat

    L'éléphant dans la pièce, cependant, est qu'un processus zombie 2 peut être difficile à distinguer d'un processus qui est bien vivant.  kill 0fonctionne sur un zombie, et existe. Vous pouvez identifier les zombies avec les techniques énumérées dans le paragraphe précédent (faire et lire la sortie, ou regarder ). Mon très rapide et les tests occasionnels (c. -à- pas très approfondie) suggère que vous pouvez aussi le faire en faisant une ou sur , ou - ceux - ci échouera sur les zombies. (Cependant, ils échoueront également sur les processus que vous ne possédez pas.)/proc/PIDps PID/proc/PID/statreadlinklstat/proc/PID/cwd/proc/PID/root/proc/PID/exe

____________
1  si l' option -f( f orce) ne fonctionne pas, essayez -l( l azy).
2  c'est-à-dire un processus qui s'est terminé / est mort / a pris fin, mais dont le parent n'a pas encore fait a wait.

G-Man dit «Réintègre Monica»
la source
Merci pour votre commentaire sur ma réponse, que j'ai supprimé car c'était faux. Je ne crois pas que la kill(2)page de manuel indique directement le comportement que vous avez signalé, mais la perlfuncpage de manuel le fait. Je vais envoyer un courriel à Michael Kerrisk pour voir ce qu'il a à dire sur la page de manuel du système.
jrw32982 prend en charge Monica
J'ai déposé un rapport de bogue pour clarifier la page de manuel kill(2)concernant les autorisations pour "envoyer" le signal 0.
jrw32982 prend en charge Monica
@ jrw32982: Eh bien, la page de manuel dit des choses comme, "L' argument sig ... peut être 0, auquel cas la vérification des erreurs est effectuée ..." et " ERREURS - L' appel système kill () échouera et aucun signal ne sera envoyé si: … Le processus d'envoi n'est pas le super-utilisateur et son identifiant utilisateur effectif ne correspond pas à l'identifiant utilisateur effectif du processus récepteur. … »Maintenant que vous le mentionnez, je suppose que cela peut être interprété de plusieurs manières, mais, malheureusement, de nombreuses pages de manuel Unix sont écrites dans ce style, vous obligeant à lire entre les lignes. Michael peut croire que c'est assez clair comme ça.
G-Man dit `` Réintègre Monica '' le
Michael a modifié la kill(2)page de manuel (je ne la vois pas encore en ligne): "Si sig vaut 0, aucun signal n'est envoyé, mais des vérifications d'existence et de permission sont toujours effectuées; cela peut être utilisé pour vérifier l'existence d'un ID de processus ou ID de groupe de processus que l'appelant est autorisé à signaler. "
jrw32982 prend en charge Monica