Sitzung: Jeden Freitag in der Vorlesungszeit ab 16 Uhr c. t. im MAR 0.005. In der vorlesungsfreien Zeit unregelmäßig (Jemensch da?). Macht mit!

Informatik 4 (StuPO90)/Semaphoren: Unterschied zwischen den Versionen

K (Semaphore wurde nach Benutzer:Kriber/Semaphoren verschoben)
(wikify)
Zeile 1: Zeile 1:
A Pattern Language for the usage of Semaphores
+
= A Pattern Language for the usage of Semaphores =
----------------------------------------------
+
::(Nach http://c2.com/cgi/wiki?PatternLanguage)
(Nach http://c2.com/cgi/wiki?PatternLanguage)
 
  
 
Dafür zu sorgen, dass mehrere nebenläufige Prozesse korrekt miteinander arbeiten, ist erstaunlich schwierig. Speziell Mehrseitige Synchronisation bereitete mir beim Verstehen und Erklären besondere Probleme. Um die dabei auftretenden Probleme übersichtlicher zu erklären habe ich diese "Pattern Language" formuliert.
 
Dafür zu sorgen, dass mehrere nebenläufige Prozesse korrekt miteinander arbeiten, ist erstaunlich schwierig. Speziell Mehrseitige Synchronisation bereitete mir beim Verstehen und Erklären besondere Probleme. Um die dabei auftretenden Probleme übersichtlicher zu erklären habe ich diese "Pattern Language" formuliert.
Zeile 8: Zeile 7:
  
 
Folgende Probleme werden behandelt:
 
Folgende Probleme werden behandelt:
- Locking für kritische Sektionen
+
* Locking für kritische Sektionen
- Locking für beschränkten Zugriff (n clients)
+
* Locking für beschränkten Zugriff (n clients)
- Locking zum gegenseitigen Ausschluss von zwei Gruppen
+
* Locking zum gegenseitigen Ausschluss von zwei Gruppen
- Locking für Priorisierung einer von mehreren Gruppen
+
* Locking für Priorisierung einer von mehreren Gruppen
  
1. "Critical Section"
+
== 1. "Critical Section" ==
---------------------
 
 
Eine kritische Sektionen ist ein Stück Code, in dem nur ein Prozess gleichzeitig sein darf. Diese Bereiche kann man schützen, indem man sie in Semaphoren "verpackt".
 
Eine kritische Sektionen ist ein Stück Code, in dem nur ein Prozess gleichzeitig sein darf. Diese Bereiche kann man schützen, indem man sie in Semaphoren "verpackt".
  
 
Beispiel:
 
Beispiel:
--- snip ---
+
<nowiki>
 
criticalSection = Semaphore(1)
 
criticalSection = Semaphore(1)
  
Zeile 24: Zeile 22:
 
.. etwas in der kritischen Sektion tun ..
 
.. etwas in der kritischen Sektion tun ..
 
criticalSection.V()
 
criticalSection.V()
--- snap ---
+
</nowiki>
  
2. "Restricted Section"
+
== 2. "Restricted Section" ==
-----------------------
 
 
Will man einer bestimmten Anzahl von Prozessen den Aufenthalt in einem Bereich gestatten, nennt man das eine beschränkte Sektion. Dies setzt man um, indem man diese Sektion in die P()- und V()-Operationen einer Semaphore fasst, die mit dem gewünschten Wert initialisiert ist. Streng genommen ist dies also eine Erweiterung der "Critical Section".
 
Will man einer bestimmten Anzahl von Prozessen den Aufenthalt in einem Bereich gestatten, nennt man das eine beschränkte Sektion. Dies setzt man um, indem man diese Sektion in die P()- und V()-Operationen einer Semaphore fasst, die mit dem gewünschten Wert initialisiert ist. Streng genommen ist dies also eine Erweiterung der "Critical Section".
  
 
Beispiel:
 
Beispiel:
--- snip ---
+
<nowiki>
 
restrictedSection = Semaphore(n)
 
restrictedSection = Semaphore(n)
  
Zeile 37: Zeile 34:
 
... etwas tun in der kritischen Sektion
 
... etwas tun in der kritischen Sektion
 
restrictedSection.V()
 
restrictedSection.V()
--- snap ---
+
<nowiki>
  
 
Jetzt können maximal n Prozesse den kritischen Abschnitt gleichzeitig betreten.
 
Jetzt können maximal n Prozesse den kritischen Abschnitt gleichzeitig betreten.
  
3. "Mutual Exclusion"
+
== 3. "Mutual Exclusion" ==
---------------------
 
 
Wenn ein Stück Code nur von jeweils einer Art von Prozessen betreten werden darf, dann verwendet man eine Semaphore um gegenseitigen Ausschluss zu realisieren.
 
Wenn ein Stück Code nur von jeweils einer Art von Prozessen betreten werden darf, dann verwendet man eine Semaphore um gegenseitigen Ausschluss zu realisieren.
  
 
Beispiel:
 
Beispiel:
--- snip ---
+
<nowiki>
 
wenn ich der erste Prozess meiner Art bin der den kritischen Abschnitt betreten will:
 
wenn ich der erste Prozess meiner Art bin der den kritischen Abschnitt betreten will:
 
hole ich mir die Semaphore
 
hole ich mir die Semaphore
Zeile 54: Zeile 50:
 
wenn ich der letzte Prozess meiner Art bin der den kritischen Abschnitt verlässt:
 
wenn ich der letzte Prozess meiner Art bin der den kritischen Abschnitt verlässt:
 
gebe ich die semaphore frei
 
gebe ich die semaphore frei
--- snap ---
+
</nowiki>
  
 
Sowohl der erste als auch der zweite Test, ob man der erste bzw. letzte ist, ist natürlich /*TODO: ist das echt so natürlich?Wenn man den Zähler dazu erwähnt schon*/ ein kritischer Abschnitt und muss entsprechend gelockt werden. Etwas formaler:
 
Sowohl der erste als auch der zweite Test, ob man der erste bzw. letzte ist, ist natürlich /*TODO: ist das echt so natürlich?Wenn man den Zähler dazu erwähnt schon*/ ein kritischer Abschnitt und muss entsprechend gelockt werden. Etwas formaler:
  
--- snip ---
+
<nowiki>
 
criticalSection = Semaphore(1), mutualExclusion = Semaphore(1)
 
criticalSection = Semaphore(1), mutualExclusion = Semaphore(1)
  
Zeile 75: Zeile 71:
 
mutualExclusion.V()
 
mutualExclusion.V()
 
criticalSection.V()
 
criticalSection.V()
--- snap ---
+
</nowiki>
  
 
Hier wird das Muster "Critical Section" zusätzlich gebraucht, damit der Zähler zwischen seiner Veränderung und dem Test welchen Wert er hat nicht von einem anderen Prozess verändert werden kann.
 
Hier wird das Muster "Critical Section" zusätzlich gebraucht, damit der Zähler zwischen seiner Veränderung und dem Test welchen Wert er hat nicht von einem anderen Prozess verändert werden kann.
Zeile 81: Zeile 77:
 
Kombiniert man "Critical Section" mit "Mutual Exclusion" dann kann man entweder die "Critical Section" direkt um den kritischen Abschnitt legen, oder sie um das gesamte außen herum "wickeln".
 
Kombiniert man "Critical Section" mit "Mutual Exclusion" dann kann man entweder die "Critical Section" direkt um den kritischen Abschnitt legen, oder sie um das gesamte außen herum "wickeln".
  
4. "Priority"
+
== 4. "Priority" ==
-------------
 
 
Zwei ineinander verschachtelte Semaphoren können genutzt werden, um eine Art von Prozessen gegen andere zu priorisieren. Dies ist eine Ergänzung des vorhergehenden Musters.
 
Zwei ineinander verschachtelte Semaphoren können genutzt werden, um eine Art von Prozessen gegen andere zu priorisieren. Dies ist eine Ergänzung des vorhergehenden Musters.
  
 
Beispiel:
 
Beispiel:
--- snip ---
+
<nowiki>
 
äusserer kritischer Abschnitt <- An dieser Semaphore bleiben alle Prozesse  
 
äusserer kritischer Abschnitt <- An dieser Semaphore bleiben alle Prozesse  
 
"hängen" bis auf einen
 
"hängen" bis auf einen
Zeile 96: Zeile 91:
 
innerer Kritischer Abschnitt
 
innerer Kritischer Abschnitt
 
äusserer kritischer Abschnitt
 
äusserer kritischer Abschnitt
--- snap ---
+
</nowiki>
  
 
/*TODO: und die andere Prozessart?*/
 
/*TODO: und die andere Prozessart?*/
Zeile 103: Zeile 98:
  
 
Etwas Formaler:
 
Etwas Formaler:
--- snip ---
+
<nowiki>
 
outerCriticalSection = Semaphore(1), innerCriticalSection = Semaphore(1)
 
outerCriticalSection = Semaphore(1), innerCriticalSection = Semaphore(1)
  
Zeile 113: Zeile 108:
 
innerCriticalSection.V()
 
innerCriticalSection.V()
 
outerCriticalSection.V()
 
outerCriticalSection.V()
--- snap ---
+
</nowiki>
  
 
Um dieses Muster anzuwenden muss man es mit dem Muster "Mutual Exclusion" kombinieren, indem der erste einer anderen Gruppe sich die Semaphore des inneren kritischen Abschnitts "sichert".
 
Um dieses Muster anzuwenden muss man es mit dem Muster "Mutual Exclusion" kombinieren, indem der erste einer anderen Gruppe sich die Semaphore des inneren kritischen Abschnitts "sichert".
Zeile 121: Zeile 116:
 
/*TODO: hier einen Punkt einfügen, der einseitige Synchronisationabdeckt*/
 
/*TODO: hier einen Punkt einfügen, der einseitige Synchronisationabdeckt*/
  
5. "Reduction"
+
== 5. "Reduction" ==
--------------
 
 
Die Lösungen, die man aus der Kombination dieser Muster erhält, verwenden in Spezialfällen mehr Semaphoren als unbedingt nötig. Man kann daher manchmal einige der Semaphoren weglassen, da ihre Funktion schon von einer der anderen Semaphoren erfüllt werden.
 
Die Lösungen, die man aus der Kombination dieser Muster erhält, verwenden in Spezialfällen mehr Semaphoren als unbedingt nötig. Man kann daher manchmal einige der Semaphoren weglassen, da ihre Funktion schon von einer der anderen Semaphoren erfüllt werden.

Version vom 19. April 2006, 09:33 Uhr

A Pattern Language for the usage of Semaphores

(Nach http://c2.com/cgi/wiki?PatternLanguage)

Dafür zu sorgen, dass mehrere nebenläufige Prozesse korrekt miteinander arbeiten, ist erstaunlich schwierig. Speziell Mehrseitige Synchronisation bereitete mir beim Verstehen und Erklären besondere Probleme. Um die dabei auftretenden Probleme übersichtlicher zu erklären habe ich diese "Pattern Language" formuliert.

Ich würde mich sehr über Feedback (mhaecker@cs.tu-berlin.de) zu den Beispielen und Formulierungen freuen.

Folgende Probleme werden behandelt:

  • Locking für kritische Sektionen
  • Locking für beschränkten Zugriff (n clients)
  • Locking zum gegenseitigen Ausschluss von zwei Gruppen
  • Locking für Priorisierung einer von mehreren Gruppen

1. "Critical Section"

Eine kritische Sektionen ist ein Stück Code, in dem nur ein Prozess gleichzeitig sein darf. Diese Bereiche kann man schützen, indem man sie in Semaphoren "verpackt".

Beispiel:

criticalSection = Semaphore(1)

criticalSection.P()
	.. etwas in der kritischen Sektion tun ..
criticalSection.V()

2. "Restricted Section"

Will man einer bestimmten Anzahl von Prozessen den Aufenthalt in einem Bereich gestatten, nennt man das eine beschränkte Sektion. Dies setzt man um, indem man diese Sektion in die P()- und V()-Operationen einer Semaphore fasst, die mit dem gewünschten Wert initialisiert ist. Streng genommen ist dies also eine Erweiterung der "Critical Section".

Beispiel:

restrictedSection = Semaphore(n)

restrictedSection.P()
	... etwas tun in der kritischen Sektion
restrictedSection.V()
<nowiki>

Jetzt können maximal n Prozesse den kritischen Abschnitt gleichzeitig betreten.

== 3. "Mutual Exclusion" ==
Wenn ein Stück Code nur von jeweils einer Art von Prozessen betreten werden darf, dann verwendet man eine Semaphore um gegenseitigen Ausschluss zu realisieren.

Beispiel:
 <nowiki>
wenn ich der erste Prozess meiner Art bin der den kritischen Abschnitt betreten will:
	hole ich mir die Semaphore

.. etwas im kritischen Abschnitt tun ..

wenn ich der letzte Prozess meiner Art bin der den kritischen Abschnitt verlässt:
	gebe ich die semaphore frei

Sowohl der erste als auch der zweite Test, ob man der erste bzw. letzte ist, ist natürlich /*TODO: ist das echt so natürlich?Wenn man den Zähler dazu erwähnt schon*/ ein kritischer Abschnitt und muss entsprechend gelockt werden. Etwas formaler:

criticalSection = Semaphore(1), mutualExclusion = Semaphore(1)

criticalSection.P()
	howManyInRestrictedSection++
	if 1 == howManyInCriticalSection:
		mutualExclusion.P()
criticalSection.V()

.. etwas in dem kritischen Abschnitt erledigen,
	 in dem jetzt diese Prozesse unter sich sind ...

criticalSection.P()
	howManyInRestrictedSection--
	if 0 == howManyInCriticalSection:
		mutualExclusion.V()
criticalSection.V()

Hier wird das Muster "Critical Section" zusätzlich gebraucht, damit der Zähler zwischen seiner Veränderung und dem Test welchen Wert er hat nicht von einem anderen Prozess verändert werden kann.

Kombiniert man "Critical Section" mit "Mutual Exclusion" dann kann man entweder die "Critical Section" direkt um den kritischen Abschnitt legen, oder sie um das gesamte außen herum "wickeln".

4. "Priority"

Zwei ineinander verschachtelte Semaphoren können genutzt werden, um eine Art von Prozessen gegen andere zu priorisieren. Dies ist eine Ergänzung des vorhergehenden Musters.

Beispiel:

äusserer kritischer Abschnitt <- An dieser Semaphore bleiben alle Prozesse 
				 "hängen" bis auf einen
	innerer Kritischer Abschnitt <- an dieser Semaphore ist also immer 
					nur maximal ein Prozess gleichzeitig

		.. kritischer Bereich ..

	innerer Kritischer Abschnitt
äusserer kritischer Abschnitt

/*TODO: und die andere Prozessart?*/

Die Semaphore, die den inneren kritischen Abschnitt schützt, kann jetzt von einem anderen Prozess aus einer anderen Prozessgruppe gelockt werden. Wenn das passiert, dann muss er maximal so lange warten, bis sie der Prozess der sie gerade besitzt freigibt, da nur einer aus dieser Gruppe daran "hängen" kann. Er hat also alle anderen Prozesse die an der äusseren Semaphore warten überholt.

Etwas Formaler:

outerCriticalSection = Semaphore(1), innerCriticalSection = Semaphore(1)

outerCriticalSection.P()
	innerCriticalSection.P()
		
		.. kritischer Bereich ..
	
	innerCriticalSection.V()
outerCriticalSection.V()

Um dieses Muster anzuwenden muss man es mit dem Muster "Mutual Exclusion" kombinieren, indem der erste einer anderen Gruppe sich die Semaphore des inneren kritischen Abschnitts "sichert".

Will man "Priority" mit "Restricted Section" kombinieren, ist zu beachten, das die begrenzte kritische Sektion der Gruppe die überholt werden soll natürlich den gesamten Code mit einschließt, der die Priorität sichert. Tut man das nicht, kann das Überholen nicht garantiert werden. Auf der anderen Seite, bei den Prozessen, die überholen sollen, muss die begrenzte kritische Sektion innerhalb des Codes liegen, der die Priorität garantiert, da andernfalls in dem Spezialfall, in dem die Begrenzung auf eins liegt, das Überholen nicht funktioniert.

/*TODO: hier einen Punkt einfügen, der einseitige Synchronisationabdeckt*/

5. "Reduction"

Die Lösungen, die man aus der Kombination dieser Muster erhält, verwenden in Spezialfällen mehr Semaphoren als unbedingt nötig. Man kann daher manchmal einige der Semaphoren weglassen, da ihre Funktion schon von einer der anderen Semaphoren erfüllt werden.