SIGHUP verstehen und Hintergrunddienste sauber vom Terminal lösen
nohup ignoriert das Signal SIGHUP, disown entfernt einen Job nachträglich aus der Job-Tabelle der Shell, und setsid startet eine komplett neue Session ohne kontrollierendes Terminal. Wer den Unterschied kennt, wählt für jeden Hintergrunddienst in einem Deployment-Skript das jeweils passende, robusteste Werkzeug.
Inhaltsverzeichnis
- 1. Das Grundproblem: SIGHUP und die Bindung an das kontrollierende Terminal
- 2. nohup im Detail: wie es SIGHUP gezielt abfängt
- 3. disown: einen bereits laufenden Job aus der Job-Tabelle der Shell entfernen
- 4. setsid: eine neue Sitzung ohne kontrollierendes Terminal starten
- 5. Unterschied zwischen vom Terminal getrennt und ohne kontrollierendes Terminal
- 6. Die Kombination nohup, & und disown in der Praxis
- 7. Wann setsid wirklich nötig ist: echte Daemonisierung
- 8. Deployment-Skripte: welches Werkzeug für welchen Hintergrunddienst passt
- 9. Fallstricke: Stream-Umleitung und doppeltes Forking
- 10. Zusammenfassung
- 11. FAQ
1. Das Grundproblem: SIGHUP und die Bindung an das kontrollierende Terminal
Jeder interaktiv gestartete Prozess ist normalerweise an ein kontrollierendes Terminal gebunden, über eine Session, die die Shell beim Login oder beim Öffnen eines Terminal-Fensters aufbaut. Schließt der Benutzer dieses Terminal oder trennt sich eine SSH-Verbindung, sendet der Kernel an alle Prozesse dieser Session das Signal SIGHUP, dessen Standardverhalten ohne expliziten Handler die sofortige Beendigung des Prozesses ist.
Für ein kurzes, interaktives Kommando ist dieses Verhalten meist erwünscht, für einen im Hintergrund gestarteten Deployment-Prozess, der auch nach dem Schließen der SSH-Sitzung weiterlaufen soll, ist es dagegen ein echtes Problem. Genau hier setzen nohup, disown und setsid an, jedes mit einem unterschiedlichen Mechanismus, um einen Prozess vor diesem Signal zu schützen oder ihn erst gar nicht an das Terminal zu binden.
2. nohup im Detail: wie es SIGHUP gezielt abfängt
nohup startet ein Kommando mit einer expliziten Ignorierung des Signals SIGHUP, sodass der Kernel das Signal zwar weiterhin an den Prozess sendet, sobald das Terminal getrennt wird, der Prozess es aber laut seiner Signal-Maske ignoriert und deshalb nicht beendet wird. Standardmäßig leitet nohup zusätzlich stdout und stderr in eine Datei namens nohup.out um, weil ein vom Terminal getrennter Prozess sonst versuchen würde, in ein nicht mehr existierendes Terminal zu schreiben, was zu einem weiteren Signal führen kann.
nohup ändert dabei ausdrücklich nichts an der Prozessgruppen- oder Session-Zugehörigkeit des gestarteten Prozesses, der Prozess bleibt technisch Teil derselben Session wie die startende Shell. Läuft die Shell selbst noch, etwa weil sie in einem tmux-Fenster steckt, bleibt der mit nohup gestartete Prozess trotzdem Kind dieser Shell und wird beendet, falls die Shell selbst mit SIGKILL beendet wird, ein Signal, das sich grundsätzlich nicht ignorieren lässt.
nohup ./long-running-deploy.sh > deploy.log 2>&1 &
deploy_pid=$!
echo "Laeuft als PID $deploy_pid, SIGHUP wird ignoriert"
3. disown: einen bereits laufenden Job aus der Job-Tabelle der Shell entfernen
disown entfernt einen im Hintergrund laufenden Job aus der internen Job-Tabelle der aktuellen Shell, ohne den Prozess selbst zu beeinflussen. Der wichtigste Effekt: Ohne Eintrag in der Job-Tabelle sendet die Shell beim eigenen Beenden kein SIGHUP mehr an diesen Prozess, weil sie ihn schlicht nicht mehr als eigenen Job kennt, während sie ohne disown standardmäßig SIGHUP an alle noch laufenden Hintergrundjobs weiterreicht.
Der entscheidende Unterschied zu nohup: disown wirkt erst nachträglich auf einen bereits gestarteten Hintergrundprozess (mit & gestartet), während nohup schon beim Start eingesetzt werden muss. Wer vergessen hat, einen langlaufenden Prozess mit nohup zu starten, kann disown -h %1 nutzen, um genau diesen Effekt nachträglich zu erreichen, ohne den Prozess neu starten zu müssen.
./long-running-deploy.sh &
deploy_pid=$!
# Realized too late that nohup was forgotten - fix it after the fact
disown -h %1
echo "Job aus der Job-Tabelle entfernt, PID $deploy_pid ueberlebt SIGHUP"
4. setsid: eine neue Sitzung ohne kontrollierendes Terminal starten
setsid startet ein Kommando in einer komplett neuen Session, in der der neue Prozess automatisch zum Session-Leader wird und von vornherein kein kontrollierendes Terminal besitzt. Das ist ein grundlegend anderer Mechanismus als nohup: nohup lässt den Prozess in derselben Session, ignoriert nur das Signal, während setsid den Prozess von der Session der startenden Shell komplett löst, noch bevor überhaupt ein SIGHUP relevant werden könnte.
Weil ein Prozess ohne kontrollierendes Terminal grundsätzlich kein SIGHUP durch eine Terminaltrennung mehr empfangen kann, es gibt schließlich kein Terminal mehr, das dieses Signal auslösen würde, ist setsid der robusteste der drei Mechanismen für echte Hintergrunddienste. Der Nachteil: Ein mit setsid gestarteter Prozess kann nicht mehr über die normale Job-Kontrolle der Shell angesprochen werden, weil er formal keiner interaktiven Shell-Session mehr angehört.
setsid ./deploy-daemon.sh > deploy.log 2>&1 < /dev/null &
echo "Neue Session gestartet, kein kontrollierendes Terminal"
5. Unterschied zwischen vom Terminal getrennt und ohne kontrollierendes Terminal
nohup und disown lösen ein anderes Problem als setsid, auch wenn alle drei häufig im selben Atemzug genannt werden. nohup und disown sorgen dafür, dass ein Prozess ein SIGHUP übersteht oder es gar nicht erst empfängt, ändern aber nichts daran, dass der Prozess weiterhin formal Mitglied der ursprünglichen Session ist, mit demselben kontrollierenden Terminal, falls dieses noch existiert.
setsid dagegen entfernt das kontrollierende Terminal von vornherein, unabhängig davon, ob es später getrennt wird oder nicht. Dieser Unterschied wird relevant, sobald ein Prozess selbst versucht, mit dem Terminal zu interagieren, etwa eine Passwortabfrage auf stdin auszugeben: Ein Prozess ohne kontrollierendes Terminal scheitert an solchen Interaktionen von Anfang an, ein nur mit nohup geschützter Prozess dagegen so lange nicht, wie sein Terminal tatsächlich existiert.
6. Die Kombination nohup, & und disown in der Praxis
In den meisten Deployment-Skripten reicht die Kombination aus nohup beim Start und dem Hintergrund-Operator & völlig aus, um einen Prozess sicher über das Ende einer SSH-Sitzung hinweg laufen zu lassen. disown wird in der Praxis vor allem dann gebraucht, wenn nohup aus irgendeinem Grund vergessen wurde oder wenn ein interaktiv gestarteter Job nachträglich, ohne Neustart, von der aktuellen Shell abgekoppelt werden soll.
Ein robustes Muster für ein Deployment-Skript kombiniert deshalb nohup für den SIGHUP-Schutz mit einer expliziten Umleitung von stdin, stdout und stderr, damit der Prozess unter keinen Umständen versucht, mit dem möglicherweise schon geschlossenen Terminal zu interagieren, selbst wenn nohup allein das SIGHUP-Signal bereits abfängt.
nohup ./deploy-worker.sh < /dev/null > deploy.log 2>&1 &
worker_pid=$!
disown "$worker_pid" 2>/dev/null || true
echo "Worker $worker_pid laeuft unabhaengig von dieser Sitzung"
7. Wann setsid wirklich nötig ist: echte Daemonisierung
Für die meisten Deployment-Skripte, die einen einmaligen Hintergrundprozess starten, reichen nohup und & aus, weil moderne Deployment-Umgebungen ohnehin selten Prozesse dauerhaft an eine interaktive SSH-Sitzung binden wollen. setsid wird dagegen unverzichtbar, sobald ein Prozess echte Daemon-Eigenschaften braucht: keinerlei Bindung an ein Terminal, keine versehentliche Job-Kontrolle durch eine Shell, und eine eigene, unabhängige Session, die ihn zum Beispiel vor SIGHUP-Ketten durch Terminal-Multiplexer wie tmux vollständig abschirmt.
In produktiven systemd-basierten Umgebungen übernimmt systemd selbst bereits die Rolle von setsid für jeden Dienst, den es startet, weshalb explizites setsid in solchen Skripten meist überflüssig ist. Relevant bleibt setsid vor allem für Legacy-Deployment-Skripte ohne systemd-Integration oder für Fälle, in denen ein Prozess bewusst aus einem interaktiven Cron- oder CI-Kontext heraus vollständig von der aufrufenden Session gelöst werden muss.
8. Deployment-Skripte: welches Werkzeug für welchen Hintergrunddienst passt
Für kurzlebige Hintergrundprozesse innerhalb eines interaktiven Deployment-Skripts, die den Rest des Skriptlaufs überdauern sollen, reicht in der Regel nohup in Kombination mit &, ergänzt um eine explizite Umleitung aller Standard-Streams. Für nachträgliches Abkoppeln eines bereits laufenden Jobs, den man vergessen hat mit nohup zu schützen, ist disown das richtige, gezielte Werkzeug, ohne den Prozess neu starten zu müssen.
Für echte, langlebige Hintergrunddienste, die dauerhaft und unabhängig von jeder interaktiven Sitzung laufen sollen, ist eine systemd-Unit-Datei die eigentlich richtige Lösung, weil sie Neustarts, Logging und Ressourcenlimits einheitlich regelt. setsid bleibt das passende Werkzeug für den Übergangsfall, in dem echte Daemonisierung außerhalb einer systemd-Umgebung nötig ist, etwa in minimalen Container-Basisimages ohne eigenen Init-Prozess.
9. Fallstricke: Stream-Umleitung und doppeltes Forking
Der häufigste Fehler bei allen drei Werkzeugen ist, stdin nicht explizit umzuleiten. Ein Hintergrundprozess, der versucht, von stdin zu lesen, während das ursprüngliche Terminal längst geschlossen ist, blockiert unter Umständen unendlich oder erhält beim Lesen ein Fehlersignal, je nachdem, wie der jeweilige Prozess mit einem geschlossenen Deskriptor umgeht, weshalb < /dev/null in produktiven Skripten praktisch immer dazugehört.
Ein zweiter, subtilerer Fallstrick betrifft doppeltes Forking bei echter Daemonisierung: Ein mit setsid gestarteter Prozess ist zwar Session-Leader ohne Terminal, kann aber unter bestimmten Umständen selbst wieder ein Terminal öffnen und damit erneut ein kontrollierendes Terminal erhalten. Klassische Unix-Daemons vermeiden das durch einen zweiten fork() nach setsid, sodass der eigentliche Daemon-Prozess selbst kein Session-Leader mehr ist und deshalb gar kein Terminal mehr erhalten kann, ein Detail, das setsid allein nicht abdeckt.
| Werkzeug | Zeitpunkt der Anwendung | Löst SIGHUP | Trennt Terminal komplett |
|---|---|---|---|
| nohup | Beim Start des Prozesses | Ja, durch Ignorieren | Nein, Session bleibt bestehen |
| disown | Nach dem Start, nachträglich | Ja, durch Entfernen aus Job-Tabelle | Nein, Session bleibt bestehen |
| setsid | Beim Start, neue Session | Ja, indirekt | Ja, von Anfang an kein Terminal |
| nohup + disown | Start und nachträglich kombiniert | Ja, doppelt abgesichert | Nein, aber praktisch robust |
Mironsoft
Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur
Shell-Skripte, die in der Produktion zuverlässig laufen?
Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.
Code-Review
ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.
Refactoring
Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.
CI-Integration
ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.
10. Zusammenfassung
nohup, disown und setsid: Das Wichtigste auf einen Blick
SIGHUP-Ursache
Terminaltrennung sendet SIGHUP an alle Prozesse der Session, Standardverhalten ist sofortige Beendigung.
nohup
Ignoriert SIGHUP gezielt beim Start des Prozesses, lässt die Session-Zugehörigkeit aber unverändert.
disown
Entfernt einen bereits laufenden Job nachträglich aus der Job-Tabelle, ohne den Prozess neu zu starten.
setsid
Startet eine komplett neue Session ohne kontrollierendes Terminal, robustester Mechanismus für echte Daemons.