Softwarelösungen: Versuch 4 Nichtstun in Schleife wird ersetzt durch zeitweilige Zurücknahme der Anforderung, um es anderen Prozessen zu erlauben, die Ressource zu belegen: /* Prozess 0 */ wiederhole flag[0] := true; solange (flag[1] = true) flag[0] := false; /* zufäll.verzög. */; flag[0] := true; /* kritischer Abschnitt */ flag[0] := false; /* nichtkrit. Abschnitt */ /* Prozess 1 */ wiederhole flag[1] := true; solange (flag[0] = true) flag[1] := false; /* zufäll.verzög. */; flag[1] := true; /* kritischer Abschnitt */ flag[1] := false; /* nichtkrit. Abschnitt */
Softwarelösungen: Versuch 4 Vorteil: Auch nicht-alternierender Zugriff auf kritischen Abschnitt. Wegen zufälliger Verzögerung nur geringe Wahrscheinlichkeit für Deadlock. Nachteil: Busy waiting Deadlock nach wie vor möglich! Unsaubere Lösung! Verhalten ist schlecht vorhersagbar Es gibt Situationen, in denen es nie voran geht es für sehr lange Zeit nicht voran geht Nach Beobachtung eines unerwünschten Verhaltens tritt dieses möglicherweise über sehr lange Zeit nicht mehr auf -> kritische Wettläufe!
Softwarelösungen: Versuch 5 3 gemeinsame Variablen: turn, flag[0], flag[1] Initialisierung: flag[0] := false; flag[1] := false; turn beliebig Variable turn bestimmt, welcher Prozess auf seiner Anforderung bestehen darf. /* Prozess 0 */ wiederhole flag[0] := true; turn := 1; solange (flag[1] = true und turn = 1) tue nichts; /* kritischer Abschnitt */ flag[0] := false; /* nichtkrit. Abschnitt */ /* Prozess 1 */ wiederhole flag[1] := true; turn := 0; solange (flag[0] = true und turn = 0) tue nichts; /* kritischer Abschnitt */ flag[1] := false; /* nichtkrit. Abschnitt */
Softwarelösungen: Versuch 5 Deadlock Kein Deadlock möglich wegen Variable turn! turn kann nicht gleichzeitig 0 und 1 sein. Wechselseitiger Ausschluss Sei o.b.d.a. Prozess 1 im kritischen Abschnitt zum Zeitpunkt t 0 und Prozess 0 verlässt die solange -Schleife und geht auch in den kritischen Abschnitt. 2 Fälle: 1.turn = 0 2.flag[1] = false Nicht-alternierender Zugriff Möglich, weil flag[i] = false, wenn Prozess i in nicht-kritischem Abschnitt.
Softwarelösungen: Versuch 5 Beweis: Wechselseitiger Ausschluss Fall 1: turn = 0 (zum Zeitpunkt t0) t1: Zeitpunkt, an dem P1 zum letzten Mal solange -Schleife verlassen hat. t2: P1 führt zum letzten Mal turn = 0 aus. t3: P1 führt zum letzten Mal flag[1] = true aus. t2': P0 führt zum letzten Mal turn = 1 aus. t3': P0 führt zum letzten Mal flag[0] = true aus. Es gilt t2' < t2 da sonst turn = 0 zum Zeitpunkt t0 und t3' < t2'. Damit: t3' < t2' < t2 < t1 turn=1 (P0) turn=0 (P1) P0 geht in k.a. t3' t2' t2 t1 t0 P1 in k.a.
Softwarelösungen: Versuch 5 Beweis (Fortsetzung Fall1): nach t3' kommt keine Anweisung mehr an flag[0] und nach t2 kommt keine Anweisung mehr an turn Somit: Zum Zeitpunkt t1 ist flag[0] = true und turn = 0. Damit ist zum Zeitpunkt t1 aber die solange -Schleife in P1 nicht verlassen worden. -> Widerspruch!
Softwarelösungen: Versuch 5 Beweis (Fortsetzung): Fall 2: flag[1] = false Wann kann flag[1] = false gesetzt werden? -> nur an der Stelle (*) in P1 (und bei der Initialisierung)! /* Prozess 0 */ wiederhole flag[0] := true; turn := 1; solange (flag[1] = true und turn = 1) tue nichts; /* kritischer Abschnitt */ flag[0] := false; /* nichtkrit. Abschnitt */ /* Prozess 1 */ wiederhole flag[1] := true; turn := 0; solange (flag[0] = true und turn = 0) tue nichts; /* kritischer Abschnitt */ flag[1] := false; (*) /* nichtkrit. Abschnitt */
Softwarelösungen: Versuch 5 Beweis (Fortsetzung Fall 2: flag[1] = false) t1: Zeitpunkt, an dem P1 zum letzten Mal solange -Schleife verlassen hat. t2: P1 führt zum letzten Mal turn = 0 aus. t3: P1 führt zum letzten Mal flag[1] = true aus. D.h. flag[1] = false vor Zeitpunkt t3. Dann wird aber flag[1] = true. Da aber nach Annahme P1 im kritischen Abschnitt, muss flag[1] = true zum Zeitpunkt t0 weiterhin gelten. -> Widerspruch! flag[1] = false flag[1] = true P0 geht in k.a. t3 t2 t1 t0 P1 in k.a.
Softwarelösungen: Versuch 5 Wechselseitiger Ausschluss Sei o.b.d.a. Prozess 1 im kritischen Abschnitt zum Zeitpunkt t 0 und Prozess 0 verlässt die solange -Schleife und geht auch in den kritischen Abschnitt. 2 Fälle: 1.turn = 0 2.flag[1] = false Für beide Fälle wurde gezeigt: Es kann nicht sein, dass P0 in den kritischen Abschnitt geht, wenn P1 bereits drin ist. Aus Symmetriegründen folgt: P0 und P1 sind nie gleichzeitig im kritischen Abschnitt.
Softwarelösungen: Versuch 5 Vorteile: Nicht-alternierender Zugriff auf den kritischen Abschnitt W echselseitiger Ausschluss garantiert Kein Deadlock Nachteil: Aktives W arten! Versuch 5 entspricht Petersons Algorithmus für wechselseitigen Ausschluss (1981). Verallgemeinerbar auf n Prozesse (wesentlich komplizierter!).
Softwarelösungen: Zusammenfassung Wechselseitiger Ausschluss ist in Software schwer zu realisieren. Alles was einfacher ist als Petersons Algorithmus ist höchstwahrscheinlich falsch. Beweise des Gegenteils sind durchaus willkommen Fehler durch kritische Wettläufe, subtile Fehler Formale Beweise sind unabdingbar! Software-Lösungen für wechselseitigen Ausschluss benötigen aktives Warten. Effizientere Lösungen sind möglich durch Hardware-Unterstützung Ins Betriebssystem integrierte Lösungen
Wechselseitiger Ausschluss in Hardware Zur Erinnerung Versuch 2 als Softwarelösung /* Prozess 0 */ wiederhole solange (flag[1] = true) tue nichts; flag[0] := true; /* kritischer Abschnitt */ flag[0] := false; /* nichtkrit. Abschnitt */ /* Prozess 1 */ wiederhole solange (flag[0] = true) tue nichts; flag[1] := true; /* kritischer Abschnitt */ flag[1] := false; /* nichtkrit. Abschnitt */ Warum scheiterte dieser Versuch? Weil Testen und Setzen von Flags nicht in einem einzigen Schritt durchführbar: Prozesswechsel zwischen Testen und Setzen ist möglich.
Wechselseitiger Ausschluss in Hardware Neues Konzept: Einführung atomarer Operationen. Hardware garantiert atomare Ausführung. Testen und Setzen zusammen bilden eine atomare Operation: Definiere neuen Befehl TSL: Test and Set Lock Da TSL ein einziger Befehl ist, kann ein Prozesswechsel nicht zwischen Testen und Setzen erfolgen (nicht mitten im Befehl )
Wechselseitiger Ausschluss in Hardware Befehl TSL RX, LOCK mit Speicheradresse LOCK und Register RX hat folgende W irkung: RX = Speicher[LOCK]; Speicher[LOCK] := 1 Ein Befehl d.h. atomare Ausführung Prozesse, die Zugriff auf den kritischen Abschnitt erhalten wollen, führen folgende Befehle aus: enter_region: TSL, RX, LOCK // kopiere Lock-Variable und setze Lock CMP RX, #0 // War Lock-Variable = 0? (CMP = compare) JNE enter_region // Wenn Lock schon gesetzt war -> Schleife (JNE = jump if not equal)... // Fortfahren und Betreten des krit. Abschnitts Prozesse, die den kritischen Abschnitt verlassen, führen folgenden Befehl aus: STOREI LOCK, #0 // Speicher[LOCK] := 0 (STOREI = store immediate)
Wechselseitiger Ausschluss in Hardware Vorteil: Nicht-alternierender Zugriff auf den kritischen Abschnitt Wechselseitiger Ausschluss garantiert Kein Deadlock Nachteil: Aktives Warten genau wie bei Softwarelösung! Weiterer Nachteil sowohl für Petersons Algorithmus als auch für Hardware-Lösung: Bei festen Prioritäten von Prozessen, können diese Lösungen trotzdem zu Deadlocks führen. Szenario: Prozess 0 hat höhere Priorität, ist aber (z.b. wegen I/O-Operation) blockiert. Prozess 1 betritt kritischen Abschnitt. Prozess 0 wird wieder rechenbereit, erhält Prozessor wegen höherer Priorität Wenn Prozess 0 in den kritischen Abschnitt will, betritt er die Schleife für aktives Warten ) Deadlock!!! => Aktives Warten ist nicht nur ein Effizienzproblem!
Wechselseitiger Ausschluss im Betriebssystem Folgerung: Um aktives Warten zu verhindern, muss wechselseitiger Ausschluss ins Betriebssystem integriert werden! Idee: Statt aktiv zu warten, blockieren Prozesse einfach! Neuer Systemaufruf sleep(lock) Nach Verlassen des kritischen Abschnitts weckt der verlassende Prozess einen anderen Prozess auf, der auf Erlaubnis wartet, den kritischen Abschnitt zu betreten (sofern ein solcher Prozess vorhanden ist) Neuer Systemaufruf wakeup(lock) Parameter lock wird nur gebraucht, um Aufrufe für den gleichen kritischen Abschnitt einander zuzuordnen. Eine W arteschlange pro kritischen Abschnitt
Mutex Mutex = Mutual Exclusion Vor dem Eintritt in einen kritischen Abschnitt wird die Funktion mutex_lock(lock) aufgerufen function mutex_lock(lock) solange (testset(lock) = false) sleep(lock); return; testset(lock) führt atomare TSL-Operation aus; liefert true gdw. Lockvariable vorher 0 war.
Mutex Nach Verlassen des kritischen Abschnitts wird mutex_unlock(lock) aufgerufen function mutex_unlock(lock) unset(lock); // lock wird freigegeben wakeup(lock); return; Es muss eine Warteschlange für Prozesse geben, die auf lock warten. Nach wakeup(lock) wird der erste Prozess in der Queue bereit (aber nicht notwendigerweise aktiv -> Scheduler-Algorithmus!) Die Variable lock heißt Mutexvariable bzw. kurz Mutex.