Unter
Locking (engl. für
Sperren) versteht man in der Informatik das Sperren des Zugriffs auf eine Ressource. Eine solche Sperre ermöglicht den exklusiven Zugriff eines Prozesses auf eine Ressource d.h. mit der Garantie dass kein anderer Prozess diese Ressource liest oder verändert solange die Sperre besteht.
Locking wird häufig bei Prozesssynchronisation sowie in Datei- und Datenbanksystemen verwendet um atomare und konsistente Lese- und Schreibanforderungen zu gewährleisten.
Locking-Arten
Möchte ein Prozess exklusiven Zugriff auf eine Ressource, muss er eine Sperre bei einem Verwaltungsprozess (z.B. einem Locking-Manager) anfordern. Um die angeforderte Ressource nicht komplett zu sperren gibt es zwei grundlegende Arten von Sperren:
- Read-Lock: Besitzt eine Ressource einen Read-Lock so möchte der Prozess, der diese Sperre gesetzt hat, von der Ressource nur lesen. Somit können auch andere Prozesse auf diese Ressource lesend zugreifen, diese aber nicht verändern.
- Write-Lock: Eine Ressource mit Write-Lock verhindert, dass die Ressource von anderen Prozessen gelesen oder geschrieben wird, da der Prozess, der den Lock gesetzt hat, die Ressource verändern möchte.
Lock-Freigabe
Ist der Prozess, der eine Sperre angefordert hat fertig, muss er die Sperre wieder aufheben. Prozesse, die aufgrund einer Sperre auf eine Ressource nicht zugreifen konnten, müssen warten und reihen sich in eine Warteschlange ein. Es existieren mehrere Möglichkeiten wie diese Warteschlange ausgelegt ist, z.B. prioritätsgesteuert, FIFO-gesteuert, usw.
Verhungern/Starving
Hebt ein Prozess eine Sperre nicht wieder auf, so warten ggf. andere Prozesse unendlich lange auf diese Freigabe. Diese Prozesse ?verhungern? (engl.
starve) somit.
Deadlock
Das Setzen einer Sperre kann Deadlocks verursachen, nämlich dann, wenn zwei Prozesse gegenseitig auf die Freigabe von ihnen gesperrter Ressourcen warten.
Hierarchisches Locking
Beim hierarchischen Locking werden Ressourcen zu größeren logischen Einheiten zusammengefasst. Es ist nun möglich die gesamte logische Einheit zu sperren. Dies bringt einen Performancegewinn, da so nicht alle Elemente der Einheit separat gesperrt und überwacht werden müssen. Die richtige Wahl der Granularität der logischen Einheiten ist dabei von großer Bedeutung.
Hierarchisches Locking findet vor allem in Datenbanksystemen Anwendung.
Implementierung
Der zentrale Aspekt von Locks ist die Fähigkeit, einen Prozess, der gerade nicht "bedient" werden kann, so lange
warten zu lassen, bis das Lock frei ist ? das entspricht der Funktion
warte_bis, die im Pseudocode weiter unten zur Anwendung kommen wird. Das Warten auf eine Bedingung ist grundsätzlich auf zwei Arten möglich:
- Die erste Möglichkeit ist die Umsetzung mit Hilfe des Prozess-Schedulers (also des Betriebssystems bzw. der Laufzeitumgebung, siehe Time Sharing): Der Scheduler kennt das Lock und weist dem Prozess solange keine Rechenzeit zu, bis das Lock frei ist. Das ist im Allgemeinen nur dann möglich, wenn der Lock-Mechanismus vom Betriebssystem (bzw. der Laufzeitumgebung) bereitgestellt wird, denn nur dann kann der Scheduler das Lock und seinen aktuellen Zustand kennen. Alternativ kann die Laufzeitumgebung auch ein Monitor-Konzept unterstützen, in dem dies durch die Funktionen wait (auf eine Bedingungsvariable) und notify (beendet wait auf die Bedingungsvariable) umgesetzt wird ? die Bedingung wird dann in einem kritischen Codesegment des Monitors geprüft. Die Umsetzung von warte_bis wäre dann folgendermaßen möglich:
'''Funktion''' warte_bis( '''Parameter''' bedingung )
- Das setzt voraus, dass beim Ändern der Variablen, auf die sich die Bedingung bezieht, stets notify aufgerufen wird, so dass die Bedingung erneut geprüft wird.
- Die zweite Möglichkeit ist die Umsetzung als Spinlock: der Prozess prüft in einer Schleife ständig, ob das Lock frei ist, und fährt erst dann fort. Dies ist auch ohne Unterstützung des Schedulers möglich, hat aber den Nachteil, dass der Prozess Rechenzeit verbraucht, während er wartet (?aktives Warten? bzw. ?Busy Waiting?). Wenn das Betriebssystem (bzw. die Laufzeitumgebung) eine Möglichkeit bietet, einen Prozess für eine vorgegebene Zeit ?schlafen? zu lassen (sleep), so ist das nicht ganz so schlimm, da nur in regelmäßigen Abständen etwas Rechenzeit gebraucht wird, um das Lock zu prüfen (man spricht auch von slow busy waiting oder lazy polling). Bietet das Betriebssystem diese Möglichkeit aber nicht, so beansprucht der Prozess während des Wartens die volle Rechenleistung des Systems und bremst dadurch andere laufende Prozesse aus. Die Umsetzung von warte_bis wäre mit der Möglichkeit zu schlafen ähnlich wie oben, nur mit dem Unterschied, dass die Bedingung in regelmäßigen Abständen geprüft wird, und nicht (nur) wenn sich die betreffenden Variablen ändern:
'''Funktion''' warte_bis( '''Parameter''' bedingung )
Beispiel
Die Veränderungen von Prozess 2 werden somit von Prozess 1 überschrieben und sind verloren. Solche Fehler sind manchmal schwer zu reproduzieren, da sie zufällig auftreten.Beide Änderungen sind in der Ressource enthalten.
Ein anschaulicheres Beispiel könnte ein falsch implentierter Bankautomat sein:Neuer Stand: 150?
falsch!Neuer Stand: 50?
richtig!
Siehe auch
- Sperrverfahren
- Sperreskalation
- Deadlock
ParallelverarbeitungDatenbanktheorieBetriebssystemtheorie
Lock Cierre de exclusión mutuaVerrou ????? Lock??? U?raktas Blokada