Synchronisation. Gliederung. Synchronisation. Einführung (1) Übersicht: 1. Einführung und Übersicht. 2. Prozesse und Threads. 3.



Ähnliche Dokumente
Betriebssysteme Theorie

leave: mov flag, 0 ; 0 in flag speichern: Lock freigeben ret

Hans-Georg Eßer, Hochschule München Betriebssysteme I, SS Synchronisation (1) Folie 3

Softwarelösungen: Versuch 4

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

Monitore. Klicken bearbeiten

Betriebssysteme 1 BS1-E SS Prof. Dr.-Ing. Hans-Georg Eßer Fachhochschule Südwestfalen. Foliensatz E: Synchronisation Deadlocks. v1.

Betriebssysteme 1. Einführung (2) Synchronisation: Probleme mit gleichzeitigem Zugriff auf Datenstrukturen Beispiel: Zwei Threads erhöhen einen Zähler

Verteilte Systeme CS5001

Prozeß P1 Prozeß P2. Zur Synchronisation stehen den beiden Prozessen binäre Semaphore und die beiden Funktionen

Domänenmodell: Fadenkommunikation und -synchronisation

#define N 5 // Anzahl der Philosophen. while (TRUE) { // Der Philosoph denkt

Synchronisation in Java. Invisible Web

Vorkurs C++ Programmierung

Methoden. von Objekten definiert werden, Methoden,, Zugriffsmethoden und Read-Only

4D Server v12 64-bit Version BETA VERSION

Betriebssysteme. G: Parallele Prozesse. (Teil B: Klassische Problemstellungen, Mutual Exclusion, kritische Regionen)

Bitte verwenden Sie nur dokumentenechtes Schreibmaterial!

Tag 4 Inhaltsverzeichnis

Grundlagen verteilter Systeme

Objektorientierte Programmierung

Programmierkurs Java

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Client-Server-Beziehungen

In diesem Tutorial lernen Sie, wie Sie einen Termin erfassen und verschiedene Einstellungen zu einem Termin vornehmen können.

Einführung in die Java- Programmierung

Architektur Verteilter Systeme Teil 2: Prozesse und Threads

Zeichen bei Zahlen entschlüsseln

Betriebssystembau (BSB)

5 DATEN Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

S7-Hantierungsbausteine für R355, R6000 und R2700

Installationsanleitung für CashPro im Mehrbenutzerzugriff/Netzwerkbetrieb

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = Euro ergeben.

Datenbank-Verschlüsselung mit DbDefence und Webanwendungen.

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {...

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Sichtbarkeit & statische Methoden. Einsatz von Sichtbarkeit Einsatz statischer Methoden programmatische Realisierung 2 Beispielaufgaben

SEP 114. Design by Contract

Hans-Georg Eßer, FH München Betriebssysteme I, WS 2006/07, 2007/01/24 Zusammenfassung (2/2) Folie 2

Einführung in die Programmierung

C++ Grundlagen. ++ bedeutet Erweiterung zum Ansi C Standard. Hier wird eine Funktion eingeleitet

Fakultät Angewandte Informatik Lehrprofessur für Informatik

Um dies zu tun, öffnen Sie in den Systemeinstellungen das Kontrollfeld "Sharing". Auf dem Bildschirm sollte folgendes Fenster erscheinen:

Übung zu Grundlagen der Betriebssysteme. 10. Übung

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me

Zählen von Objekten einer bestimmten Klasse

Technische Informatik II

Foliensatz 5: Synchronisation. Hans-Georg Eßer, Hochschule München Betriebssysteme I, Sommersemester Folie 3. Foliensatz 5: Synchronisation

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

Tutorial Windows XP SP2 verteilen

Klausur WS 2006/07 Programmiersprache Java Objektorientierte Programmierung II 15. März 2007

iphone-kontakte zu Exchange übertragen

XT Großhandelsangebote

Über die Internetseite Hier werden unter Download/aktuelle Versionen die verschiedenen Module als zip-dateien bereitgestellt.

Java Kurs für Anfänger Einheit 5 Methoden

Erweiterung AE WWS Lite Win: AES Security Verschlüsselung

Einführung in die Programmierung

Anleitung zur Erstellung einer Batchdatei. - für das automatisierte Verbinden mit Netzlaufwerken beim Systemstart -

Es kann maximal ein Prozess die Umladestelle benutzen.

Tagesprogramm

Grundlagen von Python

Lizenzen auschecken. Was ist zu tun?

Was ist PDF? Portable Document Format, von Adobe Systems entwickelt Multiplattformfähigkeit,

Seite 1 von 14. Cookie-Einstellungen verschiedener Browser

Lizenz-Server überwachen

Synchronisation in Datenbanksystemen in a nutshell

Prof. Dr. Uwe Schmidt. 21. August Aufgaben zur Klausur Objektorientierte Programmierung im SS 2007 (IA 252)

2. Einrichtung der ODBC-Schnittstelle aus orgamax (für 32-bit-Anwendungen)

Dämon-Prozesse ( deamon )

Tag 4 Inhaltsverzeichnis

Modellierung und Programmierung 1

2. Word-Dokumente verwalten

Kapitel 4. Monitore und wechselseitiger Ausschluss

Installationshinweise BEFU 2014

Handbuch. timecard Connector Version: REINER SCT Kartengeräte GmbH & Co. KG Goethestr Furtwangen

Tapps mit XP-Mode unter Windows 7 64 bit (V2.0)

Betriebssysteme. Dipl.-Ing.(FH) Volker Schepper

Arrays von Objekten. Annabelle Klarl. Einführung in die Informatik Programmierung und Softwareentwicklung

Anleitung zum Computercheck Windows Firewall aktivieren oder eine kostenlose Firewall installieren

1 Vom Problem zum Programm

1 Transaktionen in SQL. 2 Was ist eine Transaktion. 3 Eigenschaften einer Transaktion. PostgreSQL

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Formular»Fragenkatalog BIM-Server«

Applet Firewall und Freigabe der Objekte

Brainloop Dox Häufig gestellte Fragen

Lokale Installation von DotNetNuke 4 ohne IIS

Seminar: Mobile Geräte QNX Einführung

Objektorientierte Programmierung. Kapitel 12: Interfaces

Betriebssysteme I SS 2008 Hans-Georg Eßer, Hochschule München Zusammenfassung Seite 1

Tipps und Tricks zu Netop Vision und Vision Pro

Universität Karlsruhe (TH)

Software Engineering Interaktionsdiagramme

Computeria Solothurn

Mit jedem Client, der das Exchange Protokoll beherrscht (z.b. Mozilla Thunderbird mit Plug- In ExQulla, Apple Mail, Evolution,...)

Verwendung des Terminalservers der MUG

Ordner Berechtigung vergeben Zugriffsrechte unter Windows einrichten

Nebenläufige Programmierung

Systeme I: Betriebssysteme Kapitel 5 Nebenläufigkeit und wechselseitiger Ausschluss. Wolfram Burgard

Transkript:

Gliederung 1. Einführung und Übersicht 2. Prozesse und Ths 3. Interrupts 4. Scheduling 5. 6. Interprozesskommunikation 7. Speicherverwaltung Cl. Schnörr / HM 1 Gliederung Cl. Schnörr / HM 2 Einführung (1) Übersicht: Einführung Race-Condition Kritische Abschnitte Mechanismen des BS und Standard-Primitive Tips zur praktischen Anwendung: Deadlocks vermeiden Mutex-Objekt Wozu? Ths und z.t. Prozesse haben gemeinsamen Zugriff auf bestimmte Daten, z.b. Ths des gleichen Prozesses: gemeinsamer virtueller Speicher öffnen der gleichen Datei zum Lesen/Schreiben Prozesse mit Shared-Memory (--> IPC) SMP-System: Scheduler (je einer pro CPU): Zugriff auf gleiche Prozesslisten / Warteschlangen Datenbanken: Zugriff über eine globale Datenbank-Verbindung (DB-Connect) Cl. Schnörr / HM 3 Cl. Schnörr / HM 4

Einführung (2) Einführung (3) Beispiel: gleichzeitiger Zugriff auf Datenstruktur: zwei Ths erhöhen einen gemeinsamen Zähler: Beispiel: gleichzeitiger Zugriff auf Datenstruktur: gewünscht: eine der folgenden koordinierten Reihenfolgen: Cl. Schnörr / HM 5 Cl. Schnörr / HM 6 Einführung (4) Einführung (5) Beispiel: gleichzeitiger Zugriff auf Datenstruktur: Ursache: erhoehe_zaehler() arbeitet nicht atomar: Scheduler kann Funktion unterbrechen (anderer Th arbeitet weiter) Funktion kann auf mehreren CPUs gleichzeitig laufen Lösung: stelle sicher, dass immer nur ein Prozess/Th gleichzeitig auf gemeinsame Daten zugreift bis Vorgang abgeschlossen ist == gegenseitiger Ausschluß (Mutual Exclusion) Beispiel: Datenbanken: Datenbank: analoges Problem: exec sql CONNECT... exec sql SELECT kontostand INTO $var FROM KONTO WHERE kontonummer = $knr $var = $var - abhebung exec sql UPDATE Konto SET kontostand = $var WHERE kontonummer = $knr exec sql disconnect paralleler Zugriff auf gleichen Datensatz potentiell fehlerhaft --> Definition der (Datenbank-) Transaktion, die u.a. atomar und isoliert erfolgen muss Cl. Schnörr / HM 7 Cl. Schnörr / HM 8

Race-Condition (1) Race-Condition (2) Eigenschaften: Race Condition mehrere parallele Ths/Prozesse nutzen gemeinsame Ressource Zustand hängt von Reihenfolge der Ausführung ab: Ergebnis nicht vorhersagbar / nicht reproduzierbar Race: Ths liefern sich ein Rennen um den ersten/schnellsten Zugriff Warum Race-Conditions vermeiden? Ergebnisse paralleler Berechnungen sind potentiell falsch Programmtests können funktionieren, später die Anwendung aber versagen Race-Condition als Sicherheitslücke Race-Conditions sind auch Sicherheitslücken wird vom Angreifer genutzt einfaches Beispiel: ( command ); f = open( /tmp/script, w ); ( f, command ); close( f ); chmod( /tmp/script, a+x ); system( /tmp/script ); Angreifer ändert Dateiinhalt vor dem chmod() --> Programm läuft mit Rechten des Opfers / Race-Condition Cl. Schnörr / HM 9 / Race-Condition Cl. Schnörr / HM 10 Race-Condition (3) Kritischer Bereich Aspekte einer Lösung Idee: Zugriff via Flag / Lock auf Th/Prozess beschränken: erhoehe_zaehler() { flag = ( lock ); if ( flag == LOCK_NOT_SET ) { set( lock ); //Start kritischer Bereich w = ( adr ); w = w + 1; ( adr, w ); //Ende kritischer Bereich release( lock ); Problem: Lock-Variable nicht geschützt / Race-Condition kein Problem: gleichzeitiges Lesen von Daten Ths, die disjunkt sind, d.h. keine gemeinsame Daten haben / nutzen problematisch: mehrere Prozesse/Ths greifen gemeinsam auf Objekt zu, davon mindestens einer schreibend Cl. Schnörr / HM 11 Was ist ein kritischer Bereich? Programmteil, der auf gemeinsame Daten zugreift Block zwischen erstem und letztem Zugriff Formulierung: kritischen Bereich betreten / verlassen (enter / leave critical section) Anforderungen an parallele Ths: maximal ein Th gleichzeitig in kritischem Bereich kein Th außerhalb kritischem Bereich darf anderen blockieren (--> potentiell Deadlock) kein Th soll ewig auf Freigabe eines kritischen Bereichs warten Deadlocks zu vermeiden, z.b. zwei Ths in verschiedenen kritischen Bereichen blockieren sich gegenseitig / kritischer Bereich Cl. Schnörr / HM 12

Gegenseitiger Ausschluß Programmtechnische Gegenseitiger Ausschluß (Mutual Exclusion, kurz Mutex): nie mehr als ein Th betritt kritischen Bereich es ist Aufgabe des Programmierers, dies zu garantieren das BS bietet Mechanismen und Hilfsmittel, gegenseitigen Ausschluß durchzusetzen Verantwortung für Fehlerfreiheit liegt beim Programmierer: Compiler können i.d.r. Fehler aus Nebenläufigkeit nicht erkennen Idee war: Zugriff via Flag / Lock auf Th/Prozess beschränken Problem: Lock-Variable nicht geschützt Lösung: eine Lock-Variable atomar testen und setzen dies kann per Programmlogik über mehrere Variable erreicht werden (--> Lit.: Dekker1966, Petersons Algorithmus) ist kompliziert bei mehr als zwei Ths/Prozessen besser: Nutzung von standard BS-Mechanismen / Mutex Cl. Schnörr / HM 13 Cl. Schnörr / HM 14 Test-and-Set-Lock (TSL) Maschineninstruktion (moderner CPUs) mit dem Namen TSL = Test and Set Lock die atomar eine Lock-Variable liest (testet) und setzt, also garantiert ohne Unterbrechung Mechanismen und Standard-Primitive im Fall mehrerer CPUs: TSL muss Speicherbus sperren, damit kein Th auf anderer CPU in gleicher Weise zugreifen kann enter: tsl register, flag ; Variable in Register kopieren und ; dann Variable auf 1 setzen cmp register, 0 ; war Variable 0? jnz enter ; nicht 0: Lock war gesetzt, also Schleife(pollen) ret leave: mov flag, 0 ret ; 0 in flag speichern: Lock freigeben / Mechanismen Cl. Schnörr / HM 15 Cl. Schnörr / HM 16

Aktives und passives Warten (1) Aktives und passives Warten (2) Aktives Warten (busy waiting): Ausführen einer Schleife, bis eine Variable einen bestimmten Wert annimmt Th ist bereit und belegt die CPU Variable muss von einem anderen Th gesetzt werden (großes) Problem, wenn der andere Th endet (großes) Problem, wenn anderer Th -- z.b. wegen niedriger Priorität -- nicht dazu kommt, Variable zu setzen auch Pollen/Polling genannt Passives Warten (sleep and wake): Th blockiert und wartet auf Ereignis, das ihn in den Zustand bereit versetzt blockierter Th verschwendet keine CPU-Zeit anderer Th muss Eintreten des Ereignisses bewirken (kleines) Problem, wenn anderer Th endet bei Ereignis muss blockierter Th geweckt werden explizit durch anderen Th durch Mechanismen des BS Cl. Schnörr / HM 17 Cl. Schnörr / HM 18 Erzeuger-Verbraucher-Problem (1) Erzeuger-Verbraucher-Problem (2) Erzeuger-Verbraucher-Problem (producer-consumer problem, bounded-buffer problem) zwei kooperierende Ths: Erzeuger speichert Informationspaket in beschränktem Puffer Verbraucher liest Informationen aus diesem Puffer : Puffer nicht überfüllen: wenn Puffer voll, muß Erzeuger warten, bis Verbraucher ein weiteres Paket abgeholt hat nicht aus leerem Puffer lesen: wenn Puffer leer, muß Verbraucher warten, bis Erzeuger ein weiteres Paket abgelegt hat Realisierung mit passivem Warten: eine gemeinsame Variable count zählt belegte Positionen im Puffer wenn Erzeuger ein Paket einstellt und Puffer leer war (count == 0) --> wecken des Verbrauchers wenn Verbraucher ein Paket abholt und Puffer voll war (count == max) --> wecken des Erzeugers Cl. Schnörr / HM 19 Cl. Schnörr / HM 20

Erzeuger-Verbraucher mit sleep-wake Deadlock-Problem bei sleep / wake (1) #define N 100 int count = 0; //Anzahl der Plätze im Puffer //Anzahl der belegten Plätze im Puffer Race-Condition im vorigen Programm --> potentieller Deadlock, z.b. producer() { while (TRUE) { // Endlosschleife produce( item ); // Erzeuge etwas für den Puffer if (count == N) sleep(); // Wenn Puffer voll: schlafen legen enter( item ); // In den Puffer einstellen count = count + 1; //Zahl belegter Plätze inkrementieren if (count == 1) wake(consumer); //war der Puffer vorher leer? consumer() { while (TRUE) { // Endlosschleife if (count == 0) sleep(); // Wenn Puffer leer: schlafen legen remove_item (item); // Etwas aus dem Puffer entnehmen count = count - 1; // Zahl belegter Plätze dekrementieren if (count == N-1) wake(producer); //war der Puffer vorher voll? consume_item (item); // Verarbeiten Verbraucher liest Variable count, die den Wert 0 hat Kontextwechsel zum Erzeuger: Erzeuger stellt etwas in den Puffer, erhöht count und weckt Verbraucher, da count==0 war Kontextwechsel zum Verbraucher: Verbraucher legt sich schlafen, da count==0 gelesen wurde Erzeuger schreibt Puffer voll und legt sich auch schlafen Cl. Schnörr / HM 21 Cl. Schnörr / HM 22 Deadlock-Problem bei sleep / wake (2) Standardprimitive zur Ursache des Problems: Wakeup-Signal für einen -- noch nicht -- schlafenden Prozess wird ignoriert --> Weckaufruf irgendwie aufbewahren Lösungsmöglichkeit: Systemaufrufe sleep() und wake() verwenden wakeup pending bit bei wake() für nicht schlafenden Th dessen wakeup-pending-bit setzen bei sleep() das wakeup-pending-bit des Ths prüfen und falls gesetzt, nicht schlafen legen Welche Standardprimitive zur gibt es? Mutex (mutual exclusion) = binärer Semaphor Semaphor Event (ähnlich Condition-Variable) Monitor Locking aber: Lösung lässt sich nicht verallgemeinern (mehrere Prozesse benötigen weitere Bits). Cl. Schnörr / HM 23 Cl. Schnörr / HM 24

Mutex (1) Mutex (2) Mutex (mutual exclusion) = binärer Semaphor zur eines kritischen Bereichs bzw. gemeinsamer Daten kann nur 2 Zustände / Werte annehmen: true / frei: Zugang erlaubt false / gesperrt: Zugang gesperrt Anforderungs- und Freigabe-Operationen: Anforderung: wait() / lock() / get() Freigabe: signal() / unlock() / release() Anforderung: ein Th, der eine bereits vergebene Mutex anfordert, blockiert --> Warteschlange wait() und signal()-operationen sind selbst kritische Abschnitte --> atomar realisiert Implementierung als System-Calls und Verhinderung von Kontext-Wechseln (z.b. durch kurzzeitiges Ausschalten von Interrupts) wait( mutex ) { if ( mutex == 1 ) mutex = 0; else BLOCK_CALLER; signal( mutex ) { if ( P in QUEUE(mutex)) { wakeup( P ); remove( P, QUEUE ); else mutex = 1; Freigabe: Warteschlange enthält Ths Warteschlange leer --> einen wecken --> Mutex auf true Cl. Schnörr / HM 25 Cl. Schnörr / HM 26 Semaphor (1) Semaphor (2) Semaphor: Integer- (Zähler-) Variable mit festgelegtem Anfangswert N ( Anzahl verfügbarer Ressourcen ) Anforderung (wait()): Varianten: negative Semaphor-Werte: Anforderung (wait()): - Wert immer um 1 reduzieren - --> Wert entspricht Zahl blockierter Ths in Warteschlange falls >= 1: falls == 0: Wert um 1 reduzieren Th blockieren --> Warteschlange Freigabe (signal()): - Wert immer um 1 erhöhen Freigabe (signal()): falls Warteschlange nicht leer: falls Warteschlange leer: einen Th wecken Wert um 1 erhöhen nicht-blockierend: z.b. bool ret = try_lock(); pths-semaphore: sind auch zwischen Prozessen verwendbar (Standard: nur Ths) wait( &sem ); // Kode, der die Ressource nutzt signal( &sem ); SysV-IPC Semaphore: arbeiten prozessübergreifend sind nach Prozessende noch vorhanden Cl. Schnörr / HM 27 Cl. Schnörr / HM 28

Semaphor (3) Erzeuger-Verbraucher-Problem mit Semaphoren Beispiel: Verbraucher: Ths A,B,C: wait() Erzeuger: Th D: signal() Semaphore s zählt verfügbare Ressource typedef int semaphore; semaphoremutex = 1; // Kontrolliert Zugriff auf Puffer semaphoreempty = N; // Zählt freie Plätze im Puffer semaphorefull = 0; // Zählt belegte Plätze im Puffer producer() { while (TRUE) { // Endlosschleife produce_item(item); // Erzeuge etwas für den Puffer wait(empty); // Leere Plätze dekrementieren bzw. blockieren wait(mutex); // Eintritt in den kritischen Bereich enter_item(item); // In den Puffer einstellen signal(mutex); // Kritischen Bereich verlassen signal(full); // Belegte Plätze erhöhen, evtl. consumer wecken consumer() { while (TRUE) { // Endlosschleife wait(full); // Belegte Plätze dekrementieren bzw. blockieren wait(mutex); // Eintritt in den kritischen Bereich remove_item(item); // Aus dem Puffer entnehmen signal(mutex); // Kritischen Bereich verlassen signal(empty); // Freie Plätze erhöhen, evtl producer wecken consume_entry(item) // Verbrauchen Cl. Schnörr / HM 29 Cl. Schnörr / HM 30 Events (1) Events (2) Events: Beispiel: kein Th besitzt einen Event (<-> Mutex) wait() blockiert, falls Event nicht im signalisierten Zustand automatischer Event: th A. event.wait().. th B.. event.signal();. t # ths waiting after each call: 2 0 0 0 0 0 0 0 manual 2 1 0 0 0 1 2 0 automatic manual automatic jedes wait() setzt Event automatisch zurück (reset) signalled falls mehrere Ths warten: bei einem signal() kehrt nur genau ein Th von seinem wait() zurück manueller Event: Event muß manuell zurückgesetzt werden (reset) non-signalled signal() signal() signal() signal() wait() wait() reset() Zeit t falls mehrere Ths warten: bei einem signal() kehren alle Ths von ihren wait() zurück Cl. Schnörr / HM 31 Cl. Schnörr / HM 32

Monitor (1) Monitor (2) Problem bei Arbeit mit Mutexen und Semaphoren: Programmierer gezwungen, kritische Bereiche mit wait() und signal() abzusichern schon bei einem Fehler --> Synchronisierung versagt Monitor: muss von Programmiersprache unterstützt werden (z.b. Java, Concurrent-Pascal) Sammlung von Prozeduren, Variablen, speziellen Bedingungsvariablen: Prozesse können Prozeduren des Monitors aufrufen, aber nicht auf dessen Datenstrukturen zugreifen kapselt kritische Bereiche zu jedem Zeitpunkt nur ein einziger Prozess im Monitor aktiv (Monitor-Prozedur ausführen) Freigabe durch Verlassen der Monitor-Prozedur Cl. Schnörr / HM 33 Cl. Schnörr / HM 34 Monitor (3) Monitor (4) Monitor-Konzept erinnert an Klassen (Objektorientierung) Module Beispiel: Zugriff auf Festplatte mit Mutex: Kapselung: nur Zugriff über public-prozeduren Monitor wird von Programmiersprache / Compiler bereitgestellt nicht der Programmierer ist für gegenseitigen Ausschluß verantwortlich Klasse, bei der jede public-methode synchronisiert ist mutex disc_access = 1; wait( disc_access ); // Daten lesen signal( disc_access ); wait( disc_access ); // Daten schreiben signal( disc_access ); Was tun, wenn Prozess im Monitor blockieren muss? Condition-Variablen (Zustandsvariable): gleiches Beispiel: mit Monitor: monitor disc { entry ( discaddr, memaddr ) { // Daten lesen ; entry ( discaddr, memaddr ) { //Daten schreiben ; init() { // Gerät initialisieren ; ; monitor disc; disc.( da, ma ); disc.( da, ma ); Idee: Prozess muss auf Eintreten einer Bedingung (Condition) warten m_wait( var ): aufrufenden Prozess sperren (er gibt den Monitor frei) m_signal( var ): gesperrte(n) Prozess(e) wecken erfolgt unmittelbar vor Verlassen des Monitors Cl. Schnörr / HM 35 Cl. Schnörr / HM 36

Monitor (5) Monitor (6) Monitor Process 1 Prozess 2 Gesperrte Prozesse landen in Warteschlange zu entsprechender Condition-Variable Daten Interne Warteschlangen haben Vorrang vor Prozesszugriffen von außen Condition- Variable cv f() g() Implementierung mit Mutex / Semaphor: Prozedur Prozedur f() { wait( cv);... g() {... signal( cv); m_wait(cv) m_signal(cv) Zeit t conditionvariable { int queuesize = 0; mutex m; semaphore waiting; wait() { m.lock(); queuesize++; m.release(); waiting.down(); signal() { m.lock(); while( queuesize > 0 ) { // alle wecken queuesize--; waiting.up(); m.release(); ; Cl. Schnörr / HM 37 Cl. Schnörr / HM 38 Erzeuger-Verbraucher-Problem mit Monitor Locking (1) monitor iostream { item buffer; int count; const int N = 64; condition nonempty, nonfull; entry append( item x ) { while (count == N-1) m_wait(nonfull); put(buffer, x); // count += 1; m_signal(nonempty); Locking erweitert Funktionalität von Mutexen durch Unterscheidung verschiedener Lock-Modi (Zugriffsarten) und Festlegung derer Verträglichkeit Concurrent Read: Concurrent Write: Lesezugriff, andere Schreiber erlaubt Schreibzugriff, andere Schreiber erlaubt z.b. auf oberen Lock-Hierarchien in Datenbanken entry remove(item x) { while (count == 0) m_wait(nonempty); get(buffer, x); // count -= 1; m_signal(nonfull); init() { count = 0; // Initialisierung Cl. Schnörr / HM 39 Protected Read: Protected Write: Exclusive: Lesezugriff, andere Leser erlaubt, aber keine Schreiber (share lock) Schreibzugriff, andere Leser erlaubt, aber kein weiterer Schreiber (update lock) Lese/Schreibzugriff, keine anderen Zugriffe erlaubt Th fordert Lock in bestimmtem Modus an. Ist Lock-Modus zu einem Lock eines anderen Ths unverträglich -> Blockierung Cl. Schnörr / HM 40

Locking (2) Lock- Verträglichkeiten : access sharing concurrent concurrent protected protected exclusive concurrent + + + + - concurrent protected + + - - - + - + - - Praxisbeispiele protected + - - - - exclusive -- - - - - - Cl. Schnörr / HM 41 / Praxis Cl. Schnörr / HM 42 im Linux-Kernel Atomare Operationen auf Integer-Variablen Bit-Operationen auf Bitvektoren Spin-Locks Reader-Writer-Locks Semaphore / Reader-Writer Semaphore in C++ Ths, Mutexe atomare Befehle Mutex-Objekt in C++ Praxis: Übersicht Praxis / Linux-Kernel: Atomare Integer-Operationen Typ atomic_t (24 Bit Integer): Initialisierung: atomic_t var = ATOMIC_INIT( 0 ); Wert setzen: atomic_set( &var, wert ); Addieren: atomic_add( wert, &var ); ++: atomic_inc( &var ); Subtrahieren: atomic_sub( wert, &var ); --: atomic_dec( &var ); Auslesen: int i = atomic_( &var ); res = atomic_sub_and_test( i, &var ); subtrahiert i atomar von var return true, falls Ergebnis 0, sonst false res = atomic_add_negative( i, &var ); addiert i atomar zu var return true, falls Ergebnis negativ, sonst false / Praxis Cl. Schnörr / HM 43 / Praxis / Linux-Kernel Cl. Schnörr / HM 44

Praxis / Linux-Kernel: Atomare Bit-Operationen Praxis / Linux-Kernel: Spin-Locks (1) Einzelne Bits in Bitvektoren setzen Datentyp: beliebig, z.b. unsigned long bitvektor = 0; nur über Pointer anzusprechen Anzahl der nutzbaren Bits abhängig vom verwendeten Datentyp Test-and-Set-Operationen geben zusätzlich vorherigen Wert des jeweiligen Bits zurück set_bit( i, &bv ); clear_bit( i, &bv ); change_bit( i, &bv ); Einzelne Bits auslesen: b = test_bit( i, &bv ); Suchfunktionen: i-tes Bit setzen i-tes Bil löschen i-tes Bit kippen pos = find_first_bit( &bv, length ); pos = find_first_zero_bit( &bv, length ); b = test_and_set_bit( i, &bv ); b = test_and_clear_bit( i, &bv ); b = test_and_change_bit( i, &bv ); Spin-Lock: Lock mit Mutex-Funktion: gegenseitiger Ausschluß Code, der Spin-Lock anfordert und nicht erhält blockiert nicht (kein aufwendiger Wechsel in Kernel-Mode) sondern läuft weiter (spinning), bis Lock verfügbar nur zu verwenden bei kurzen Wartezeiten auf Lock, bei Mehrprozessorsystemen sind nicht rekursiv, also z.b. nicht in rekursiven Funktionen verwendbar Typ spinlock_t spinlock_t slock = SPIN_LOCK_UNLOCKED spin_lock( &slock ): /* kritischer Abschnitt */ spin_unlock( &slock ); / Praxis / Linux-Kernel Cl. Schnörr / HM 45 / Praxis / Linux-Kernel Cl. Schnörr / HM 46 Praxis / Linux-Kernel: Spin-Locks (2) Praxis / Linux-Kernel: Reader-Writer-Locks da Spin-Locks nicht schlafen/blockieren, sind diese in Interrupt-Handlern verwendbar in diesem Fall: zusätzlich Interrupts sperren: spinlock_t slock = SPIN_LOCK_UNBLOCKED; unsigned long flags; spin_lock_irqsave( &slock, flags ); // aktuelle Interrupts sichern /* ktitischer Abschnitt */ // dann sperren spin_unlock_irq_restore( &slock, flags ); // ursprüngl. Zustand restaurieren Reader-Writer-Locks: Alternative zu normalen Locks, die mehrere Lesezugriffe zulässt, aber bei schreibendem Zugriff exklusiv ist (wie normaler Lock) wenn zu Beginn alle Interrupts aktiviert sind, geht es auch einfacher: spinlock_t slock = SPIN_LOCK_UNBLOCKED; spin_lock_irq( &slock ); /* ktitischer Abschnitt */ spin_unlock_irq( &slock ); // aktuelle Interrupts sperren auch Varianten für Interrupt-Behandlung: - _lock_irq / _unlock_irq - _lock_irq / _unlock_irq - _lock_irqsave / _unlock_irqrestore - _lock_irw_save / _unlock_irqrestore / Praxis / Linux-Kernel Cl. Schnörr / HM 47 / Praxis / Linux-Kernel Cl. Schnörr / HM 48

Praxis / Linux-Kernel: Semaphore (1) Praxis / Linux-Kernel: Semaphore (2) Kernel-Semaphore sind schlafende Locks wartende Prozesse werden in Warteschlange gestellt, bei Freigabe wird erster geweckt eignen sich für Sperren, die über längeren Zeitraum gehalten werden (<-> Spin-Lock) sind nur im Prozess-Kontext, nicht in Interrupt-Handlern einsetzbar (Interrupt-Handler werden nicht vom Scheduler behandelt) Code, der Semaphore verwendet, darf nicht bereits normalen Lock besitzen (Semaphore-Zugriff kann zum schlafen-legen führen Typ: semaphore statische Deklaration: static DECLARE_SEMAPHORE_GENERIC( name, count ); static DECLARE_MUTEX( name ); // count = 1 dynamische Deklaration: sema_init( &sem, count ); init_mutex( &sem ); // count = 1; Verwendung mit up() und down(): down( &sem ); /* kritischer bereich */ up( &sem ); / Praxis / Linux-Kernel Cl. Schnörr / HM 49 / Praxis / Linux-Kernel Cl. Schnörr / HM 50 Praxis / Linux-Kernel: Semaphore (3) Praxis: in C++ (1) Reader-Writer-Semaphore: analog zu Reader-Writer-Locks: Typ rw_semaphore, der spezielle Up- und Down- Operationen für Lese- und Schreibzugriffe erlaubt alle RW-Semaphore sind Mutexe (bei Initialisierung count=1) Lesender Code: static DECLARE_RWSEM( rwsem ); init_rwsem( &rwsem ); down_( &rwsem ); //-only Abschnitt up_( &rwsem ); Schreibender Code: down_( &rwsem ); //lesen+schreiben-abschnitt up_( &rwsem ); Ths, Mutexe, usw.: pths-bibliothek (UL-Ths): quasi-standard in Unix/Linux wenig gebräuchlich unter Windows (<-> Win-API) #include <pths.h>, linken mit libpths C++0x/C++11-Standard: Th/Mutex-API: OpenMP-Standard: #include <th> Parallelisierung von Schleifen mittels Ths #include <omp.h> #pragma omp parallel for num_ths(ncpu) for ( long y = ylow; y <= yhigh; ++y ) {... / Praxis / Linux-Kernel Cl. Schnörr / HM 51 / Praxis / C++ Cl. Schnörr / HM 52

Praxis: in C++ (2) Praxis: in C++ (3) Nachbildung eines Monitors mittels pths struct ProducerConsumer { pth_mutex_t count_mtx; //zu initialisieren pth_cond_t full, empty; int count; void enter() { pth_mutex_lock( &count_mtx ); while ( count == N-1 ) pth_cond_wait( &full, &count_mtx ); enter_item(); count++; if ( count == 0 ) pth_cond_signal( &empty ); pth_mutex_unlock( &count_mtx ); void remove() { pth_mutex_lock( &count_mtx ); while ( count == 0 ) pth_cond_wait( &empty, &count_mtx ); remove_item(); count--; if (count == N-1) pth_cond_signal( &full ); pth_mutex_unlock( &count_mtx ); ; / Praxis / C++ Erzeuger- Verbraucherprogramm ProducerConsumer pc; void producer() { produce_item(); pc.enter(); void consumer() { pc.remove(); consume_item(); Cl. Schnörr / HM 53 Atomare Befehle (--> Literatur): C++0x/C++11-Standard: Atomic-API: OpenMP-Standard: #include <atomic> #pragma omp atomic newline statement_expression #pragma omp critical { statements;... Compiler-Unterstützung: sync_lock_test_and_set( &lock, val ); // g++, testandset( _lock, 0 ); InterlockedExchange( (long *)&lock, val ); // msvc,, testandset( _lock, 0 ); / Praxis / C++ Cl. Schnörr / HM 54 Praxis: in C++ (4) Mutex-Objekt: RefCount::refcount; //in mehreren Objekten int RefCount::dec_refcount() { pth_mutex_lock( &lock ); --refcount; pth_mutex_unlock( &lock ); Kontextwechsel vor return --> Vor Rückgabe Veränderung von refcount durch andere Ths möglich return refcount; //not ok! Lösung: Mutex in Konstruktor und Destruktor class MutexObj { private: MutexObj( pth_mutex_t & lock ) : _lock(lock) { pth_mutex_lock( &_lock ); ~MutexObj() { pth_mutex_unlock( &_lock ); BS-I pth_mutex_t / & /_lock; Praxis / C++ ; int RefCount::dec_refcount() { MutexObj lock( refcount_lock ); return --refcount; //ok von refcount bis nach lokaler Kopie bei return Aber: nur ok bei return-per-value! Cl. Schnörr / HM 55