Angenommen, ich habe einen Prozess, der genau einen untergeordneten Prozess erzeugt. Wenn nun der übergeordnete Prozess aus irgendeinem Grund beendet wird (normalerweise oder abnormal, durch Töten, ^ C, Fehler behaupten oder irgendetwas anderes), möchte ich, dass der untergeordnete Prozess stirbt. Wie mache ich das richtig?
Einige ähnliche Fragen zum Stackoverflow:
- (früher gefragt) Wie kann ich veranlassen, dass ein untergeordneter Prozess beendet wird, wenn der übergeordnete Prozess dies tut?
- (später gefragt) Werden mit fork () erstellte untergeordnete Prozesse automatisch beendet, wenn das übergeordnete Element beendet wird?
Einige ähnliche Fragen zum Stackoverflow für Windows :
prctl()
. Übrigens ist die von Maxim verknüpfte Antwort falsch.man prctl
sagt: Setzen Sie das Todessignal des übergeordneten Prozesses des aufrufenden Prozesses auf arg2 (entweder einen Signalwert im Bereich 1..maxsig oder 0 zum Löschen). Dies ist das Signal, das der aufrufende Prozess erhält, wenn sein übergeordnetes Element stirbt. Dieser Wert wird für das untergeordnete Element einer Verzweigung (2) und (seit Linux 2.4.36 / 2.6.23) gelöscht, wenn eine Binärdatei für die Set-User-ID oder die Set-Group-ID ausgeführt wird.Ich versuche, das gleiche Problem zu lösen, und da mein Programm unter OS X ausgeführt werden muss, hat die Nur-Linux-Lösung für mich nicht funktioniert.
Ich bin zu dem gleichen Schluss gekommen wie die anderen Personen auf dieser Seite - es gibt keine POSIX-kompatible Möglichkeit, ein Kind zu benachrichtigen, wenn ein Elternteil stirbt. Also habe ich das nächstbeste herausgefunden - die Kinderumfrage.
Wenn ein übergeordneter Prozess (aus irgendeinem Grund) stirbt, wird der übergeordnete Prozess des Kindes zu Prozess 1. Wenn das Kind einfach regelmäßig abfragt, kann es überprüfen, ob sein übergeordneter Prozess 1 ist. Wenn dies der Fall ist, sollte das Kind beendet werden.
Das ist nicht großartig, aber es funktioniert und ist einfacher als die an anderer Stelle auf dieser Seite vorgeschlagenen TCP-Socket- / Lockfile-Polling-Lösungen.
quelle
gettpid()
nicht 1, sondern erhält daspid
des Zonenplaners (Prozesseszsched
).Ich habe dies in der Vergangenheit erreicht, indem ich den "ursprünglichen" Code im "Kind" und den "gespawnten" Code im "Elternteil" ausgeführt habe (das heißt: Sie kehren den üblichen Sinn des Tests danach um
fork()
). Dann fangen Sie SIGCHLD im "gespawnten" Code ein ...Möglicherweise nicht in Ihrem Fall möglich, aber süß, wenn es funktioniert.
quelle
Wenn Sie den untergeordneten Prozess nicht ändern können, können Sie Folgendes versuchen:
Dadurch wird das untergeordnete Element in einem Shell-Prozess mit aktivierter Jobsteuerung ausgeführt. Der untergeordnete Prozess wird im Hintergrund erzeugt. Die Shell wartet auf eine neue Zeile (oder einen EOF) und tötet dann das Kind.
Wenn der Elternteil stirbt - egal aus welchem Grund - schließt er sein Rohrende. Die untergeordnete Shell erhält eine EOF aus dem Lesevorgang und beendet den untergeordneten Hintergrundprozess.
quelle
dup2
stdin vermeiden und übernehmen, indem Sie dasread -u
Flag verwenden, um aus einem bestimmten Dateideskriptor zu lesen. Ich habesetpgid(0, 0)
dem Kind auch ein a hinzugefügt , um zu verhindern, dass es beim Drücken von ^ C im Terminal beendet wird.dup2()
Aufrufs ist falsch. Wenn Siepipes[0]
als Standard verwenden möchten, müssen Siedup2(pipes[0], 0)
statt schreibendup2(0, pipes[0])
. Hier schließtdup2(oldfd, newfd)
der Anruf eine zuvor geöffnete neue Datei.Unter Linux können Sie ein untergeordnetes Todessignal im Kind installieren, z.
Beachten Sie, dass durch das Speichern der übergeordneten Prozess-ID vor dem Fork und das Testen im untergeordneten Prozess
prctl()
eine Racebedingung zwischenprctl()
und dem Beenden des Prozesses, der das untergeordnete Element aufgerufen hat, beseitigt wird .Beachten Sie auch, dass das elterliche Todessignal des Kindes bei neu erstellten eigenen Kindern gelöscht wird. Es ist nicht betroffen von einem
execve()
.Dieser Test kann vereinfacht werden, wenn wir sicher sind, dass der Systemprozess, der für die Adoption aller Waisenkinder verantwortlich ist, PID 1 hat:
Sich auf diesen Systemprozess zu verlassen
init
und PID 1 zu haben, ist jedoch nicht portabel.POSIX.1-2008 spezifiziert :Traditionell ist der Systemprozess, der alle Waisenkinder annimmt, PID 1, dh init - der Vorfahr aller Prozesse.
Auf modernen Systemen wie Linux oder FreeBSD könnte ein anderer Prozess diese Rolle spielen. Unter Linux kann ein Prozess beispielsweise aufrufen
prctl(PR_SET_CHILD_SUBREAPER, 1)
, um sich als Systemprozess zu etablieren, der alle Waisenkinder eines seiner Nachkommen erbt (vgl. Ein Beispiel zu Fedora 25).quelle
init(8)
Prozess ist. Das einzige, was Sie annehmen können, ist, dass sich die übergeordnete ID ändert, wenn ein übergeordneter Prozess stirbt. Dies geschieht tatsächlich einmal im Leben eines Prozesses ... und dann, wenn der Elternteil des Prozesses stirbt. Es gibt nur eine Hauptausnahme und ist fürinit(8)
Kinder, aber Sie sind davor geschützt, wieinit(8)
nieexit(2)
(Kernel-Panik in diesem Fall)Der Vollständigkeit halber. Unter macOS können Sie kqueue verwenden:
quelle
NSTask
oder posix spawn. Sehen Sie diestartTask
Funktion in meinem Code hier: github.com/neoneye/newton-commander-browse/blob/master/Classes/…Hat der untergeordnete Prozess eine Pipe zum / vom übergeordneten Prozess? In diesem Fall erhalten Sie beim Schreiben ein SIGPIPE oder beim Lesen eine EOF - diese Bedingungen können erkannt werden.
quelle
Inspiriert von einer anderen Antwort hier, habe ich die folgende All-POSIX-Lösung entwickelt. Die allgemeine Idee besteht darin, einen Zwischenprozess zwischen dem Elternteil und dem Kind zu erstellen, der einen Zweck hat: Beachten Sie, wenn der Elternteil stirbt, und töten Sie das Kind explizit.
Diese Art von Lösung ist nützlich, wenn der Code im untergeordneten Element nicht geändert werden kann.
Bei dieser Methode gibt es zwei kleine Einschränkungen:
Abgesehen davon ist der eigentliche Code, den ich verwende, in Python. Hier ist es der Vollständigkeit halber:
quelle
Ich glaube nicht, dass es möglich ist, zu garantieren, dass nur Standard-POSIX-Aufrufe verwendet werden. Wie im wirklichen Leben hat ein Kind, sobald es geboren ist, ein Eigenleben.
Es ist möglich, dass der übergeordnete Prozess die meisten möglichen Beendigungsereignisse abfängt und versucht, den untergeordneten Prozess zu diesem Zeitpunkt zu beenden, aber es gibt immer einige, die nicht abgefangen werden können.
Zum Beispiel kann kein Prozess a fangen
SIGKILL
. Wenn der Kernel dieses Signal verarbeitet, wird der angegebene Prozess ohne Benachrichtigung an diesen Prozess abgebrochen.Um die Analogie zu erweitern - die einzige andere Standardmethode besteht darin, dass das Kind Selbstmord begeht, wenn es feststellt, dass es keinen Elternteil mehr hat.
Es gibt nur eine Linux-Möglichkeit, dies zu tun
prctl(2)
- siehe andere Antworten.quelle
Wie andere Leute bereits betont haben, ist es nicht portabel, sich darauf zu verlassen, dass die Eltern-PID 1 wird, wenn die Eltern aussteigen. Anstatt auf eine bestimmte übergeordnete Prozess-ID zu warten, warten Sie einfach, bis sich die ID ändert:
Fügen Sie nach Wunsch einen Mikroschlaf hinzu, wenn Sie nicht mit voller Geschwindigkeit abfragen möchten.
Diese Option erscheint mir einfacher als die Verwendung einer Pipe oder die Verwendung von Signalen.
quelle
getpid()
Vorgang wird vor dem Anruf im übergeordneten Element ausgeführtfork()
. Wenn der Elternteil vorher stirbt, existiert das Kind nicht. Was passieren kann, ist, dass das Kind eine Weile bei den Eltern lebt.Installieren Sie einen Trap-Handler , um SIGINT zu fangen, der Ihren untergeordneten Prozess abbricht, wenn er noch lebt, obwohl andere Poster korrekt sind, dass er SIGKILL nicht fängt.
Öffnen Sie eine .lock-Datei mit exklusivem Zugriff und lassen Sie die untergeordnete Umfrage versuchen, sie zu öffnen. Wenn das Öffnen erfolgreich ist, sollte der untergeordnete Prozess beendet werden
quelle
Diese Lösung hat bei mir funktioniert:
Dies war für einen arbeiterartigen Prozess gedacht, dessen Existenz nur zu Lebzeiten der Eltern Sinn machte.
quelle
Ich denke, ein schneller und schmutziger Weg besteht darin, eine Verbindung zwischen Kind und Eltern herzustellen. Wenn Eltern gehen, erhalten Kinder ein ZEICHEN.
quelle
Einige Plakate haben bereits Pfeifen und erwähnt
kqueue
. Tatsächlich können Sie durch den Aufruf auch ein Paar verbundener Unix-Domain-Sockets erstellensocketpair()
. Der Sockeltyp sollte seinSOCK_STREAM
.Angenommen, Sie haben die beiden Socket-Dateideskriptoren fd1, fd2. Nun ,
fork()
das Kind Prozess zu schaffen, die die fds erben. Im Elternteil schließen Sie fd2 und im Kind schließen Sie fd1. Jetzt kann jeder Prozesspoll()
das verbleibende offene fd an seinem eigenen Ende für dasPOLLIN
Ereignis. Solange jede Seiteclose()
während der normalen Lebensdauer nicht explizit ihren fd hat, können Sie ziemlich sicher sein, dass einPOLLHUP
Flag die Beendigung des anderen anzeigt (egal ob sauber oder nicht). Nach Benachrichtigung über dieses Ereignis kann das Kind entscheiden, was zu tun ist (z. B. um zu sterben).Sie können versuchen, den obigen Proof-of-Concept-Code zu kompilieren und in einem Terminal wie auszuführen
./a.out &
. Sie haben ungefähr 100 Sekunden Zeit, um mit dem Abschalten der übergeordneten PID durch verschiedene Signale zu experimentieren, oder sie wird einfach beendet. In beiden Fällen sollte die Meldung "Kind: Eltern aufgelegt" angezeigt werden.Im Vergleich zur Methode mit dem
SIGPIPE
Handler muss für diese Methode derwrite()
Aufruf nicht ausgeführt werden.Diese Methode ist auch symmetrisch , dh die Prozesse können denselben Kanal verwenden, um die Existenz des anderen zu überwachen.
Diese Lösung ruft nur die POSIX-Funktionen auf. Ich habe dies unter Linux und FreeBSD versucht. Ich denke, es sollte unter anderen Unixen funktionieren, aber ich habe es nicht wirklich getestet.
Siehe auch:
unix(7)
von Linux - man - Seiten,unix(4)
für FreeBSD,poll(2)
,socketpair(2)
,socket(7)
auf Linux.quelle
Unter POSIX , die
exit()
,_exit()
und_Exit()
werden Funktionen definiert:Wenn Sie also festlegen, dass der übergeordnete Prozess ein steuernder Prozess für seine Prozessgruppe ist, sollte das untergeordnete Element beim Beenden des übergeordneten Prozesses ein SIGHUP-Signal erhalten. Ich bin mir nicht sicher, ob das passiert, wenn die Eltern abstürzen, aber ich denke, das passiert. Sicherlich sollte es für Fälle ohne Absturz gut funktionieren.
Beachten Sie, dass Sie möglicherweise viel Kleingedrucktes lesen müssen - einschließlich des Abschnitts Basisdefinitionen (Definitionen) sowie der Systemdienstinformationen für
exit()
undsetsid()
undsetpgrp()
-, um ein vollständiges Bild zu erhalten. (Würde ich auch!)quelle
Wenn Sie beispielsweise ein Signal an die PID 0 senden
Dieses Signal wird an die gesamte Prozessgruppe gesendet, wodurch das Kind effektiv getötet wird.
Sie können es einfach mit etwas wie testen:
Wenn Sie dann ^ D drücken, wird der Text
"Terminated"
als Hinweis darauf angezeigt, dass der Python-Interpreter tatsächlich getötet wurde, anstatt nur beendet zu werden, weil stdin geschlossen wurde.quelle
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
druckt glücklich 4 statt beendetFalls es für andere relevant ist, wenn ich JVM-Instanzen in gegabelten untergeordneten Prozessen aus C ++ spawne, war die einzige Möglichkeit, die JVM-Instanzen nach Abschluss des übergeordneten Prozesses ordnungsgemäß zu beenden, die folgenden Schritte. Hoffentlich kann jemand in den Kommentaren Feedback geben, wenn dies nicht der beste Weg ist, dies zu tun.
1) Rufen Sie
prctl(PR_SET_PDEATHSIG, SIGHUP)
den gegabelten untergeordneten Prozess wie vorgeschlagen auf, bevor Sie die Java-App überexecv
und starten2) Fügen Sie der Java-Anwendung einen Shutdown-Hook hinzu, der abfragt, bis die übergeordnete PID gleich 1 ist, und führen Sie dann eine harte Aktion durch
Runtime.getRuntime().halt(0)
. Die Abfrage erfolgt durch Starten einer separaten Shell, in der derps
Befehl ausgeführt wird (siehe: Wie finde ich meine PID in Java oder JRuby unter Linux? ).EDIT 130118:
Es scheint, dass dies keine robuste Lösung war. Ich habe immer noch Probleme, die Nuancen der Vorgänge zu verstehen, aber manchmal bekam ich immer noch verwaiste JVM-Prozesse, wenn diese Anwendungen in Bildschirm- / SSH-Sitzungen ausgeführt wurden.
Anstatt die PPID in der Java-App abzufragen, ließ ich einfach den Shutdown-Hook eine Bereinigung durchführen, gefolgt von einem harten Stopp wie oben. Dann habe ich sichergestellt, dass
waitpid
in der übergeordneten C ++ - App der erzeugte untergeordnete Prozess aufgerufen wird, wenn es Zeit war, alles zu beenden. Dies scheint eine robustere Lösung zu sein, da der untergeordnete Prozess sicherstellt, dass er beendet wird, während der übergeordnete Prozess vorhandene Verweise verwendet, um sicherzustellen, dass seine untergeordneten Prozesse beendet werden. Vergleichen Sie dies mit der vorherigen Lösung, bei der der übergeordnete Prozess jederzeit beendet wurde und die Kinder versuchen, herauszufinden, ob sie vor dem Beenden verwaist waren.quelle
PID equals 1
Warten ist ungültig. Das neue übergeordnete Element könnte eine andere PID sein. Sie sollten überprüfen, ob es vom ursprünglichen übergeordneten Element (getpid () vor dem fork ()) zum neuen übergeordneten Element (getppid () im untergeordneten Element (getppid ()) wechselt, wenn es vor dem fork () aufgerufen wird.Eine andere Linux-spezifische Möglichkeit besteht darin, das übergeordnete Element in einem neuen PID-Namespace zu erstellen. Es wird dann PID 1 in diesem Namespace sein, und wenn es beendet wird, werden alle seine Kinder sofort mit getötet
SIGKILL
.Leider müssen Sie haben, um einen neuen PID-Namespace zu erstellen
CAP_SYS_ADMIN
. Diese Methode ist jedoch sehr effektiv und erfordert keine wirklichen Änderungen an den Eltern oder den Kindern über den ersten Start des Elternteils hinaus.Siehe Klon (2) , pid_namespaces (7) und Unshare (2) .
quelle
Wenn ein Elternteil stirbt, ändert sich die PPID von Waisenkindern auf 1 - Sie müssen nur Ihre eigene PPID überprüfen. In gewisser Weise handelt es sich um eine Umfrage, die oben erwähnt wurde. Hier ist das Muschelstück dafür:
quelle
Ich habe 2 Lösungen gefunden, beide nicht perfekt.
1. Töten Sie alle Kinder durch Töten (-pid), wenn Sie das SIGTERM-Signal erhalten.
Offensichtlich kann diese Lösung "kill -9" nicht verarbeiten, funktioniert jedoch in den meisten Fällen und ist sehr einfach, da nicht alle untergeordneten Prozesse gespeichert werden müssen.
Auf die gleiche Weise können Sie den 'exit'-Handler wie oben beschrieben installieren, wenn Sie process.exit irgendwo aufrufen. Hinweis: Strg + C und plötzlicher Absturz wurden vom Betriebssystem automatisch verarbeitet, um die Prozessgruppe zu beenden, daher hier nicht mehr.
2.Verwenden Sie chjj / pty.js , um Ihren Prozess mit angeschlossenem Steuerungsterminal zu erzeugen .
Wenn Sie den aktuellen Prozess ohnehin mit -9 beenden, werden auch alle untergeordneten Prozesse automatisch beendet (vom Betriebssystem?). Ich vermute, dass der aktuelle Prozess SIGPIPE erhält, wenn der aktuelle Prozess eine andere Seite des Terminals hält. Wenn der aktuelle Prozess stirbt, stirbt er.
quelle
Ich habe es geschafft, eine tragbare Lösung ohne Abfrage mit drei Prozessen zu erstellen, indem ich die Terminalsteuerung und Sitzungen missbraucht habe. Das ist mentale Masturbation, funktioniert aber.
Der Trick ist:
Dieser Weg:
Mängel:
quelle
Obwohl 7 Jahre vergangen sind, bin ich gerade auf dieses Problem gestoßen, als ich die SpringBoot-Anwendung ausführe, die den Webpack-Dev-Server während der Entwicklung starten und beenden muss, wenn der Backend-Prozess stoppt.
Ich versuche zu verwenden,
Runtime.getRuntime().addShutdownHook
aber es funktionierte unter Windows 10, aber nicht unter Windows 7.Ich habe es so geändert, dass ein dedizierter Thread verwendet wird, der darauf wartet, dass der Prozess beendet wird, oder
InterruptedException
der auf beiden Windows-Versionen ordnungsgemäß zu funktionieren scheint.quelle
In der Vergangenheit hat das Prozesssystem unter UNIX v7 die Verwaistung von Prozessen durch Überprüfen der übergeordneten ID eines Prozesses erkannt. Wie ich bereits sagte, ist der
init(8)
Systemprozess historisch gesehen nur aus einem Grund ein besonderer Prozess: Er kann nicht sterben. Es kann nicht sterben, da der Kernel-Algorithmus zum Zuweisen einer neuen übergeordneten Prozess-ID von dieser Tatsache abhängt. Wenn ein Prozess seinenexit(2)
Aufruf ausführt (mittels eines Prozesssystemaufrufs oder einer externen Task als Signal oder dergleichen), weist der Kernel allen untergeordneten Elementen dieses Prozesses die ID des Init-Prozesses als übergeordnete Prozess-ID zu. Dies führt zu dem einfachsten Test und der portabelsten Methode, um festzustellen, ob ein Prozess verwaist ist. Überprüfen Sie einfach das Ergebnis desgetppid(2)
Systemaufrufs und ob es sich um die Prozess-ID des handeltinit(2)
Prozess dann wurde der Prozess vor dem Systemaufruf verwaist.Aus diesem Ansatz ergeben sich zwei Probleme, die zu Problemen führen können:
init
Prozess in einen beliebigen Benutzerprozess zu ändern. Wie können wir also sicherstellen, dass der Init-Prozess immer übergeordnet zu allen verwaisten Prozessen ist? Nun, imexit
Systemaufrufcode wird explizit geprüft, ob der Prozess, der den Aufruf ausführt, der Init-Prozess ist (der Prozess mit einer PID von 1), und wenn dies der Fall ist, gerät der Kernel in Panik (er sollte nicht mehr gewartet werden können die Prozesshierarchie), so dass der Init-Prozess keinenexit(2)
Aufruf ausführen darf .1
wird jedoch durch den POSIX-Ansatz nicht gerechtfertigt, der besagt (wie in einer anderen Antwort angegeben), dass nur die Prozess-ID eines Systems für diesen Zweck reserviert ist. Fast keine Posix-Implementierung tut dies, und Sie können in ursprünglichen, von Unix abgeleiteten Systemen davon ausgehen, dass eine1
Antwort auf einengetppid(2)
Systemaufruf ausreicht, um anzunehmen, dass der Prozess verwaist ist. Eine andere Möglichkeit, dies zu überprüfen, besteht darin,getppid(2)
direkt nach der Abzweigung einen Wert zu erstellen und diesen Wert mit dem Ergebnis eines neuen Aufrufs zu vergleichen. Dies funktioniert einfach nicht in allen Fällen, da beide Aufrufe nicht zusammen atomar sind und der übergeordnete Prozess nach demfork(2)
und vor dem erstengetppid(2)
Systemaufruf abbrechen kann. Der Prozess beendetparent id only changes once, when its parent does an
(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) `, aber Sie können davon ausgehen, dass diese Prozesse auch keine übergeordneten Prozesse haben (außer wenn Sie den init-Prozess in einem System ersetzen).quelle
Ich habe die übergeordnete PID mithilfe der Umgebung an das untergeordnete Element übergeben und dann regelmäßig überprüft, ob / proc / $ ppid vom untergeordneten Element vorhanden ist.
quelle