Ein Webserver benötigt funktionierende Netzwerk-Interfaces. Eine Datenbank braucht gemountete Dateisysteme. Services starten nicht in beliebiger Reihenfolge - Abhängigkeiten zwischen Diensten bestimmen die Boot-Sequenz. Das Init-System löst diese Abhängigkeiten auf und startet Dienste in korrekter Reihenfolge.
Abhängigkeits-Arten
Service-Abhängigkeiten beschreiben Beziehungen zwischen Diensten. Diese Beziehungen definieren Reihenfolge und Notwendigkeit:
Zeitliche Reihenfolge (Ordering):
Ein Dienst muss vor oder nach einem anderen starten. Ein Webserver sollte nach dem Netzwerk starten - ohne Netzwerk kann er keine Verbindungen annehmen. Die Reihenfolge definiert nur die Sequenz, nicht die Notwendigkeit. Ein Dienst kann nach einem anderen starten, ohne dass der erste erfolgreich sein muss.
Harte Abhängigkeiten (Requirements):
Ein Dienst benötigt zwingend einen anderen. Eine Web-Anwendung die PostgreSQL nutzt, kann ohne laufende Datenbank nicht funktionieren. Wenn die Datenbank nicht startet, darf die Web-Anwendung ebenfalls nicht starten. Harte Abhängigkeiten erzwingen Erfolgsbedingungen.
Weiche Abhängigkeiten (Wants):
Ein Dienst profitiert von einem anderen, benötigt ihn aber nicht zwingend. Ein Logging-Service kann optional einen Remote-Log-Server nutzen. Wenn der Remote-Server nicht verfügbar ist, funktioniert lokales Logging trotzdem. Weiche Abhängigkeiten erlauben Fallbacks.
Konflikte (Conflicts):
Zwei Dienste können nicht gleichzeitig laufen. Ein System mit mehreren DNS-Servern (unbound, dnsmasq) darf nur einen aktivieren - beide würden Port 53 belegen. Konflikte verhindern problematische Kombinationen.
Abhängigkeits-Deklaration
Init-Systeme verwenden unterschiedliche Methoden um Abhängigkeiten zu definieren:
Deklarative Abhängigkeiten (systemd):
Services deklarieren ihre Abhängigkeiten in Konfigurationsdateien. Ein Webserver definiert “After=network.target” und “Requires=postgresql.service”. Das Init-System liest diese Deklarationen und berechnet die Start-Reihenfolge automatisch. Abhängigkeits-Änderungen erfordern keine Code-Änderungen.
Implizite Reihenfolge (SysV-Init):
Nummerierte Verzeichnisse definieren die Start-Sequenz. /etc/rc3.d/S10network startet vor /etc/rc3.d/S20apache2. Die Nummern (10, 20) bestimmen die Reihenfolge. Niedrigere Nummern starten früher. Diese Methode ist einfach aber inflexibel - Reihenfolgen-Änderungen erfordern Umbenennen von Symlinks.
Script-basierte Prüfung (BSD-Init):
Services prüfen in ihren Start-Scripten ob Abhängigkeiten erfüllt sind. Ein Webserver-Script testet ob das Netzwerk verfügbar ist bevor es startet. Diese Methode ist transparent aber erfordert Prüflogik in jedem Script.
Paralleler vs. sequenzieller Start
Die Art wie das Init-System Abhängigkeiten verarbeitet, beeinflusst die Boot-Zeit erheblich:
Sequenzieller Start (SysV-Init, BSD-Init):
Dienste starten nacheinander in fester Reihenfolge. Dienst A startet komplett, dann startet Dienst B, dann Dienst C. Einfach und vorhersehbar, aber langsam. Moderne Hardware mit mehreren CPU-Kernen wird nicht optimal genutzt - nur ein Service startet gleichzeitig.
Ein typischer Server-Boot mit sequenziellem Start:
- Netzwerk initialisieren (2 Sekunden)
- Datenbank starten (3 Sekunden)
- Webserver starten (1 Sekunde)
- SSH-Server starten (0.5 Sekunden)
Gesamtzeit: 6.5 Sekunden, obwohl SSH und Webserver keine Datenbank benötigen.
Paralleler Start (systemd):
Dienste ohne gegenseitige Abhängigkeiten starten gleichzeitig. SSH-Server, Webserver und Logging-Daemon können parallel starten wenn sie sich nicht gegenseitig benötigen. Dieses nutzt moderne Multi-Core-CPUs optimal. Komplexer, aber deutlich schneller.
Derselbe Server-Boot mit parallelem Start:
- Netzwerk initialisieren (2 Sekunden)
- Parallel: Datenbank + SSH + Logging (3 Sekunden - Datenbank ist der langsamste Dienst)
- Webserver startet nach Datenbank (1 Sekunde)
Gesamtzeit: 6 Sekunden. SSH und Logging nutzen die Wartezeit auf die Datenbank.
Socket-Activation und Parallelisierung:
Socket-Activation erlaubt noch aggressivere Parallelisierung. Das Init-System startet Dienste parallel auch wenn Abhängigkeiten bestehen. Ein Dienst der einen anderen benötigt, blockiert bei der ersten Verbindung bis der Abhängige gestartet ist. Komplexeste Methode, schnellster Boot.
Abhängigkeits-Graphen
Die Abhängigkeits-Beziehungen zwischen Services bilden einen gerichteten Graphen. Jeder Dienst ist ein Knoten, jede Abhängigkeit eine Kante:
Graph-Struktur:
Ein einfacher Webserver-Setup als Graph:
network.target
↓
postgresql.service
↓
webapp.service
↓
nginx.service
nginx benötigt webapp, webapp benötigt PostgreSQL, PostgreSQL benötigt Netzwerk. Der Graph zeigt die Abhängigkeits-Kette klar.
Komplexe Graphen:
Reale Systeme haben komplexere Abhängigkeits-Graphen mit Verzweigungen:
network.target
↙ ↓ ↘
sshd postgresql nginx
↓
webapp
Netzwerk ist Voraussetzung für mehrere Dienste. PostgreSQL ist nur für webapp notwendig. SSH und nginx haben keine Datenbank-Abhängigkeit.
Graph-Auflösung:
Das Init-System berechnet aus dem Abhängigkeits-Graphen eine gültige Start-Reihenfolge. Topologisches Sortieren findet eine Sequenz die alle Abhängigkeiten respektiert. Mehrere gültige Reihenfolgen sind möglich - das Init-System wählt eine aus.
Kreisabhängigkeiten
Kreisabhängigkeiten entstehen wenn Dienst A von Dienst B abhängt und Dienst B von Dienst A. Kreise verhindern einen erfolgreichen Start:
Kreis-Erkennung:
Dienst A benötigt Dienst B. Dienst B benötigt Dienst C. Dienst C benötigt Dienst A. Ein Kreis entsteht - kein Dienst kann starten weil jeder auf einen anderen wartet.
A → B → C → A
Keiner der drei Dienste hat erfüllte Voraussetzungen. Das Init-System erkennt solche Kreise und verweigert den Start.
Auflösungs-Strategien:
Abhängigkeiten abschwächen: Eine harte Abhängigkeit (Requires) in weiche Abhängigkeit (Wants) ändern. Der Kreis bleibt, aber Services können starten auch wenn Abhängigkeiten nicht erfüllt sind.
Reihenfolge ohne Abhängigkeit: Zeitliche Reihenfolge (Before/After) ohne Erfolgsbedingung (Requires/Wants). Dienst A startet nach Dienst B, benötigt ihn aber nicht zwingend.
Abhängigkeiten entfernen: Die unnötige Abhängigkeit identifizieren und entfernen. Oft sind Kreisabhängigkeiten Konfigurationsfehler - zwei Dienste die sich gegenseitig benötigen sollten normalerweise ein Dienst sein.
Praktisches Beispiel:
Ein Mail-Server benötigt DNS-Auflösung. Der DNS-Server sendet Status-Mails über den Mail-Server. Ein Kreis entsteht. Lösung: DNS-Server nutzt externen Mail-Server für Status-Mails oder verzichtet auf Mail-Benachrichtigungen bis der Mail-Server verfügbar ist.
Init-System-Unterschiede bei Abhängigkeiten
Die drei großen Init-System-Familien handhaben Abhängigkeiten unterschiedlich:
systemd-Ansatz:
Umfassende Abhängigkeits-Deklaration in Unit-Files. Supports After=, Before=, Requires=, Wants=, Conflicts=, BindsTo=, PartOf=. Automatische Berechnung der Start-Reihenfolge aus Abhängigkeits-Graph. Paralleler Start aller möglichen Services. Socket-Activation für noch aggressivere Parallelisierung. Komplexes System mit maximaler Flexibilität und Geschwindigkeit.
SysV-Init-Ansatz:
Nummerierte Start-Reihenfolge in Runlevel-Verzeichnissen. S10network, S20postgresql, S30nginx - die Nummer bestimmt die Sequenz. Keine expliziten Abhängigkeits-Deklarationen. Strikt sequenzieller Start. Einfach zu verstehen, langsam beim Boot. Abhängigkeits-Änderungen erfordern Symlink-Umbenennungen.
BSD-Init-Ansatz:
rcorder liest Abhängigkeits-Kommentare aus Start-Scripten:
# PROVIDE: webapp
# REQUIRE: postgresql networking
# BEFORE: nginx
rcorder sortiert Services basierend auf diesen Kommentaren. Berechnet gültige Start-Reihenfolge automatisch. Sequenzieller Start aber mit automatischer Reihenfolgen-Berechnung. Kompromiss zwischen systemd-Komplexität und SysV-Einfachheit.
Target-basierte Synchronisation
Targets oder Milestones gruppieren zusammengehörige Services:
Konzept:
Ein Target ist ein virtueller Dienst der “fertig” ist wenn alle abhängigen Services laufen. network.target ist erreicht wenn Netzwerk-Interfaces aktiv sind. multi-user.target ist erreicht wenn alle Multi-User-Services laufen.
Verwendung:
Services deklarieren Abhängigkeit zu Targets statt zu einzelnen Diensten. Ein Webserver benötigt network.target, nicht spezifische Netzwerk-Dienste. Targets abstrahieren Implementierungs-Details - welcher Netzwerk-Dienst läuft ist irrelevant, nur dass Netzwerk funktioniert.
Boot-Sequenz:
Der Boot-Prozess erreicht Targets sequenziell:
- local-fs.target (Dateisysteme gemountet)
- network.target (Netzwerk verfügbar)
- multi-user.target (Multi-User-Services laufen)
- graphical.target (Grafische Oberfläche läuft)
Services können auf jedes passende Target warten. Ein Dienst der Netzwerk benötigt, wartet auf network.target. Ein grafischer Dienst wartet auf graphical.target.
Fehlerbehandlung bei Abhängigkeiten
Wenn abhängige Services nicht starten, muss das Init-System reagieren:
Harte Abhängigkeiten scheitern:
Dienst A hat harte Abhängigkeit (Requires) zu Dienst B. Dienst B startet nicht. Dienst A wird nicht gestartet. Die Abhängigkeits-Kette bricht ab. Logs dokumentieren die fehlgeschlagene Abhängigkeit.
Weiche Abhängigkeiten tolerieren Fehler:
Dienst A hat weiche Abhängigkeit (Wants) zu Dienst B. Dienst B startet nicht. Dienst A startet trotzdem. Die Funktionalität ist möglicherweise eingeschränkt. Logs warnen über die fehlende optionale Abhängigkeit.
Timeout-Behandlung:
Ein Service startet nicht in angemessener Zeit. Das Init-System wartet nicht unbegrenzt - typisch 90 Sekunden Timeout. Nach Timeout gilt der Service als Failed. Abhängige Services starten nicht wenn eine harte Abhängigkeit existiert.
Restart-Propagation:
Ein Service mit vielen abhängigen Services wird neu gestartet. Sollen abhängige Services ebenfalls neu starten? BindsTo= erzwingt Neustart abhängiger Services. Normale Requires= lässt abhängige Services laufen.
Praktische Szenarien und Debugging
Abhängigkeits-Probleme zeigen sich in verschiedenen Situationen:
Dienst startet zu früh:
Ein Webserver startet bevor Netzwerk-Interfaces verfügbar sind. Er bindet an 127.0.0.1 statt an die richtige IP. Lösung: After=network.target zur Service-Konfiguration hinzufügen.
Dienst startet zu spät:
Ein Monitoring-Service startet nach allen anderen Diensten. Er verpasst wichtige Boot-Events. Lösung: Before=multi-user.target setzt - Service startet früher im Boot-Prozess.
Boot hängt:
Das System wartet endlos beim Boot. Ein Service mit langer Start-Zeit blockiert den Boot-Prozess. Logs zeigen welcher Service hängt. Timeout erhöhen oder Service-Start-Zeit optimieren.
Kreisabhängigkeit verhindert Start:
Mehrere Services starten nicht. Logs zeigen “Ordering cycle found”. Abhängigkeits-Graph visualisieren und unnötige Abhängigkeiten entfernen.
Paralleler Start verursacht Races:
Ein Service startet manchmal erfolgreich, manchmal nicht. Race Condition bei parallelem Start. Eine Abhängigkeit fehlt in der Konfiguration. Service startet bevor die Voraussetzung erfüllt ist. Richtige Ordering-Abhängigkeit hinzufügen.
Zusammenfassung der Init-System-Philosophien
Die Behandlung von Abhängigkeiten zeigt die fundamentalen Unterschiede:
systemd: Maximale Flexibilität und Geschwindigkeit durch deklarative Abhängigkeiten und parallelen Start. Komplexes System mit steiler Lernkurve. Optimal für schnelle Boot-Zeiten auf moderner Hardware.
SysV-Init: Einfachheit durch nummerierte Reihenfolge. Vorhersehbar und transparent. Langsam durch sequenziellen Start. Gut für Systeme wo Boot-Zeit unwichtig ist.
BSD-Init: Kompromiss zwischen beiden. Automatische Reihenfolgen-Berechnung aus Script-Kommentaren. Sequenzieller Start aber flexible Konfiguration. Balance zwischen Einfachheit und Features.
Die Wahl des Init-Systems beeinflusst wie Abhängigkeiten definiert, geprüft und aufgelöst werden. Alle drei Ansätze lösen dasselbe Problem - Services in korrekter Reihenfolge starten.
Weiterführende Themen
Diese Serie erklärt distributionsunabhängige Init-Konzepte. Distributions-spezifische Artikel zeigen die konkrete Umsetzung:
- Debian-Grundlagen (geplant): systemd mit Debian-Besonderheiten
- OpenBSD-Grundlagen (geplant): rcctl und rc.d-System
- Arch-Grundlagen (geplant): systemd mit Arch-spezifischen Units
Diese Artikel setzen das Init-Verständnis voraus und zeigen systemspezifische Befehle und Konfigurationen.
Verwendete Software und Versionen
Diese Anleitung bezieht sich auf:
- Unix/Linux-Systeme: Allgemeine Abhängigkeits-Konzepte
- Kontext: Distributionsunabhängig - systemd, SysV-Init, BSD-Init
- Stand: Oktober 2025