Dies ist eine alte Version des Dokuments!


Gegenseitiger Ausschluss (Mutex - Mutual Exclusion)

Im Betriebssystemcode oder auch in Prozessen kann der Fall auftreten, dass einen bestimmten Codeabschnitt nur ein Prozess zu einer Zeit ausführen darf, und auch wenn er unterbrochen wird, darf kein anderer Prozess diesen Code nebenläufig ausführen. Wir sprechen von einem kritischen Abschnitt (Critical Section).

Siehe auch Interrupts.

Realisierung

Wir nehmen also an, dass (Teile von) Prozesscode nur von einem Prozess gleichzeitig ausgeführt werden darf. Auch bei Unterbrechung darf kein anderer Prozess in diesen Abschnitt eintreten. Als erste Näherung würden wir mit folgender Lösung beginnen, unter Verwendung einer Lock-Variable s:

lock s;
if (!isSet(s)) 
{
    set(s);
    critical_section ();
    unset (s);
}
else
{
     // ... ein anderer Prozess ist im kritischen Abschnitt
}

Die Lock-Variable, die als Boolsche Variable implementiert sein könnte, sagt also aus, ob ein Prozess bereits im kritischen Abschnitt ist. Der kritische Abschnitt geht vom setzen der Lock-Variable bis zum zurücksetzen.

Allerdings funktioniert der Code nicht, denn was passiert, wenn der Prozess zwischen if (isSet(s)) und set(s) unterbrochen wird, und ein anderer Prozess genau das gleiche macht, und in die Critical Section springt? Dann befinden sich zwei Prozesse in dem kritischen Abschnitt. Dh. was wir brauchen ist ein atomarer Befehl, der nicht unterbrochen werden kann, der eine Variable testet und gleichzeitig setzt. Wir nehmen an, wir haben einen solchen Befehl testnset:

lock s;
if (testnset(s)==false)
{
    criticalsection();
    unset (s);
}

Dh. wenn s noch nicht gesetzt ist, gibt der Befehl false zurück und setzt s im gleichen Moment. Dadurch wird garantiert, dass kein Prozess diesen unterbrechen kann, zwischen testen und setzen. Ist s bereits gesetzt, dann führt auch ein erneutes setzen nur dazu, dass s gesetzt ist, dh. der Wert wird nicht verfälscht.

Verallgemeinerung

Bislang haben wir nur einen Prozess in der Critical Section zugelassen. Allerdings kann es verschiedene Probleme geben, die mehr als einen Prozess zulassen oder sogar Prozesse in verschiedene Gruppen einteilen.

Semaphore

Eine Semaphore ist im Prinzip eine ganzzahlige Zählvariable. Sie wird mit einem Wert größer als 0 initialisiert. Bei einem Eintreten in den kritischen Abschnitt wird in einem atomaren Befehl der Zähler um 1 verringert. Ist der Zähler allerdings 0, so wird der Prozess blockiert, und er kann nicht in den kritischen Abschnitt eintreten. Er wird deblockiert, sobald ein Prozess aus dem kritischen Abschnitt austritt und dabei die Semaphore um 1 erhöht.

Leser / Schreiber-Problem

Wir haben eine Datei auf einer Festplatte. Nun ist es möglich den Zugriff auf eine Datei nur genau einem Prozess zu gestatten, aber das würde bei häufig und von vielen Prozessen gelesenen Dateien nur dazu führen, dass unser System erheblich langsamer wird. Daher erlauben wir den Zugriff auf eine Datei durch unendlich viele Leser, aber nur durch einen Schreiber. Schreiber, werden Daten in einer Datei ändern, wenn das zwei Prozesse gleichzeitig tun, führt das zu einem nicht deterministischen (nicht vorhersagbarem) Ergebnis. Außerdem darf kein Leser die Datei mehr lesen wollen.

Eine Realisierung ist mit zwei Locks möglich. Es gibt ein Lese-Lock und ein Schreib-Lock. Nun gibt es verschiedenene Konstellationen:

  • Lese-Lock ist gesetzt, Schreib-Lock allerdings nicht, ein Leser will eintreten
    • Leser tritt ein
  • Lese-Lock ist gesetzt, Schreib-Lock ist gesetzt, ein Leser will eintreten
    • Leser wird blockiert, da ein Schreiber die Datei zunächst verändern will (hier sind verschiedene Strategien möglich!)
  • Lese-Lock ist gesetzt, Schreib-Lock nicht, ein Schreiber will eintreten
    • Schreiber wird blockiert, bis alle Leser aus dem kritischen Abschnitt ausgetreten sind, und das Lese-Lock zurückgesetzt wird.
  • Lese-Lock ist egal, Schreib-Lock ist gesetzt, ein Schreiber will eintreten
    • Schreiber wird blockiert, bis alle Leser aus dem kritischen Abschnitt ausgetreten sind, und alle vorher angekommenen Schreiber ihre Schreibaktion vollendet haben.

Alle Locks könnten als umgedrehte (also noch oben zählende) Semaphore implementiert sein.