Supposons que j'ai un processus qui engendre exactement un processus enfant. Maintenant, lorsque le processus parent se termine pour une raison quelconque (normalement ou anormalement, par kill, ^ C, affirmer l'échec ou toute autre chose), je veux que le processus enfant meure. Comment faire ça correctement?
Une question similaire sur stackoverflow:
- (demandé plus tôt) Comment puis-je provoquer la fermeture d'un processus enfant lorsque le parent le fait?
- (demandé plus tard) Les processus enfants créés avec fork () sont-ils automatiquement supprimés lorsque le parent est tué?
Une question similaire sur stackoverflow pour Windows :
prctl()
sans condition de course. Btw, la réponse liée par Maxim est incorrecte.man prctl
dit: Définissez le signal de mort du processus parent du processus appelant sur arg2 (soit une valeur de signal dans la plage 1..maxsig, soit 0 pour effacer). C'est le signal que le processus appelant recevra lorsque son parent décède. Cette valeur est effacée pour l'enfant d'un fork (2) et (depuis Linux 2.4.36 / 2.6.23) lors de l'exécution d'un binaire set-user-ID ou set-group-ID.J'essaie de résoudre le même problème, et puisque mon programme doit fonctionner sur OS X, la solution Linux uniquement ne fonctionnait pas pour moi.
Je suis arrivé à la même conclusion que les autres personnes sur cette page - il n'y a pas de moyen compatible POSIX d'avertir un enfant quand un parent décède. J'ai donc jeté un coup d'œil à la prochaine meilleure chose - avoir le sondage des enfants.
Lorsqu'un processus parent meurt (pour une raison quelconque), le processus parent de l'enfant devient le processus 1. Si l'enfant interroge simplement périodiquement, il peut vérifier si son parent est 1. Si tel est le cas, l'enfant doit quitter.
Ce n'est pas génial, mais cela fonctionne, et c'est plus facile que les solutions d'interrogation de socket / fichier de verrouillage TCP suggérées ailleurs sur cette page.
la source
gettpid()
ne devient pas 1 mais obtient lepid
planificateur de zone (processuszsched
).J'ai réalisé cela dans le passé en exécutant le code "original" dans "l'enfant" et le code "engendré" dans le "parent" (c'est-à-dire: vous inversez le sens habituel du test après
fork()
). Puis piéger SIGCHLD dans le code "engendré" ...Peut-être pas possible dans votre cas, mais mignon quand cela fonctionne.
la source
Si vous ne parvenez pas à modifier le processus enfant, vous pouvez essayer quelque chose comme ceci:
Cela exécute l'enfant à partir d'un processus shell avec le contrôle des travaux activé. Le processus enfant est généré en arrière-plan. Le shell attend une nouvelle ligne (ou un EOF) puis tue l'enfant.
Lorsque le parent décède - quelle qu'en soit la raison - il ferme son extrémité du tuyau. Le shell enfant obtiendra un EOF de la lecture et procédera à la suppression du processus enfant en arrière-plan.
la source
dup2
et reprendre stdin en utilisant l'read -u
indicateur pour lire à partir d'un descripteur de fichier spécifique. J'ai également ajouté unsetpgid(0, 0)
dans l'enfant pour l'empêcher de sortir en appuyant sur ^ C dans le terminal.dup2()
appel est incorrect. Si vous souhaitez utiliserpipes[0]
comme stdin, vous devez écrire à ladup2(pipes[0], 0)
place dedup2(0, pipes[0])
. C'estdup2(oldfd, newfd)
là que l'appel ferme un newfd précédemment ouvert.Sous Linux, vous pouvez installer un signal de mort parentale chez l'enfant, par exemple:
Notez que le stockage de l'ID de processus parent avant le fork et le test dans l'enfant après
prctl()
élimine une condition de concurrence entreprctl()
et la sortie du processus qui a appelé l'enfant.Notez également que le signal de décès des parents de l'enfant est effacé chez les enfants nouvellement créés. Il n'est pas affecté par un
execve()
.Ce test peut être simplifié si nous sommes certains que le processus système chargé de l'adoption de tous les orphelins a le PID 1:
S'appuyer sur ce processus système étant
init
et ayant PID 1 n'est pas portable, cependant. POSIX.1-2008 spécifie :Traditionnellement, le processus système adoptant tous les orphelins est PID 1, c'est-à-dire init - qui est l'ancêtre de tous les processus.
Sur les systèmes modernes comme Linux ou FreeBSD, un autre processus pourrait avoir ce rôle. Par exemple, sous Linux, un processus peut appeler
prctl(PR_SET_CHILD_SUBREAPER, 1)
pour s'établir comme processus système qui hérite de tous les orphelins de l'un de ses descendants (cf. un exemple sur Fedora 25).la source
init(8)
processus .... la seule chose que vous pouvez supposer est que lorsqu'un processus parent meurt, c'est que son ID parent changera. Cela se produit en fait une fois dans la vie d'un processus ... et c'est lorsque le parent du processus décède. Il n'y a qu'une seule exception principale à cela, et c'est pour lesinit(8)
enfants, mais vous êtes protégé contre cela, commeinit(8)
jamaisexit(2)
(panique du noyau dans ce cas)Par souci d'exhaustivité. Sur macOS, vous pouvez utiliser kqueue:
la source
NSTask
ou spawn posix. Voir lastartTask
fonction dans mon code ici: github.com/neoneye/newton-commander-browse/blob/master/Classes/…Le processus enfant a-t-il un canal vers / depuis le processus parent? Si c'est le cas, vous recevrez un SIGPIPE si vous écrivez, ou vous obtiendrez EOF lors de la lecture - ces conditions pourraient être détectées.
la source
Inspiré par une autre réponse ici, j'ai trouvé la solution tout-POSIX suivante. L'idée générale est de créer un processus intermédiaire entre le parent et l'enfant, qui a un seul but: remarquer quand le parent meurt et tuer explicitement l'enfant.
Ce type de solution est utile lorsque le code de l'enfant ne peut pas être modifié.
Il y a deux petites mises en garde avec cette méthode:
En passant, le code que j'utilise est en Python. Le voici pour être complet:
la source
Je ne pense pas qu'il soit possible de garantir que l'utilisation uniquement des appels POSIX standard. Comme dans la vraie vie, une fois qu'un enfant est né, il a sa propre vie.
Il est possible que le processus parent intercepte la plupart des événements de terminaison possibles et tente de tuer le processus enfant à ce stade, mais il y en a toujours qui ne peuvent pas être interceptés.
Par exemple, aucun processus ne peut attraper a
SIGKILL
. Lorsque le noyau gère ce signal, il va tuer le processus spécifié sans aucune notification à ce processus.Pour étendre l'analogie - la seule autre façon standard de le faire est de se suicider lorsque l'enfant découvre qu'il n'a plus de parent.
Il existe une façon de faire uniquement avec Linux
prctl(2)
- voir d'autres réponses.la source
Comme d'autres personnes l'ont souligné, s'appuyer sur le pid parent pour devenir 1 lorsque le parent sort n'est pas portable. Au lieu d'attendre un ID de processus parent spécifique, attendez simplement que l'ID change:
Ajoutez un micro-sommeil comme vous le souhaitez si vous ne souhaitez pas interroger à pleine vitesse.
Cette option me semble plus simple que d'utiliser un tuyau ou de s'appuyer sur des signaux.
la source
getpid()
se fait dans le parent avant d'appelerfork()
. Si le parent décède avant, l'enfant n'existe pas. Ce qui peut arriver, c'est que l'enfant vit son parent pendant un certain temps.Installez un gestionnaire de pièges pour attraper SIGINT, ce qui tue votre processus enfant s'il est encore en vie, bien que d'autres affiches soient correctes pour qu'il n'attrape pas SIGKILL.
Ouvrez un .lockfile avec un accès exclusif et demandez au sondage enfant d'essayer de l'ouvrir - si l'ouverture réussit, le processus enfant devrait quitter
la source
Cette solution a fonctionné pour moi:
Il s'agissait d'un processus de type travailleur dont l'existence n'avait de sens que lorsque le parent était vivant.
la source
Je pense qu'un moyen rapide et sale est de créer un tuyau entre l'enfant et le parent. À la sortie du parent, les enfants recevront un SIGPIPE.
la source
Certaines affiches ont déjà mentionné les pipes et
kqueue
. En fait, vous pouvez également créer une paire de sockets de domaine Unix connectés par l'socketpair()
appel. Le type de socket doit êtreSOCK_STREAM
.Supposons que vous ayez les deux descripteurs de fichier socket fd1, fd2. Maintenant,
fork()
pour créer le processus enfant, qui héritera des fds. Dans le parent, vous fermez fd2 et chez l'enfant, vous fermez fd1. Maintenant, chaque processus peutpoll()
ouvrir le fd restant de sa propre extrémité pour l'POLLIN
événement. Tant que chaque côté n'a pas explicitementclose()
son fd pendant la durée de vie normale, vous pouvez être assez sûr qu'unPOLLHUP
indicateur doit indiquer la terminaison de l'autre (que ce soit propre ou non). Dès notification de cet événement, l'enfant peut décider quoi faire (par exemple mourir).Vous pouvez essayer de compiler le code de validation de principe ci-dessus et l'exécuter dans un terminal comme
./a.out &
. Vous avez environ 100 secondes pour expérimenter la destruction du PID parent par divers signaux, ou il se fermera simplement. Dans les deux cas, vous devriez voir le message "enfant: parent raccroché".Par rapport à la méthode utilisant le
SIGPIPE
gestionnaire, cette méthode ne nécessite pas d'essayer l'write()
appel.Cette méthode est également symétrique , c'est-à-dire que les processus peuvent utiliser le même canal pour surveiller leur existence mutuelle.
Cette solution appelle uniquement les fonctions POSIX. J'ai essayé cela sous Linux et FreeBSD. Je pense que cela devrait fonctionner sur d'autres Unix, mais je n'ai pas vraiment testé.
Voir également:
unix(7)
des pages de manuel Linux,unix(4)
FreeBSD,poll(2)
,socketpair(2)
,socket(7)
sous Linux.la source
Sous Posix , les
exit()
,_exit()
et les_Exit()
fonctions sont définies à:Ainsi, si vous vous arrêtez pour que le processus parent soit un processus de contrôle pour son groupe de processus, l'enfant doit recevoir un signal SIGHUP lorsque le parent quitte. Je ne suis pas absolument sûr que cela se produise lorsque le parent se bloque, mais je pense que oui. Certes, pour les cas non-crash, cela devrait fonctionner correctement.
Notez que vous devrez peut-être lire beaucoup de petits caractères - y compris la section Définitions de base (définitions), ainsi que les informations sur les services système pour
exit()
etsetsid()
etsetpgrp()
- pour obtenir l'image complète. (Moi aussi!)la source
Si vous envoyez un signal au pid 0, en utilisant par exemple
ce signal est envoyé à l'ensemble du groupe de traitement, tuant ainsi efficacement l'enfant.
Vous pouvez le tester facilement avec quelque chose comme:
Si vous appuyez ensuite sur ^ D, vous verrez le texte
"Terminated"
comme une indication que l'interpréteur Python a bien été tué, au lieu d'être simplement quitté à cause de la fermeture de stdin.la source
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
imprime joyeusement 4 au lieu de TerminéDans le cas où cela est pertinent pour quelqu'un d'autre, lorsque je génère des instances JVM dans des processus enfants bifurqués à partir de C ++, la seule façon dont je pouvais obtenir que les instances JVM se terminent correctement après la fin du processus parent était de faire ce qui suit. J'espère que quelqu'un pourra fournir des commentaires dans les commentaires si ce n'était pas la meilleure façon de le faire.
1) Appelez
prctl(PR_SET_PDEATHSIG, SIGHUP)
le processus enfant forké comme suggéré avant de lancer l'application Java viaexecv
, et2) Ajoutez un hook d'arrêt à l'application Java qui interroge jusqu'à ce que son PID parent soit égal à 1, puis faites un effort
Runtime.getRuntime().halt(0)
. L'interrogation se fait en lançant un shell séparé qui exécute laps
commande (Voir: Comment trouver mon PID en Java ou JRuby sous Linux? ).EDIT 130118:
Il semble que ce n'était pas une solution solide. Je lutte toujours un peu pour comprendre les nuances de ce qui se passe, mais je recevais parfois des processus JVM orphelins lors de l'exécution de ces applications dans des sessions écran / SSH.
Au lieu d'interroger le PPID dans l'application Java, j'ai simplement demandé au hook d'arrêt d'effectuer un nettoyage suivi d'un arrêt brutal comme ci-dessus. Ensuite, j'ai veillé à invoquer
waitpid
dans l'application parent C ++ sur le processus enfant généré lorsqu'il était temps de tout terminer. Cela semble être une solution plus robuste, car le processus enfant s'assure qu'il se termine, tandis que le parent utilise les références existantes pour s'assurer que ses enfants se terminent. Comparez cela à la solution précédente qui avait mis fin au processus parent chaque fois qu'il le souhaitait, et avait demandé aux enfants de déterminer s'ils étaient orphelins avant de terminer.la source
PID equals 1
attente n'est pas valide. Le nouveau parent pourrait être un autre PID. Vous devez vérifier s'il passe du parent d'origine (getpid () avant le fork ()) au nouveau parent (getppid () dans l'enfant différent de getpid () lorsqu'il est appelé avant le fork ()).Une autre façon de faire cela qui est spécifique à Linux est de créer le parent dans un nouvel espace de noms PID. Ce sera alors PID 1 dans cet espace de noms, et quand il le quittera, tous ses enfants seront immédiatement tués avec
SIGKILL
.Malheureusement, pour créer un nouvel espace de noms PID, vous devez avoir
CAP_SYS_ADMIN
. Mais, cette méthode est très efficace et ne nécessite aucun changement réel pour le parent ou les enfants au-delà du lancement initial du parent.Voir clone (2) , pid_namespaces (7) et unshare (2) .
la source
Si le parent décède, le PPID des orphelins passe à 1 - il vous suffit de vérifier votre propre PPID. D'une certaine manière, c'est un sondage, mentionné ci-dessus. voici une pièce de coquille pour ça:
la source
J'ai trouvé 2 solutions, toutes deux pas parfaites.
1.Tuez tous les enfants en les tuant (-pid) lorsqu'ils reçoivent le signal SIGTERM.
Évidemment, cette solution ne peut pas gérer "kill -9", mais elle fonctionne dans la plupart des cas et est très simple car elle n'a pas besoin de se souvenir de tous les processus enfants.
De la même manière, vous pouvez installer le gestionnaire 'exit' comme ci-dessus si vous appelez process.exit quelque part. Remarque: Ctrl + C et un crash soudain ont été automatiquement traités par OS pour tuer le groupe de processus, donc plus ici.
2.Utilisez chjj / pty.js pour lancer votre processus avec le terminal de contrôle connecté.
Lorsque vous tuez le processus en cours de toute façon même par -9, tous les processus enfants seront également automatiquement tués (par le système d'exploitation?). Je suppose que parce que le processus actuel tient un autre côté du terminal, donc si le processus actuel meurt, le processus enfant obtiendra SIGPIPE donc meurt.
la source
J'ai réussi à faire une solution portable, sans polling avec 3 processus en abusant du contrôle du terminal et des sessions. C'est de la masturbation mentale, mais ça marche.
L'astuce est:
De cette façon:
Lacunes:
la source
Même si 7 ans se sont écoulés, je viens de rencontrer ce problème car j'exécute l'application SpringBoot qui doit démarrer webpack-dev-server pendant le développement et doit le tuer lorsque le processus backend s'arrête.
J'essaie d'utiliser
Runtime.getRuntime().addShutdownHook
mais cela a fonctionné sur Windows 10 mais pas sur Windows 7.Je l'ai changé pour utiliser un thread dédié qui attend la fin du processus ou
InterruptedException
qui semble fonctionner correctement sur les deux versions de Windows.la source
Historiquement, à partir d'UNIX v7, le système de processus a détecté l'orphelinité des processus en vérifiant l'ID parent d'un processus. Comme je l'ai dit, historiquement, le
init(8)
processus système est un processus spécial pour une seule raison: il ne peut pas mourir. Il ne peut pas mourir car l'algorithme du noyau pour gérer l'attribution d'un nouvel identifiant de processus parent dépend de ce fait. lorsqu'un processus exécute son processus, le processus est devenu orphelin avant l'appel système.exit(2)
appel (au moyen d'un appel système de processus ou par une tâche externe comme lui envoyant un signal ou similaire), le noyau réaffecte à tous les enfants de ce processus l'ID du processus init en tant qu'ID de processus parent. Cela conduit au test le plus simple et au moyen le plus portable de savoir si un processus est devenu orphelin. Vérifiez simplement le résultat dugetppid(2)
appel système et s'il s'agit de l'ID de processus duinit(2)
Deux problèmes émergent de cette approche qui peuvent conduire à des problèmes:
init
processus en n'importe quel processus utilisateur, alors comment pouvons-nous garantir que le processus init sera toujours le parent de tous les processus orphelins? Eh bien, dans leexit
code d'appel système, il y a une vérification explicite pour voir si le processus exécutant l'appel est le processus init (le processus avec pid égal à 1) et si c'est le cas, le noyau panique (il ne devrait plus être en mesure de maintenir hiérarchie des processus), il n'est donc pas permis au processus init de passer unexit(2)
appel.1
, mais cela n'est pas garanti par l'approche POSIX, qui déclare (comme exposé dans d'autres réponses) que seul l'ID de processus d'un système est réservé à cette fin. Presque aucune implémentation posix ne fait cela, et vous pouvez supposer dans les systèmes dérivés Unix d'origine1
que la réponse à l'getppid(2)
appel système suffit pour supposer que le processus est orphelin. Une autre façon de vérifier est de fairegetppid(2)
juste après le fork et de comparer cette valeur avec le résultat d'un nouvel appel. Cela ne fonctionne tout simplement pas dans tous les cas, car les deux appels ne sont pas atomiques ensemble, et le processus parent peut mourir aprèsfork(2)
et avant le premiergetppid(2)
appel système. Laparent id only changes once, when its parent does an
sortie de processus (2)call, so this should be enough to check if the
getppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of
init (8) `, mais vous pouvez supposer en toute sécurité ces processus comme n'ayant aucun parent non plus (sauf lorsque vous remplacez dans un système le processus init)la source
J'ai transmis le pid parent en utilisant l'environnement à l'enfant, puis vérifié périodiquement si / proc / $ ppid existe à partir de l'enfant.
la source