Comment puis-je tuer un processus et m'assurer que le PID n'a pas été réutilisé

40

Supposons, par exemple, que vous ayez un script shell semblable à:

longrunningthing &
p=$!
echo Killing longrunningthing on PID $p in 24 hours
sleep 86400
echo Time up!
kill $p

Devrait faire le tour, ne devrait pas? Sauf que le processus s'est peut-être terminé tôt et que son PID a peut-être été recyclé, ce qui signifie que certains travaux innocents obtiennent une bombe dans sa file d'attente de signaux. En pratique, cela a peut-être de l'importance, mais cela m'inquiète néanmoins. Le piratage à long terme pour laisser tomber tout seul, ou conserver / supprimer son PID sur le système de service ferait l'affaire, mais je pense à la situation générique ici.

FJL
la source
3
Vous vous assurez que si votre processus ciblé meurt, il tue son assassin dans le processus.
mikeserv
2
Utilisez les killallcorrespondances sur le nom, de sorte qu'au moins vous ne supprimez qu'un processus portant le même nom que longrunningthing. En supposant que vous n’en aurez qu’un seul à la fois.
LawrenceC
8
Vous pouvez enregistrer l'heure de début du processus d'origine et, avant la suppression, vérifier que l'heure de début du processus avec ce pid correspond à ce que vous avez enregistré. La paire pid, start-time est un identifiant unique pour les processus sous Linux.
Bakuriu
1
Puis-je vous demander pourquoi vous en avez besoin? Quelle est la chose fondamentale que vous essayez de réaliser? (quelque chose qui tourne continuellement mais est réinitialisé toutes les 24h?)
Olivier Dulac le
2
@mikeserv Un processus ne peut garantir qu'il se passera quelque chose en cas de décès.
Kasperd

Réponses:

29

Le mieux serait d'utiliser la timeoutcommande si vous l'avez, ce qui est fait pour ça:

timeout 86400 cmd

L’implémentation GNU actuelle (8.23) fonctionne au moins en utilisant alarm()ou l’équivalent en attendant le processus enfant. Il ne semble pas se prémunir contre la SIGALRMlivraison entre le waitpid()retour et la timeoutsortie (ce qui annule effectivement cette alarme ). Pendant cette petite fenêtre, il timeoutest même possible d’écrire des messages sur stderr (par exemple si l’enfant dumpait un noyau), ce qui élargirait encore cette fenêtre de course (indéfiniment si stderr est un tube plein, par exemple).

Personnellement, je peux vivre avec cette limitation (qui sera probablement corrigée dans une future version). timeoutfera également très attention de signaler le bon état de sortie, de gérer les autres cas (comme SIGALRM bloqué / ignoré au démarrage, de traiter les autres signaux ...) mieux que vous ne le feriez probablement à la main.

À titre approximatif, vous pouvez l'écrire perlcomme suit:

perl -MPOSIX -e '
  $p = fork();
  die "fork: $!\n" unless defined($p);
  if ($p) {
    $SIG{ALRM} = sub {
      kill "TERM", $p;
      exit 124;
    };
    alarm(86400);
    wait;
    exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
  } else {exec @ARGV}' cmd

Il y a une timelimitcommande sur http://devel.ringlet.net/sysutils/timelimit/ (devance GNU timeoutde quelques mois).

 timelimit -t 86400 cmd

Celui-ci utilise un alarm()mécanisme semblable à celui d'un utilisateur mais installe un gestionnaire SIGCHLD(en ignorant les enfants arrêtés) pour détecter l'enfant en train de mourir. Il annule également l’alarme avant son exécution waitpid()(cela n’annule pas la livraison SIGALRMsi elle était en attente, mais de la façon dont elle est écrite, je ne vois pas cela comme un problème) et tue avant d’ appeler waitpid()(donc ne peut pas tuer un pid réutilisé.) )

Netpipes a également une timelimitcommande. Celle-ci est antérieure à toutes les autres de plusieurs décennies, adopte une autre approche, mais ne fonctionne pas correctement pour les commandes arrêtées et renvoie un 1statut de sortie à l'expiration du délai.

Pour répondre plus directement à votre question, vous pouvez faire quelque chose comme:

if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
  kill "$p"
fi

C’est-à-dire, vérifiez que le processus est toujours un de nos enfants. Là encore, il y a une petite fenêtre de course (entre psrécupérer le statut de ce processus et le killtuer) au cours de laquelle le processus pourrait mourir et son pid être réutilisé par un autre processus.

Avec quelques coquilles ( zsh, bash, mksh), vous pouvez passer des spécifications d'emploi au lieu de pid.

cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status

Cela ne fonctionne que si vous ne créez qu'un seul job d'arrière-plan (sinon, il n'est pas toujours possible d'obtenir la bonne spécification d'emploi de manière fiable).

Si cela pose un problème, démarrez simplement une nouvelle instance de shell:

bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd

Cela fonctionne parce que le shell supprime le travail de la table des travaux lors du décès de l'enfant. Dans ce cas, il ne devrait y avoir aucune fenêtre de course car, au moment où le shell appelle kill(), le signal SIGCHLD n’a pas été traité et le pid ne peut pas être réutilisé (car il n’a pas été attendu), ou il a été traité et le Le travail a été supprimé de la table des processus (et killsignalerait une erreur). bash« s killau moins des blocs SIGCHLD avant qu'il accède à sa table de travail pour étendre %et débloque après la kill().

Une autre option pour éviter d'avoir ce sleepprocessus en suspens même après cmdsa mort, avec bashou ksh93consiste à utiliser un tuyau avec read -tau lieu de sleep:

{
  {
    cmd 4>&1 >&3 3>&- &
    printf '%d\n.' "$!"
  } | {
    read p
    read -t 86400 || kill "$p"
  }
} 3>&1

Celui-ci a toujours des conditions de course et vous perdez le statut de sortie de la commande. Il suppose également que cmdne ferme pas son fd 4.

Vous pouvez essayer de mettre en œuvre une solution sans race dans perlles cas suivants:

perl -MPOSIX -e '
   $p = fork();
   die "fork: $!\n" unless defined($p);
   if ($p) {
     $SIG{CHLD} = sub {
       $ss = POSIX::SigSet->new(SIGALRM); $oss = POSIX::SigSet->new;
       sigprocmask(SIG_BLOCK, $ss, $oss);
       waitpid($p,WNOHANG);
       exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
           unless $? == -1;
       sigprocmask(SIG_UNBLOCK, $oss);
     };
     $SIG{ALRM} = sub {
       kill "TERM", $p;
       exit 124;
     };
     alarm(86400);
     pause while 1;
   } else {exec @ARGV}' cmd args...

(bien qu’il faille l’améliorer pour traiter d’autres types d’affaires).

Une autre méthode sans race pourrait utiliser des groupes de processus:

set -m
((sleep 86400; kill 0) & exec cmd)

Cependant, notez que l'utilisation de groupes de processus peut avoir des effets secondaires si des entrées / sorties sont impliquées dans un terminal. Il a cependant l'avantage supplémentaire de tuer tous les autres processus supplémentaires engendrés par cmd.

Stéphane Chazelas
la source
4
Pourquoi ne pas mentionner la meilleure méthode en premier?
Deltab
2
@deltab: timeoutn'est pas portable, la réponse mentionnait d'abord une solution portable.
jeudi
1
@deltab: il donne un aperçu du fonctionnement des choses et en particulier de la façon dont une approche fondée sur le «bon sens» peut échouer (Stéphane préfère apprendre à quelqu'un à pêcher d'abord, ce que j'aime bien). On attend la réponse complète
Olivier Dulac le
@Stephane: pour "obtenir le bon nombre d'emplois n'est pas toujours possible de manière fiable": ne pouvez-vous pas d'abord compter le résultat de jobs, puis le savoir (puisqu'il s'agit de votre propre shell, dans lequel vous avez le contrôle sur ce qui se passera ensuite) le travail sera N + 1? [alors vous pouvez économiser N et plus tard tuer% N + 1])
Olivier Dulac
1
@OlivierDulac, cela supposerait qu'aucun travail précédent ne s'est terminé avant que vous n'en commenciez un nouveau (les shells réutilisent les numéros de travail).
Stéphane Chazelas
28

En général, vous ne pouvez pas. Toutes les réponses données jusqu'à présent sont des heuristiques boguées. Dans un seul cas, vous pouvez utiliser le pid en toute sécurité pour envoyer des signaux: lorsque le processus cible est un enfant direct du processus qui enverra le signal et que le parent ne l’a pas encore attendu. Dans ce cas, même s'il est sorti, le pid est réservé (c'est ce qu'est un "processus zombie") jusqu'à ce que le parent l'attende. Je ne suis au courant d'aucun moyen de le faire proprement avec la coque.

Un autre moyen sûr de supprimer les processus consiste à les démarrer avec un terminal de contrôle configuré sur un pseudo-terminal pour lequel vous possédez le côté maître. Vous pouvez ensuite envoyer des signaux via le terminal, par exemple en écrivant le caractère pour SIGTERMou SIGQUITsur le pty.

Une autre méthode plus pratique en script consiste à utiliser une screensession nommée et à envoyer des commandes à la session écran pour y mettre fin. Ce processus se déroule sur un tuyau ou un socket Unix nommé en fonction de la session d'écran, qui ne sera pas automatiquement réutilisé si vous choisissez un nom unique sécurisé.

R ..
la source
4
Je ne vois pas pourquoi cela ne pourrait pas être fait avec des coquillages. J'ai donné plusieurs solutions.
Stéphane Chazelas
3
Pourriez-vous s'il vous plaît donner une explication et une sorte de discussion quantitative sur les fenêtres de course et autres inconvénients? Sans cela, "Toutes les réponses données jusqu'à présent sont des heuristiques boguées" est une confrontation inutile sans aucun avantage.
Peter
3
@peterph: En général, toute utilisation d'un pid est une course TOCTOU - peu importe la façon dont vous vérifiez s'il fait toujours référence au même processus que celui auquel vous vous attendez, il pourrait cesser de faire référence à ce processus et faire référence à un nouveau. processus dans l'intervalle avant de l'utiliser (envoi du signal). Le seul moyen d'éviter cela est de pouvoir bloquer la libération / la réutilisation du pid, et le seul processus capable de le faire est le parent direct.
R ..
2
@ StéphaneChazelas: Comment empêcher le shell d'attendre un pid d'un processus en arrière-plan qui s'est terminé? Si vous pouvez le faire, le problème peut être résolu facilement dans le cas où OP aurait besoin.
R ..
5
@peterph: "La fenêtre de la course est petite" n'est pas une solution. Et la rareté de la race dépend de l'affectation séquentielle des pid. Les bogues qui causent quelque chose de très grave une fois par an sont bien pires que ceux qui se produisent tout le temps, car ils sont pratiquement impossibles à diagnostiquer et à corriger.
R ..
10
  1. Lors du lancement du processus, enregistrez son heure de début:

    longrunningthing &
    p=$!
    stime=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    echo "Killing longrunningthing on PID $p in 24 hours"
    sleep 86400
    echo Time up!
    
  2. Avant d'essayer de tuer le processus, arrêtez-le (ce n'est pas vraiment essentiel, mais c'est un moyen d'éviter les conditions de concurrence: si vous arrêtez le processus, son pid ne pourra pas être réutilisé)

    kill -s STOP "$p"
    
  3. Vérifiez que le processus avec ce PID a la même heure de début et si oui, tuez-le, sinon laissez le processus se poursuivre:

    cur=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    if [ "$cur" = "$stime" ]
    then
        # Okay, we can kill that process
        kill "$p"
    else
        # PID was reused. Better unblock the process!
        echo "long running task already completed!"
        kill -s CONT "$p"
    fi
    

Cela fonctionne car il ne peut y avoir qu'un seul processus avec le même PID et la même heure de démarrage sur un système d'exploitation donné.

Arrêter le processus pendant le contrôle évite que les conditions de course ne soient un problème. Évidemment, cela pose le problème suivant: certains processus aléatoires peuvent être arrêtés pendant quelques millisecondes. Selon le type de processus, cela peut poser problème ou non.


Personnellement, j'utiliserais simplement python et celui psutilqui gère automatiquement la réutilisation des PID:

import time

import psutil

# note: it would be better if you were able to avoid using
#       shell=True here.
proc = psutil.Process('longrunningtask', shell=True)
time.sleep(86400)

# PID reuse handled by the library, no need to worry.
proc.terminate()   # or: proc.kill()
Bakuriu
la source
Les règles Python sous UNIX ... Je ne sais pas pourquoi d'autres réponses ne commencent pas ici, car la plupart des systèmes n'interdisent pas son utilisation.
M. Mascaro le
J'ai déjà utilisé un schéma similaire (en utilisant l'heure de début), mais vos compétences en script sh sont plus nettes que les miennes! Merci.
FJL
Cela signifie que vous arrêtez potentiellement le mauvais processus. Notez que le ps -o start=format change de 18h12 à 26 janvier après un certain temps. Méfiez-vous des changements de l'heure d'été. Si vous êtes sous Linux, vous préférerez probablement TZ=UTC0 ps -o lstart=.
Stéphane Chazelas
@ StéphaneChazelas Oui, mais vous le laissez continuer après. J'ai explicitement dit: selon le type de tâche que ce processus est en train de réaliser, vous aurez peut-être du mal à l'arrêter quelques millisecondes. Merci pour le conseil lstart, je vais le modifier.
Bakuriu
Notez que (à moins que votre système ne limite le nombre de processus par utilisateur), il est facile pour quiconque de remplir la table des processus avec des zombies. Lorsqu'il ne reste plus que 3 pids disponibles, il est facile pour quiconque de démarrer des centaines de processus différents avec le même pid en une seconde. Donc, à proprement parler, votre "il ne peut y avoir qu'un seul processus avec le même PID et la même heure de démarrage pour un système d'exploitation donné" n'est pas nécessairement vrai.
Stéphane Chazelas
7

Sur un système Linux, vous pouvez vous assurer qu'un pid ne sera pas réutilisé en conservant son espace de noms pid actif. Cela peut être fait via le /proc/$pid/ns/pidfichier.

  • man namespaces -

    Lier le montage (voir mount(2)) d’ un des fichiers de ce répertoire à un autre emplacement du système de fichiers conserve l’espace de nommage correspondant au processus spécifié par pid, même si tous les processus en cours dans l’espace de nommage se terminent.

    L'ouverture de l'un des fichiers de ce répertoire (ou d'un fichier lié à l'un de ces fichiers) renvoie un descripteur de fichier pour l'espace de noms correspondant du processus spécifié par pid. Tant que ce descripteur de fichier reste ouvert, l'espace de nom restera actif, même si tous les processus de celui-ci se terminent. Le descripteur de fichier peut être passé à setns(2).

Vous pouvez isoler un groupe de processus - essentiellement un nombre quelconque de processus - en leur attribuant un espacement des noms init.

  • man pid_namespaces -

    Le premier processus créé dans un nouvel espace de noms (c'est-à-dire, le processus créé à l'aide clone(2) de l' indicateur CLONE_NEWPID ou le premier enfant créé par un processus après un appel à l' unshare(2)utilisation de l' indicateur CLONE_NEWPID ) possède le PID 1 et constitue le initprocessus de l'espace de nom ( voir init(1)) . Un processus enfant qui est orphelin dans l'espace de noms sera référencé à ce processus plutôt que init(1) (sauf si l'un des ancêtres de l'enfant dans le même espace de noms PID a utilisé la commande prctl(2) PR_SET_CHILD_SUBREAPER pour se désigner comme la faucheuse des processus de descendant orphelin) .

    Si le initprocessus d'un espace de noms PID se termine, le noyau met fin à tous les processus de l'espace de nom via un signal SIGKILL . Ce comportement reflète le fait que le initprocessus est essentiel au bon fonctionnement d'un espace de noms PID .

Le util-linuxpaquet fournit de nombreux outils utiles pour manipuler les espaces de noms. Par exemple, il existe unsharecependant des droits de superutilisateur si vous n’avez pas encore défini ses droits dans un espace de noms d’utilisateur:

unshare -fp sh -c 'n=
    echo "PID = $$"
    until   [ "$((n+=1))" -gt 5 ]
    do      while   sleep 1
            do      date
            done    >>log 2>/dev/null   &
    done;   sleep 5' >log
cat log; sleep 2
echo 2 secs later...
tail -n1 log

Si vous n'avez pas prévu d'espace de nom d'utilisateur, vous pouvez toujours exécuter en toute sécurité des commandes arbitraires en supprimant immédiatement les privilèges. La runusercommande est un autre binaire (non-setuid) fourni par le util-linuxpaquet et son intégration pourrait ressembler à:

sudo unshare -fp runuser -u "$USER" -- sh -c '...'

...etc.

Dans l'exemple ci-dessus, deux commutateurs sont passés à unshare(1)l' --forkindicateur qui fait du sh -cprocessus appelé le premier enfant créé et assure son initstatut, ainsi que l' --pidindicateur qui demande la unshare(1)création d'un espace de nom pid.

Le sh -cprocessus génère cinq shells enfants avec arrière-plan, chacun une whileboucle inifinite qui continuera à ajouter le résultat de dateà la fin de logtant que la valeur est sleep 1true. Après la création de ces processus, les shappels sleepdurent 5 secondes supplémentaires puis se terminent.

Il est peut-être intéressant de noter que si le -fdrapeau n'était pas utilisé, aucune des whileboucles d' arrière-plan ne se terminerait, mais avec elle ...

SORTIE:

PID = 1
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
2 secs later...
Mon Jan 26 19:17:48 PST 2015
Mikeserv
la source
Réponse intéressante qui semble être robuste. Probablement un peu exagéré pour un usage de base, mais mérite d'avoir une pensée.
Uriel
Je ne vois pas comment ni pourquoi garder un espace de noms PID actif empêche la réutilisation d'un PID. La même page de manuel que vous citez - tant que ce descripteur de fichier reste ouvert, l’espace de noms restera actif, même si tous les processus de l’espace de noms se terminent - suggère que les processus peuvent toujours se terminer (et donc probablement avoir leur ID de processus recyclé). Qu'est-ce que garder l'espace de nom PID en vie a à voir avec le fait d'empêcher le PID lui-même d'être réutilisé par un autre processus?
davmac
5

Pensez à améliorer votre longrunningthingcomportement, un peu plus semblable à un démon. Par exemple, vous pouvez lui faire créer un fichier pid qui permettra au moins un contrôle limité du processus. Il y a plusieurs façons de le faire sans modifier le binaire d'origine, impliquant toutes un wrapper. Par exemple:

  1. un simple script d'encapsuleur qui démarrera le travail requis en arrière-plan (avec une redirection de sortie facultative), écrira le PID de ce processus dans un fichier, puis attendra que le processus se termine (en utilisant wait) et supprime le fichier. Si pendant le processus d’attente le processus est tué, par exemple par quelque chose comme

    kill $(cat pidfile)
    

    le wrapper s'assurera simplement que le pidfile est supprimé.

  2. un wrapper de moniteur, qui mettra son propre PID quelque part et captera (et répondra) les signaux qui lui sont envoyés. Exemple simple:

    #!/bin/bash
    p=0
    trap killit USR1

    killit () {
        printf "USR1 caught, killing %s\n" "$p"
        kill -9 $p
    }

    printf "monitor $$ is waiting\n"
    therealstuff &
    p=%1
    wait $p
    printf "monitor exiting\n"

Maintenant, comme l'ont souligné @R .. et @ StéphaneChazelas, ces approches ont souvent une condition de concurrence critique ou imposent une restriction du nombre de processus que vous pouvez générer. De plus, il ne gère pas les cas où le longrunningthingfils peut se séparer (ce qui n’est probablement pas le problème dans la question initiale).

Avec les noyaux Linux récents (lus il y a quelques années), cela peut être traité de manière satisfaisante en utilisant des groupes de contrôle , à savoir le congélateur - qui, je suppose, est ce que certains systèmes d'initialisation Linux modernes utilisent.

Peterph
la source
Merci et à tous. Je lis tout en ce moment. Le problème, longrunningthingc'est que vous n'avez aucun contrôle sur ce que c'est. J'ai également donné un exemple de script shell car il expliquait le problème. J'aime la vôtre et toutes les autres solutions créatives ici, mais si vous utilisez Linux / bash, il existe un "timeout" intégré pour cela. Je suppose que je devrais obtenir la source à cela et voir comment il le fait!
FJL
@FJL, timeoutn'est pas un shell intégré. Il y a eu diverses implémentations d'une timeoutcommande pour Linux, une récemment (2008) ajoutée à GNU coreutils (donc non spécifique à Linux), et c'est ce que la plupart des distributions Linux utilisent de nos jours.
Stéphane Chazelas
@ Stéphane - Merci - j'ai par la suite trouvé une référence à GNU coreutils. Ils peuvent être portables, mais à moins que ce ne soit dans le système de base, on ne peut pas compter dessus. Je suis plus intéressé à savoir comment cela fonctionne, bien que je note votre commentaire ailleurs suggérant qu'il n'est pas fiable à 100%. Compte tenu de l'évolution de ce fil, je ne suis pas surpris!
FJL
1

Si vous utilisez Linux (et quelques autres * nix), vous pouvez vérifier si le processus que vous souhaitez tuer est toujours utilisé et que la ligne de commande correspond à votre processus long. Quelque chose comme :

echo Time up!
grep -q longrunningthing /proc/$p/cmdline 2>/dev/null
if [ $? -eq 0 ]
then
  kill $p
fi

Une alternative peut être de vérifier pendant combien de temps le processus que vous avez l'intention de tuer est en cours, avec quelque chose comme ps -p $p -o etime=. Vous pouvez le faire vous-même en extrayant ces informations /proc/$p/stat, mais ce serait délicat (le temps est mesuré en un tournemain et vous devrez aussi utiliser la disponibilité du système /proc/stat).

Quoi qu’il en soit, vous ne pouvez généralement pas vous assurer que le processus n’est pas remplacé après votre contrôle et avant de le tuer.

Uriel
la source
Ce n'est toujours pas correct car cela ne supprime pas la condition de concurrence.
strcat
@strcat En effet, aucune garantie de succès, mais la plupart des scripts ne se donnent même pas la peine de faire une telle vérification et ne font que tuer brutalement un cat pidfilerésultat. Je ne me souviens pas d'une manière propre de le faire en shell uniquement. La réponse proposée pour l’espace de nommage semble toutefois
intéressante
-1

C'est en fait une très bonne question.

La manière de déterminer l'unicité des processus consiste à examiner (a) où il se trouve dans la mémoire; et (b) ce que cette mémoire contient. Pour être plus précis, nous voulons savoir où se trouve en mémoire le texte du programme pour l’appel initial, car nous savons que la zone de texte de chaque thread occupera un emplacement différent en mémoire. Si le processus meurt et qu'un autre est lancé avec le même pid, le texte du programme du nouveau processus n'occupera pas la même place en mémoire et ne contiendra pas les mêmes informations.

Ainsi, immédiatement après le lancement de votre processus, faites md5sum /proc/[pid]/mapset enregistrez le résultat. Plus tard, quand vous voulez tuer le processus, faites un autre md5sum et comparez-le. Si cela correspond, tuez le pid. Si non, ne le faites pas.

pour vous en rendre compte, lancez deux obus bash identiques. Examinez le /proc/[pid]/mapspour eux et vous constaterez qu'ils sont différents. Pourquoi? Car même s’il s’agit du même programme, ils occupent différents emplacements en mémoire et les adresses de leur pile sont différentes. Ainsi, si votre processus meurt et que son PID est réutilisé, même si la même commande est relancée avec les mêmes arguments , le fichier "maps" sera différent et vous saurez que vous ne traitez pas avec le processus d'origine.

Voir: page de manuel proc pour plus de détails.

Notez que le fichier /proc/[pid]/statcontient déjà toutes les informations mentionnées par les autres afficheurs dans leurs réponses: âge du processus, pid parent, etc. Ce fichier contient à la fois des informations statiques et dynamiques, donc si vous préférez utiliser ce fichier comme base de comparaison, puis au lancement de votre longrunningthing, vous devez extraire les champs statiques suivants du statfichier et les enregistrer pour comparaison ultérieure:

pid, nom de fichier, pid du parent, identifiant du groupe de processus, terminal de contrôle, processus temporel démarré après le démarrage du système, taille du groupe de résidents, adresse du début de la pile,

pris ensemble, les éléments ci-dessus identifient de manière unique le processus, ce qui représente un autre chemin à parcourir. En réalité, vous pouvez vous contenter de "pid" et d'un "processus temporel démarré après le démarrage du système" avec un degré de confiance élevé. Extrayez simplement ces champs du statfichier et enregistrez-le quelque part lors du lancement de votre processus. Plus tard, avant de le tuer, extrayez-le à nouveau et comparez. S'ils concordent, vous êtes assuré de consulter le processus d'origine.

Michael Martinez
la source
1
Cela ne fonctionnera généralement pas si la /proc/[pid]/mapsmémoire supplémentaire est allouée, si la pile s'agrandit ou si les nouveaux fichiers sont mappés au fil du temps ... Et que signifie immédiatement après le lancement ? Après toutes les bibliothèques ont été mappés? Comment déterminez-vous cela?
Stéphane Chazelas
Je fais maintenant un test sur mon système avec deux processus, l'un une application Java et l'autre un serveur cfengine. Toutes les 15 minutes, je fais md5sumsur leurs fichiers de cartes. Je vais le laisser fonctionner pendant un jour ou deux et faire rapport ici avec les résultats.
Michael Martinez
@ StéphaneChazelas: Cela fait 16 heures que je vérifie mes deux processus, et aucun changement n'a été apporté dans le md5sum
Michael Martinez le
-1

Une autre façon serait de vérifier l'âge du processus avant de le tuer. De cette façon, vous pouvez vous assurer que vous ne tuez pas un processus qui ne se déclenche pas en moins de 24 heures. Vous pouvez ajouter une ifcondition en fonction de cela avant de tuer le processus.

if [[ $(ps -p $p -o etime=) =~ 1-. ]] ; then
    kill $p
fi

Cette ifcondition vérifie si l'ID de processus $pest inférieur à 24 heures (86 400 secondes).

PS: - La commande ps -p $p -o etime=aura le format<no.of days>-HH:MM:SS

Sree
la source
Le mtimeof /proc/$pn'a rien à voir avec l'heure de début du processus.
Stéphane Chazelas
Merci à StéphaneChazelas. Vous avez raison. J'ai édité la réponse pour changer la ifcondition. S'il vous plaît n'hésitez pas à commenter si son buggy.
Sree
-3

Ce que je fais est, après avoir mis fin au processus, le refait. Chaque fois que je fais cela, la réponse revient, "pas de tel processus"

allenb   12084  5473  0 08:12 pts/4    00:00:00 man man
allenb@allenb-P7812 ~ $ kill -9 12084
allenb@allenb-P7812 ~ $ kill -9 12084
bash: kill: (12084) - No such process
allenb@allenb-P7812 ~ $ 

Ne pourrait pas être plus simple et je fais cela depuis des années sans aucun problème.

Allen
la source
Cela répond à la question "Comment puis-je faire empirer les choses", pas à la question "Comment puis-je résoudre ce problème".
Stéphane Chazelas