Modul 3: Prozesskommunikation, Hierarchien & Deadlocks – Der galaktische Stillstand

1. Einleitung: Kein Prozess lebt im luftleeren Raum

Willkommen zu Tag 3! Wir wissen nun, dass das Betriebssystem das Universum in handliche, kleine Illusionen namens Prozesse unterteilt. Doch ein Prozess allein ist wie ein Vogonisches Raumschiff ohne Formular für den Hyperraumantrieb – er kommt nicht weit.

In modernen Multitasking-Betriebssystemen teilen sich laufende Prozesse die vorhandenen Ressourcen[cite: 1]. Bei gleichzeitigem Zugriff zweier Prozesse auf dieselbe Ressource kann es zu schwer lokalisierbaren Fehlern kommen[cite: 1]. Prozesse müssen koordiniert werden – sie müssen eine Hierarchie bilden, miteinander reden und dabei aufpassen, dass sie sich nicht in einem unendlichen Deadlock gegenseitig aus dem Universum aussperren.

2. Die Genealogie der Prozesse: Eltern, Kinder und Waisen

Prozesse entstehen in der Linux/Unix-Welt nicht aus dem Nichts. Sie werden geboren – oder vielmehr geklont. Ein Prozess kann einen neuen Prozess starten (in Unix klassischerweise durch die Systemaufrufe fork oder spawn)[cite: 1]. Wir nennen den erzeugenden Prozess den Elternprozess (Parent Process) und den neu erschaffenen Prozess den Kindprozess (Child Process)[cite: 1].

Das Skript definiert hierzu strenge Stammbaum-Regeln[cite: 1]:

Prozesshierarchie

Praxis-Szenario: Wenn ihr euch per SSH auf einem RHEL 9.2 Server einloggt, startet der SSH-Daemon (sshd) einen Kindprozess für eure Shell (z.B. bash). Tippt ihr dort top ein, ruft die Bash fork() auf, erzeugt ein Kind und überschreibt dessen Speicherbereich via exec() mit dem Programm top. Schießt ihr eure Bash (den Elternprozess) ab, stirbt auch euer top-Prozess.

2.1 Identität und Prozessrechte

Jeder dieser Prozesse benötigt Berechtigungen, um auf Dateien oder Sockets zuzugreifen. Im Verlauf seiner Existenz besitzt ein Prozess zwei wesentliche Benutzeridentifikationen[cite: 1]:

Wie ändert sich diese effektive ID? Wenn ein Programm besondere Rechte braucht (z.B. um das eigene Passwort in der geschützten Datei /etc/shadow zu ändern), greift ein genialer Unix-Mechanismus: das setuid-Bit. Falls die Zugriffsrechte einer ausführbaren Programmdatei das setuid-Bit enthalten, wird die effektive Benutzer-ID des laufenden Prozesses einfach mit der Eigentümerkennung des Programms überschrieben[cite: 1]. Der Prozess hat dann plötzlich die Rechte des Eigentümers (z.B. root), solange er läuft[cite: 1].

3. Inter-Process Communication (IPC): Wie Prozesse miteinander sprechen

Wir wissen, dass das Betriebssystem den RAM mithilfe der Memory Management Unit (MMU) virtuell zerlegt. Jeder Prozess lebt in seinem völlig isolierten, virtuellen Adressraum. Das ist hervorragend für die Sicherheit, aber ein Albtraum für die Kommunikation.

Für eine intelligente und fehlerfreie Synchronisierung paralleler Prozesse ist eine effektive Kommunikation zwingend notwendig[cite: 1]. Unser Handout definiert hierfür mehrere primäre Kommunikationskanäle[cite: 1]:

4. Synchronisation: Die Kunst des Ausweichens

Wenn zwei Prozesse über Shared Memory miteinander kommunizieren, entsteht ein Risiko. Stellen wir uns vor, Prozess A und Prozess B wollen beide den Status eines Checkmk-Agenten in denselben Speicherbereich schreiben. Wenn beide gleichzeitig schreiben, entsteht Datensalat.

Wegen dieser Abhängigkeiten müssen kooperierende Multitaskingprozesse synchronisiert werden[cite: 1]. Das Skript unterscheidet zwei Klassen von Abhängigkeiten[cite: 1]:

5. Deadlocks: Der absolute Stillstand

Wenn die Synchronisation fehlschlägt, betreten wir die dunkle Seite der Informatik: den Deadlock. Ein Deadlock (Verklemmung) ist ein Zustand, in dem mehrere Prozesse dauerhaft aufeinander warten, weil jeder eine Ressource hält, die der andere zwingend benötigt.

Welche Prozesse sind an einem Deadlock beteiligt? Es sind jene Prozesse, die mehrere Betriebsmittel mit jeweils exklusivem Zugriff benötigen[cite: 1]. Damit ein Deadlock überhaupt entstehen kann, müssen laut Systemtheorie vier notwendige Bedingungen gleichzeitig erfüllt sein[cite: 1]:

1. **Mutual Exclusion (Wechselseitiger Ausschluss):** Ressourcen können nicht geteilt werden (z. B. über Druckerspooler aufgelöst)[cite: 1].
2. **Belegungs- und Wartebedingung (Hold and Wait):** Ein Prozess hält bereits Ressourcen und darf weitere anfordern[cite: 1].
3. **Unterbrechbarkeitsbedingung (No Preemption):** Dem Prozess können einmal zugeteilte Betriebsmittel nicht gewaltsam vom System entzogen werden[cite: 1].
4. **Zyklische Wartebedingung (Circular Wait):** Es entsteht ein geschlossener Zyklus im Betriebsmittelgraph (A wartet auf B, B wartet auf C, C wartet auf A)[cite: 1].

Zusätzlich gibt es die hinreichende Bedingung, dass keine externe Betriebsmittelinstanz (die den Knoten auflösen könnte) bestehen darf[cite: 1].

5.1 Verhungern im Livelock

Eine perfide Abwandlung ist der Livelock[cite: 1]. Hier wurde ein Betriebsmittel vergeben, und es besteht eigentlich keine logische Verhinderung[cite: 1]. Die Ressourcen könnten irgendwann freigegeben werden, aber der Zeitpunkt ist völlig unbekannt[cite: 1]. Ein klassisches Beispiel ist striktes Prioritätsscheduling, bei dem langwierige, niedrig priorisierte Prozesse schlichtweg „verhungern“ (Starvation), weil sie immer wieder durch höher priorisierte Prozesse benachteiligt werden[cite: 1].

6. Die Bekämpfung von Deadlocks

Wie bewahren wir unsere Server vor dem totalen Freeze? Das Skript liefert vier klassische Vorgehensweisen, um Deadlocks aufzulösen oder zu umgehen[cite: 1]:

1. **Der Vogel-Strauß-Algorithmus:** Einfach ignorieren![cite: 1] (Klingt absurd, ist aber in Desktop-Systemen oft der Standard, da die mathematische Vorabprüfung extrem rechenintensiv ist).
2. **Erkennen und Beheben:** Das System erlaubt Deadlocks, baut aber z. B. regelmäßig den Graphen aller Anforderungen, erkennt Zyklen topologisch und beseitigt sie durch den **Abbruch** der beteiligten Prozesse und die sofortige Freigabe der belegten Betriebsmittel (ähnlich wie optimistische Sperrverfahren bei Datenbanken)[cite: 1].
3. **Konzeptionelles Vermeiden:** Eine der vier notwendigen Bedingungen wird durch das Design komplett eliminiert[cite: 1].
4. **Dynamisches Verhindern:** Vor jeder Zuteilung wird dynamisch geprüft, ob das System in einen Deadlock geraten könnte[cite: 1].

6.1 Dynamisches Verhindern: Der Bankieralgorithmus

Eines der berühmtesten Verfahren zur Erkennung sicherer Systemzustände bei der Ressourcenverteilung ist der Banker-Algorithmus (Bankieralgorithmus)[cite: 1]. Das Prinzip ist dem echten Leben entlehnt: Ein Banker hat eine bestimmte Menge an Ressourcen (z. B. 10 Einheiten)[cite: 1]. Jeder Kunde (Prozess) hat ein festgelegtes Kreditlimit, bis zu dem er Ressourcen anfordern darf[cite: 1]. Der Banker achtet darauf, dass er immer so viele Ressourcen in der Hinterhand behält, dass er das größte vorhandene Limit gerade noch bedienen kann[cite: 1].

Ein Kunde bekommt seine angeforderte Ressource nur dann, falls der Banker danach noch genügend Einheiten hat, um mindestens einem der Kunden sein komplettes Limit zuzuteilen[cite: 1]. * Das Problem der Realität: Jeder Prozess müsste im Voraus exakt wissen, wie viele Einheiten eines Betriebsmittels er maximal während seiner Abarbeitung benötigen wird[cite: 1]. In einem offenen Linux-Betriebssystem ist das schlichtweg unmöglich.

6.2 Deadlock-Erkennung durch Preclaiming

Ein ähnliches Konzept ist das Preclaiming, bekannt aus Datenbanksystemen (DBMS)[cite: 1]. Ein Prozess darf erst dann seinen nächsten Schritt ausführen, wenn alle von ihm angeforderten Betriebsmittel vollständig verfügbar sind[cite: 1]. Algorithmisch bedeutet dies eine zyklische Prüfung (für jeden Prozess), ob die erforderlichen Betriebsmittel vorhanden sind[cite: 1].

6.3 Vermeidung durch Lineare Ordnung

Was passiert, wenn wir Betriebsmittel linear nach Prioritäten ordnen[cite: 1]? Prozesse können zwar alle Betriebsmittel anfordern, müssen sich aber zwingend an die Nummerierungsreihenfolge halten[cite: 1]. Wenn ein Prozess ein Betriebsmittel höherer Ordnung besitzt, ist es ihm ausgeschlossen, ein Betriebsmittel niedrigerer Ordnung anzufordern, das bereits von einem anderen Prozess belegt ist[cite: 1]. Damit wird ein zyklisches Warten (und somit die vierte Deadlock-Bedingung) schlichtweg unmöglich[cite: 1]! Das Problem liegt hier in der Praxis: Es ist extrem schwer, reale und abstrakte Betriebsmittel (wie Festplatten-Spoolbereiche) sauber zu ordnen und zu nummerieren[cite: 1].


Quellen & Materialien
Alle fachlichen Grundlagen dieses Moduls basieren auf dem Skript „Betriebssysteme“ zur Erlangung des Grades Bachelor (German-Baltic Management School).[cite: 1]