Zitieren der Bash-Dokumentation (von man bash
):
JOB CONTROL
Job control refers to the ability to selectively stop
(suspend) the execution of processes and continue (resume)
their execution at a later point. A user typically employs
this facility via an interactive interface supplied jointly
by the operating system kernel's terminal driver and bash.
Also, ganz einfach gesagt, mit set -m
(die Standardeinstellung für interaktive Shells) erlaubt es, eingebaute Funktionen wie fg
zu verwenden und bg
, die unter set +m
deaktiviert würden (der Standardwert für nicht interaktive Shells).
Es ist mir jedoch nicht klar, welche Verbindung zwischen der Jobsteuerung und dem Beenden von Hintergrundprozessen beim Beenden besteht, aber ich kann bestätigen, dass es eine gibt:das Ausführen von set -m; (sleep 10 ; touch control-on) &
erstellt die Datei, wenn man die Shell direkt nach Eingabe dieses Befehls verlässt, aber set +m; (sleep 10 ; touch control-off) &
nicht.
Ich denke, die Antwort liegt in der restlichen Dokumentation für set -m
:
-m Monitor mode. [...] Background pro‐
cesses run in a separate process group and a line con‐
taining their exit status is printed upon their comple‐
tion.
Das bedeutet, dass Hintergrundjobs unter set +m
gestartet wurden sind keine eigentlichen "Hintergrundprozesse" ("Hintergrundprozesse sind solche, deren Prozessgruppen-ID sich von der des Terminals unterscheidet"):Sie haben dieselbe Prozessgruppen-ID wie die Shell, die sie gestartet hat, anstatt ihre eigene Prozessgruppe wie richtige Hintergrundprozesse zu haben. Dies erklärt das beobachtete Verhalten, wenn die Shell vor einigen ihrer Hintergrundjobs beendet wird:Wenn ich das richtig verstehe, wird beim Beenden ein Signal an die Prozesse in derselben Prozessgruppe wie die Shell gesendet (wodurch Hintergrundjobs beendet werden, die unter set +m
gestartet wurden ), aber nicht mit denen anderer Prozessgruppen (wobei echte Hintergrundprozesse, die unter set -m
gestartet wurden, allein bleiben ).
In Ihrem Fall also die startup.sh
Das Skript startet vermutlich einen Hintergrundjob. Wenn dieses Skript nicht interaktiv ausgeführt wird, z. B. über SSH, wie in der von Ihnen verlinkten Frage, ist die Jobsteuerung deaktiviert, der "Hintergrund" -Job teilt sich die Prozessgruppe der Remote-Shell und wird daher beendet, sobald diese Shell beendet wird. Umgekehrt erhält der Hintergrundjob durch Aktivieren von Jobcontrol in dieser Shell seine eigene Prozessgruppe und wird nicht beendet, wenn seine Eltern-Shell beendet wird.
Ich habe dies in der Github-Problemliste gefunden, und ich denke, dies beantwortet Ihre Frage wirklich.
Es ist nicht wirklich ein SSH-Problem, es ist eher das subtile Verhalten um die nicht-interaktiven/interaktiven BASH-Modi und die Signalweitergabe an Prozessgruppen.
Das Folgende basiert auf https://stackoverflow.com/questions/14679178/why-does-ssh-wait-for-my-subshells-without-t-and-kill-them-with-t/14866774#14866774 und http:// www.itp.uzh.ch/~dpotter/howto/daemonize, mit einigen Annahmen, die nicht vollständig validiert sind, aber Tests darüber, wie dies funktioniert, scheinen zu bestätigen.
pty/tty =falsch
Die gestartete Bash-Shell stellt eine Verbindung zu stdout/stderr/stdin des gestarteten Prozesses her und wird so lange ausgeführt, bis nichts mehr an die Sockets angeschlossen ist und ihre untergeordneten Elemente beendet wurden. Ein guter Dämonprozess stellt sicher, dass er nicht darauf wartet, dass seine Kinder beendet werden, einen Kindprozess verzweigt und dann beendet. In diesem Modus wird kein SIGHUP per SSH an den untergeordneten Prozess gesendet. Ich glaube, dass dies für die meisten Skripte korrekt funktionieren wird, die einen Prozess ausführen, der sich selbst dämonisiert und nicht im Hintergrund laufen muss. Wenn Init-Skripte '&' verwenden, um einen Prozess in den Hintergrund zu versetzen, besteht das Hauptproblem wahrscheinlich darin, ob der Hintergrundprozess jemals versucht, von stdin zu lesen, da dies ein SIGHUP auslöst, wenn die Sitzung beendet wurde.
pty/tty =wahr*
Wenn das Init-Skript den gestarteten Prozess in den Hintergrund stellt, gibt die übergeordnete BASHshell einen Exit-Code an die SSH-Verbindung zurück, die wiederum sofort beendet werden soll, da sie nicht auf die Beendigung eines untergeordneten Prozesses wartet und nicht auf stdout/stderr/ blockiert wird. stdin. Dadurch wird ein SIGHUP an die übergeordnete Bash-Shell-Prozessgruppe gesendet, die, da die Jobsteuerung im nicht interaktiven Modus in Bash deaktiviert ist, die gerade gestarteten untergeordneten Prozesse enthält. Wenn ein Daemon-Prozess explizit eine neue Prozesssitzung beim Forking oder im Forking-Prozess startet, erhalten er oder seine untergeordneten Prozesse kein SIGHUP vom Beenden des übergeordneten BASH-Prozesses. Beachten Sie, dass sich dies von ausgesetzten Jobs unterscheidet, bei denen ein SIGTERM angezeigt wird. Ich vermute, dass die Probleme mit dieser Funktion manchmal mit einer leichten Rennbedingung zu tun haben. Wenn Sie sich den Standardansatz zum Deamonisieren von -http://www.itp.uzh.ch/~dpotter/howto/daemonize ansehen, werden Sie sehen, dass im Code die neue Sitzung durch den gegabelten Prozess erstellt wird, der möglicherweise nicht vor dem ausgeführt wird parent wird beendet, was zu dem oben erwähnten zufälligen Erfolgs-/Fehlerverhalten führt. Eine sleep-Anweisung lässt genügend Zeit, damit der gegabelte Prozess eine neue Sitzung erstellt hat, weshalb es in einigen Fällen funktioniert.
pty/tty =true und die Jobsteuerung ist explizit in Bash aktiviert
SSH stellt keine Verbindung zu stdout/stderr/stdin der Bash-Shell oder zu gestarteten untergeordneten Prozessen her, was bedeutet, dass es beendet wird, sobald die übergeordnete Bash-Shell die angeforderten Befehle ausgeführt hat. In diesem Fall ist die Jobsteuerung explizit aktiviert. Alle Prozesse, die von der Bash-Shell mit '&' in den Hintergrund gestartet werden, werden sofort in eine separate Sitzung gestellt und erhalten kein SIGHUP-Signal, wenn der übergeordnete Prozess der BASH-Sitzung beendet wird (in diesem Fall eine SSH-Verbindung).
Behebung erforderlich
Ich denke, die Lösungen müssen nur explizit in der Dokumentation zu run/sudo-Operationen als Sonderfall erwähnt werden, wenn mit Hintergrundprozessen/-diensten gearbeitet wird. Verwenden Sie grundsätzlich entweder 'pty=false', oder, wo dies nicht möglich ist, aktivieren Sie die Jobsteuerung explizit als ersten Befehl, und das Verhalten wird korrekt sein.
Von https://github.com/fabric/fabric/issues/395