| Zeile 64: | Zeile 64: | ||
== Besonderheiten bei der Programmierung == | == Besonderheiten bei der Programmierung == | ||
| − | === Multiprozessorsynchronisation (TSL-Instruktion | + | === Multiprozessorsynchronisation (TSL-Instruktion) === |
| − | Um auf Uniprozessorsystemen effizient wechselseitigen Ausschluss ([[Mutex]]) zu implementieren, wird üblicherweise die ''xchg''-Instruktion verwendet (oder passende API-Funktionen wie [http://msdn.microsoft.com/en-us/library/ms683590(VS.85).aspx InterlockedExchange] unter Windows). Architekturübergreifend wird diese Instruktion als ''TSL'' (Test and Set Lock) oder auch ''TSTS'' (Test and Set) bezeichnet. Dabei liest man mit der Instruktion den alten Wert z.B. in ein Register und schreibt den neuen Wert in den Speicher (z.B. 0 = Mutex verfügbar, 1 = Mutex gelockt). Die Instruktion ist atomar, d.h. kann nicht unterbrochen werden (CPU führt immer ganze Instruktionen aus!). | + | Um auf Uniprozessorsystemen effizient wechselseitigen Ausschluss ([[Mutex]]) zu implementieren, wird üblicherweise die ''xchg''-Instruktion verwendet (oder passende API-Funktionen wie [http://msdn.microsoft.com/en-us/library/ms683590(VS.85).aspx InterlockedExchange] unter Windows). Architekturübergreifend wird diese Instruktion als ''TSL'' (Test and Set Lock) oder auch ''TSTS'' (Test and Set) bzw. ''TAS'' (Test and Set) bezeichnet. Dabei liest man mit der Instruktion den alten Wert z.B. in ein Register und schreibt den neuen Wert in den Speicher (z.B. 0 = Mutex verfügbar, 1 = Mutex gelockt). Die Instruktion ist atomar, d.h. kann nicht unterbrochen werden (CPU führt immer ganze Instruktionen aus!). Da Instruktionen ''echt'' gleichzeitig ausgeführt werden können (anstatt hintereinander bei Uniprozessorsystemen), muss diese Instruktion auf Multiprozessorsystemen ebenfalls atomar sein. |
| − | + | Eine fehlerhafte Mutex-Implementierung mit nicht-atomaren Operationen könnte z.B. so aussehen: | |
* Der Mutex sei freigegeben (= 0), es kann also ein Prozess in den kritischen Abschnitt eintreten, wenn er den Mutex lockt (d.h. auf 1 setzt) | * Der Mutex sei freigegeben (= 0), es kann also ein Prozess in den kritischen Abschnitt eintreten, wenn er den Mutex lockt (d.h. auf 1 setzt) | ||
* Nun wollen zwei Threads/Prozesse, die auf verschiedenen CPU's ausgeführt werden, etwa gleichzeitig den Mutex für sich locken | * Nun wollen zwei Threads/Prozesse, die auf verschiedenen CPU's ausgeführt werden, etwa gleichzeitig den Mutex für sich locken | ||
| − | * CPU #1 liest die Speicherstelle = 0 | + | * Thread #1 auf CPU #1 liest die Speicherstelle = 0 |
| − | * CPU #2 liest ebenfalls die Speicherstelle = 0 | + | * Thread #2 auf CPU #2 liest ebenfalls die Speicherstelle = 0 |
| − | * CPU #1 überschreibt den Speicher mit 1 und gibt als alten Wert die gelesene 0 zurück | + | * Thread #1 auf CPU #1 überschreibt den Speicher mit 1 und gibt als alten Wert die gelesene 0 zurück |
| − | * CPU #2 überschreibt den Speicher nochmals mit 1 und gibt als alten Wert die gelesene 0 zurück | + | * Thread #2 auf CPU #2 überschreibt den Speicher nochmals mit 1 und gibt als alten Wert die gelesene 0 zurück |
Nun ist eine [[Race Condition]] aufgetreten: Beide Threads/Prozesse denken nun, dass der Mutex vorher nicht gelockt war und sie nun als einziger Thread/Prozess im kritischen Bereich arbeiten werden. Ganz offensichtlich arbeiten aber in diesem Beispiel nun beide in dem kritischen Abschnitt. | Nun ist eine [[Race Condition]] aufgetreten: Beide Threads/Prozesse denken nun, dass der Mutex vorher nicht gelockt war und sie nun als einziger Thread/Prozess im kritischen Bereich arbeiten werden. Ganz offensichtlich arbeiten aber in diesem Beispiel nun beide in dem kritischen Abschnitt. | ||
| − | + | Aus diesem Grund wird zur Implementierung die TSL-Instruktion verwendet. Sie ist auch auf SMP-Systemen atomar (vgl. [http://www.intel.com/design/intarch/manuals/243191.htm Intel Spezifikation], S. 3-714), d.h. der sogenannte ''lock''-Präfix wird implizit angenommen, wenn bei der ''xchg''-Instruktion eine Speicherstelle verwendet wird. Der Präfix kann auch explizit angegeben werden - er bewirkt, dass der Speicherbus während der nächsten Instruktion gesperrt wird. | |
| − | <asm>; | + | Ein Beispiel für eine simple 32-bit TSL-Funktion: |
| − | + | <asm>; uint32_t tsl(uint32_t *ptr, uint32_t newValue) | |
| − | + | _tsl: | |
| − | + | push ebx | |
| − | + | mov eax, [esp+12] | |
| + | mov ebx, [esp+8] | ||
| + | xchg eax, [ebx] | ||
| + | pop ebx | ||
| + | ret</asm> | ||
== siehe auch == | == siehe auch == | ||