Für Langläufer
Gelegenheitsadministratoren aus dem Windows-Umfeld dürfte der Unix-/Linux-Befehl nohup wenig sagen. Doch es ist ein praktisches Progrämmchen - allerdings mit einigen Ecken und Kanten, die es beim Einsatz zu beachten gilt.
- Oliver Böhm
Oft sind es die kleinen Dinge, die man im Nachhinein für völlig trivial hält, die das Leben schwer machen. Etwa wenn der im Windows-Umfeld durchaus kundige Administrator, bevor er in den Feierabend geht, noch „mal eben schnell“ einen Application Server wie JBoss auf einem Unix-, Linux- oder MacOS-X-Rechner starten soll. Statt einer Diensteverwaltung existiert dazu meistens im bin-Verzeichnis der Anwendung ein Start-Skript:
bin/run.sh
Im Falle von JBoss erscheint die Ausgabe dieses Applikation-Servers auf der Konsole, sprich in dem Terminal, in dem JBoss gestartet wurde. Wenn der Administrator am nächsten Tag nach seinem JBoss schaut, muss er feststellen, dass der längst nicht mehr läuft. Denn er hat sich ordnungsgemäß von seinem PC abgemeldet, und damit auch vom Unix-Server, und so alle Anwendungen, die er dort gestartet hatte, beendet.
Daran ändert auch ein Start im Hintergrund durch ein angehängtes „&“ nichts (bin/run.sh &), ebensowenig hilft die Umlenkung von Standardausgaben und Fehlermeldungen (bin/run.sh > jboss.log 2> jboss.err &).
Das Problem ist, dass die Login-Shell ein Weiterleben der gestarteten Programme, der Kindprozesse, nach ihrem Ableben verhindert. Wenn man sich explizit oder implizit (z. B. durch Beenden des Terminal-Fensters) abmeldet, schickt sie allen laufenden Prozesse, die von ihr gestartet wurden, ein SIGHUP-Signal (Terminal Hangup) und beendet sie damit. Normalerweise ist das auch gut so - schließlich ist Unix ein Mehrbenutzer-Betriebssystem, das rund um die Uhr läuft, und wenn sich jemand abmeldet, besteht kein Grund, seine Prozesse weiterlaufen zu lassen und das System zu belasten.
Immer im Hintergrund
Doch ist dieses Verhalten nicht immer gewünscht. Daher können Programme durch Signalhandler SIGHUP und anderes abfangen, was zum Beispiel Unix-„Daemonen“ (entspricht „Diensten“ unter Windows) nutzen. Oder man stellt das nohup-Kommando („no hangup“) voran und lässt es so im Hintergrund laufen:
nohup bin/run.sh &
Das schützt das Kommando bin/run.sh vor dem SIGHUP-Signal am Ende der Sitzung. (Übrigens zumindest unter Linux nicht vor den Signalen SIGINT und SIGQUIT, wie dies gelegentlich in der Literatur zu finden ist.) Gleichzeitig leitet es die (Standard- und Fehler-) Ausgabe in die Datei nohup.out oder in $HOME/nohup.out, wenn keine Schreibberechtigung im aktuellen Arbeitsverzeichnis existiert.
Shell-Umleitungen bleiben im Prinzip wirksam. nohup bin/run.sh > all.out& generiert kein nohup.out, sondern schreibt Standardausgaben und Fehlermeldungen in all.out. nohup bin/run.sh 1>std.out & schreibt ebenfalls alles in std.out, nohup bin/run.sh 2>err.out& die Fehler in err.out, Standardausgaben in nohup.out. Wirklich logisch ist das nicht, weil nämlich bei bin/run.sh 1>std.out die Fehlermeldungen auf der Konsole erscheinen und nur die Standardausgaben in std.out gehen. Vor allem beim „Pipen“ von Ausgaben muss man hier aufpassen.
Zum Beenden stellt JBoss, wie andere Server, ein Shutdown-Skript bereit, meist shutdown.sh oder stop.sh:
bin/shutdown.sh
Existiert so ein Skript nicht, gilt: Prozessnummer heraussuchen und anschließend den gewünschten Prozess „killen“. Gesucht wird nach „jboss“, nicht nach „nohup“ - nohup ersetzt sich selbst durch das zu schützende Programm, weil es dieses mit execve aufruft:
ps -ef | grep -i jbosskill 4711
kill, direkt gefolgt von der Prozessnummer, sendet das Signal SIGTERM (-1). Wenn danach der Prozess immer noch läuft, kann man zum großen Holzhammer greifen und den Prozess mit SIGKILL gewaltsam beenden:
kill -9 4711
Damit wird dieser Prozess auf jeden Fall vom System entfernt. Aber Achtung: Während bei einem normalen kill der Prozess noch einen letzten Wunsch äußern kann - der vi nutzt dies zum Beispiel, um noch schnell ein Backup anzulegen -, trifft ihn das SIGKILL-Signal völlig unvorbereitet, weshalb man es nur in Notfällen einsetzen sollte. Statt der Signalnummern kann man auch die Namen als Argument verwenden, also: kill -TERM 4711 oder kill -KILL 4711. (js)