Interprozesskommunikation (IPC) Interprozesskommunikation Prozesse arbeiten oft nicht allein, sondern müssen Informationen austauschen, um eine gemeinsame Aufgabe zu erfüllen. Prozess 0 3 wichtige Fragen sind zu klären: Prozess 1 Interprozesskommunikation Prozesse arbeiten oft nicht allein, sondern müssen Informationen austauschen, um eine gemeinsame Aufgabe zu erfüllen. Prozess 0 Prozess 1 3 wichtige Fragen sind zu klären: Wie werden Daten ausgetauscht? Interprozesskommunikation Prozesse arbeiten oft nicht allein, sondern müssen Informationen austauschen, um eine gemeinsame Aufgabe zu erfüllen. Prozess 0 Prozess 1 3 wichtige Fragen sind zu klären: Wie werden Daten ausgetauscht? Wie wird sichergestellt, dass Prozesse nicht gleichzeitig auf gemeinsame Informationen zugreifen? Interprozesskommunikation Prozesse arbeiten oft nicht allein, sondern müssen Informationen austauschen, um eine gemeinsame Aufgabe zu erfüllen. Prozess 0 Prozess 1 3 wichtige Fragen sind zu klären: Wie werden Daten ausgetauscht? Wie wird sichergestellt, dass Prozesse nicht gleichzeitig auf gemeinsame Informationen zugreifen? Wie wird die richtige Reihenfolge des Zugriffs sicher gestellt? Interprozesskommunikation Prozesse arbeiten oft nicht allein, sondern müssen Informationen austauschen, um eine gemeinsame Aufgabe zu erfüllen. Prozess 0 Prozess 1 3 wichtige Fragen sind zu klären: Wie werden Daten ausgetauscht? Wie wird sichergestellt, dass Prozesse nicht gleichzeitig auf gemeinsame Informationen zugreifen? Wie wird die richtige Reihenfolge des Zugriffs sicher gestellt? Synchronisationsprobleme Kommunikationsformen Gemeinsame Variablen: vor allem in Ein-Prozessor und MultiprozessorSystemen mit gemeinsamem physikalischen Speicher Kommunikationsformen Gemeinsame Variablen: vor allem in Ein-Prozessor und MultiprozessorSystemen mit gemeinsamem physikalischen Speicher Nachrichtenaustausch: vor allem bei verteilten Systemen, also Kommunikation über Rechnergrenzen hinweg Nachrichtensysteme Kommunikation über Nachrichten wird auch Message-Passing genannt. Nachrichtensysteme Kommunikation über Nachrichten wird auch Message-Passing genannt. Gebraucht werden zwei Operationen: send(destination, message) Sendet eine Nachricht an einen Empfänger (destination). receive(source, message) Empfängt eine Nachricht von einer Quelle (source). Nachrichtensysteme Interessante Probleme beim Message Passing Wie können Sender und Empfänger eindeutig zugewiesen werden? Was passiert, wenn Nachrichten verloren gehen? Woher weiß der Sender, dass seine Nachricht verloren gegangen ist? Wie verhindert man, dass Prozesse Nachrichten beliebig „spammen“ können? Nachrichtensysteme Interessante Probleme beim Message Passing Wie können Sender und Empfänger eindeutig zugewiesen werden? Was passiert, wenn Nachrichten verloren gehen? Woher weiß der Sender, dass seine Nachricht verloren gegangen ist? Wie verhindert man, dass Prozesse Nachrichten beliebig „spammen“ können? Technologie 12.2: Vernetzte Systeme Synchronisationsprobleme Probleme treten auf, wenn zwei Prozesse gleichzeitig auf dieselbe Ressource zugreifen. Beispiel: Zwei Prozesse teilen sich die Variable count=5 Synchronisationsprobleme Probleme treten auf, wenn zwei Prozesse gleichzeitig auf dieselbe Ressource zugreifen. Beispiel: Prozess 1 Zwei Prozesse teilen sich die Variable count=5 Prozess 2 Synchronisationsprobleme Probleme treten auf, wenn zwei Prozesse gleichzeitig auf dieselbe Ressource zugreifen. Beispiel: Zwei Prozesse teilen sich die Variable count=5 Prozess 1 Prozess 2 Maschinenbefehle P1: P2: P3: register1 := count register1 := register1+1 count := register1 C1: C2: C3: register2 := count register2 := register2-1 count := register2 Synchronisationsprobleme Prozess 1 P1: P2: P3: register1 := count register1 := register1+1 count := register1 Prozess 2 C1: C2: C3: register2 := count register2 := register2-1 count := register2 Was liefert die Ausführung der Befehle in der Reihenfolge P1,P2,C1,C2,P3,C3 ? Synchronisationsprobleme Prozess 1 P1: P2: P3: register1 := count register1 := register1+1 count := register1 Prozess 2 C1: C2: C3: register2 := count register2 := register2-1 count := register2 Was liefert die Ausführung der Befehle in der Reihenfolge P1,P2,C1,C2,P3,C3 ? P1: P2: P3: register1 := count register1 := register1+1 C1: C2: register2 := count register2 := register2-1 C3: count := register2 count := register1 Synchronisationsprobleme Prozess 1 P1: P2: P3: register1 := count register1 := register1+1 count := register1 Prozess 2 C1: C2: C3: register2 := count register2 := register2-1 count := register2 Was liefert die Ausführung der Befehle in der Reihenfolge P1,P2,C1,C2,P3,C3 ? P1: P2: P3: register1 := count register1 := register1+1 C1: C2: register2 := count register2 := register2-1 C3: count := register2 count := register1 Die Bearbeitungsreihenfolge liefert den falschen Wert 4. Race Conditions Race Condition: Eine Race Condition ist eine Konstellation, in der das Ergebnis einer Operation vom zeitlichen Verhalten bestimmter Einzeloperationen abhängt. Race Conditions Race Condition: Eine Race Condition ist eine Konstellation, in der das Ergebnis einer Operation vom zeitlichen Verhalten bestimmter Einzeloperationen abhängt. Wir möchten Race Conditions unbedingt vermeiden. Kritischer Abschnitt • Ein kritischer Abschnitt (critical section) ist der Teil eines Programms, in dem auf die gemeinsam genutzten Daten zugegriffen wird. Kritischer Abschnitt • Ein kritischer Abschnitt (critical section) ist der Teil eines Programms, in dem auf die gemeinsam genutzten Daten zugegriffen wird. Zwei Prozesse dürfen niemals gleichzeitig in ihrem kritischen Abschnitt sein. Diese Forderung nennt man wechselseitigen Ausschluss (mutual exclusion). Keine Race Conditions! Wechselseitiger Ausschluss durch aktives Warten Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 1: Unterbrechungen ausschalten disable_interrupts(); critical_region(); enable_interrupts(); Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 1: Unterbrechungen ausschalten disable_interrupts(); critical_region(); enable_interrupts(); Problem: Prozess könnte Unterbrechungen nicht mehr einschalten. Prozess könnte in seinem kritischen Bereich in eine Endlosschleife geraten. Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 1: Unterbrechungen ausschalten disable_interrupts(); critical_region(); enable_interrupts(); • Umsetzungsmöglichkeit 2: Nutzung einer globalen Sperrvariablen while(sperre==true) { wait_short_time(); } sperre=true; critical_region(); sperre=false; Problem: Prozess könnte Unterbrechungen nicht mehr einschalten. Prozess könnte in seinem kritischen Bereich in eine Endlosschleife geraten. Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 1: Unterbrechungen ausschalten disable_interrupts(); critical_region(); enable_interrupts(); • Problem: Prozess könnte Unterbrechungen nicht mehr einschalten. Prozess könnte in seinem kritischen Bereich in eine Endlosschleife geraten. Umsetzungsmöglichkeit 2: Nutzung einer globalen Sperrvariablen while(sperre==true) { wait_short_time(); } sperre=true; critical_region(); sperre=false; Problem: Auch hier gibt es Race Conditions. Was passiert, wenn Prozess 0 an dieser Stelle von Prozess 1 unterbrochen wird? Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 3: Petersons Lösung für 2 Prozesse int turn; boolean interested[2]; Prozess 0 enter_region(0); void enter_region(int process) critical_region(); { leave_region(0); int other=1-process; interested[process]=true; turn=process; while(turn==process && interested[other]==true) {} } void leave_region(int process) { interested[process]=false; } Wechselseitiger Ausschluss durch aktives Warten • Umsetzungsmöglichkeit 3: Petersons Lösung für 2 Prozesse int turn; boolean interested[2]; Prozess 0 enter_region(0); void enter_region(int process) critical_region(); { leave_region(0); int other=1-process; interested[process]=true; turn=process; while(turn==process && interested[other]==true) {} } void leave_region(int process) Diese Lösung kann keine Race Conditions erzeugen! { interested[process]=false; } Wechselseitiger Ausschluss durch Sleep und Wakeup • Petersons Lösung arbeitet korrekt, aktives Warten ist aber eine Verschwendung von Rechenzeit. Wechselseitiger Ausschluss durch Sleep und Wakeup • Petersons Lösung arbeitet korrekt, aktives Warten ist aber eine Verschwendung von Rechenzeit. • Bessere Lösung: ein Prozess soll nicht warten, sondern sich selbst in einen „Schlafzustand“ versetzen können. Es können dann solange andere Prozesse bearbeitet werden, bis ein anderer Prozess ihn „aufweckt“. Wechselseitiger Ausschluss durch Sleep und Wakeup • Petersons Lösung arbeitet korrekt, aktives Warten ist aber eine Verschwendung von Rechenzeit. • Bessere Lösung: ein Prozess soll nicht warten, sondern sich selbst in einen „Schlafzustand“ versetzen können. Es können dann solange andere Prozesse bearbeitet werden, bis ein anderer Prozess ihn „aufweckt“. • Wir benötigen dazu zwei Systemaufrufe: sleep() wakeup(process) Versetzt den Prozess in den Schlafzustand. Weckt den angegebenen Prozess auf. Das Erzeuger-Verbraucher-Problem Buffer Verbraucher Erzeuger Problem 1: Was passiert wenn der Verbraucher lesen möchte, der Buffer aber leer ist? Problem 2: Was passiert, wenn der Erzeuger schreiben möchte, der Buffer aber voll ist? Das Erzeuger-Verbraucher-Problem Buffer Verbraucher Erzeuger Problem 1: Was passiert wenn der Verbraucher lesen möchte, der Buffer aber leer ist? Problem 2: Was passiert, wenn der Erzeuger schreiben möchte, der Buffer aber voll ist? Lösung durch Sleep und Wakeup. Einfache Lösung Erzeuger-Verbraucher-Problem int count = 0; void producer() { int item; while(true){ item = produce_item(); if(count==100) sleep(); insert_item(item); count++; if(count==1) wakeup(consumer); } } void consumer() { int item; while(true){ if(count==0) sleep(); item = remove_item(); count--; if(count==99) wakeup(producer); consume_item(item); } } Einfache Lösung Erzeuger-Verbraucher-Problem int count = 0; void consumer() void producer() { { int item; int item; while(true){ while(true){ if(count==0) item = produce_item(); sleep(); item = remove_item(); if(count==100) Lösung ist falsch! count--; sleep(); if(count==99) Race Conditions sind möglich! insert_item(item); wakeup(producer); count++; consume_item(item); if(count==1) } } wakeup(consumer); } } Einfache Lösung Erzeuger-Verbraucher-Problem int count = 0; void producer() { int item; while(true){ item = produce_item(); if(count==100) sleep(); insert_item(item); count++; if(count==1) wakeup(consumer); } } void consumer() { int item; while(true){ if(count==0) sleep(); item = remove_item(); count--; if(count==99) wakeup(producer); consume_item(item); } } Wird der Consumer hier unterbrochen, kurz bevor er sleep aufruft, so läuft der wakeup des Producers ins Leere und der Consumer schläft ewig. Irgendwann schläft auch der Producer ein und kann nicht mehr aufgeweckt werden. Lösung durch Semaphoren • Semaphoren sind spezielle Variablen (und speichern meist ganze Zahlen). • Zugriff auf Semaphoren nur über die Systemaufrufe up und down möglich • Um Race Conditions zu vermeiden, werden Unterbrechungen vom Betriebssystem ausgeschaltet, während up oder down abgearbeitet wird.. . Lösung durch Semaphoren • Semaphoren sind spezielle Variablen (und speichern meist ganze Zahlen). • Zugriff auf Semaphoren nur über die Systemaufrufe up und down möglich • Um Race Conditions zu vermeiden, werden Unterbrechungen vom Betriebssystem ausgeschaltet, während up oder down abgearbeitet wird. down-Systemaufruf down(semaphore s) { if(s==0) sleep(); s--; } . . Lösung durch Semaphoren • Semaphoren sind spezielle Variablen (und speichern meist ganze Zahlen). • Zugriff auf Semaphoren nur über die Systemaufrufe up und down möglich • Um Race Conditions zu vermeiden, werden Unterbrechungen vom Betriebssystem ausgeschaltet, während up oder down abgearbeitet wird. . down-Systemaufruf up-Systemaufruf down(semaphore s) { if(s==0) sleep(); s--; } up(semaphore s) { s++; if(s==1) wakeup(randomProcess); } War die Semaphore vorher Null, wird zufällig einer der schlafenden Prozesse aufgeweckt. Lösung durch Semaphoren semaphore mutex = 1; semaphore empty = 100; semaphore full = 0; void producer() { int item; while(true){ item = produce_item(); down(empty); down(mutex); insert_item(item); up(mutex); up(full); } } void consumer() { int item; while(true){ down(full); down(mutex); item = remove_item(); up(mutex); up(empty); consume_item(item); } } Lösung durch Semaphoren semaphore mutex = 1; semaphore empty = 100; semaphore full = 0; Die Semaphoren empty und full zählen die Anzahl der vorhandenen Nachrichten und synchronisieren damit die Aktionen von Producer und Consumer. void producer() { int item; while(true){ item = produce_item(); down(empty); down(mutex); insert_item(item); up(mutex); up(full); } } void consumer() { int item; while(true){ down(full); down(mutex); item = remove_item(); up(mutex); up(empty); consume_item(item); } } Lösung durch Semaphoren semaphore mutex = 1; semaphore empty = 100; semaphore full = 0; Die Semaphore mutex nimmt nur die beiden Werte 0 und 1 an. Sie steuert den Zugang zum kritischen Bereich von Consumer und Producer. Eine solche Semaphore wird auch Mutex genannt. void producer() { int item; while(true){ item = produce_item(); down(empty); down(mutex); insert_item(item); up(mutex); up(full); } } void consumer() { int item; while(true){ down(full); down(mutex); item = remove_item(); up(mutex); up(empty); consume_item(item); } } Kritischer Bereich Typische Synchronisationsprobleme Typische Synchronisationsprobleme: – – – – – Erzeuger-Verbraucher-Problem Raucherproblem Speisende-Philosophen-Problem Leser-Schreiber-Problem Schlafender-Friseur-Problem Typische Synchronisationsprobleme Typische Synchronisationsprobleme: – – – – – Erzeuger-Verbraucher-Problem Raucherproblem Speisende-Philosophen-Problem Leser-Schreiber-Problem Schlafender-Friseur-Problem Alle diese Probleme können mit Hilfe von Semaphoren und Mutexen gelöst werden. Typische Synchronisationsprobleme Typische Synchronisationsprobleme: schon behandelt – Erzeuger-Verbraucher-Problem – – – – Raucherproblem Speisende-Philosophen-Problem Leser-Schreiber-Problem Schlafender-Friseur-Problem Alle diese Probleme können mit Hilfe von Semaphoren und Mutexen gelöst werden. Typische Synchronisationsprobleme Typische Synchronisationsprobleme: schon behandelt – Erzeuger-Verbraucher-Problem – behandeln sie als TNW – – behandeln wir jetzt – Raucherproblem Speisende-Philosophen-Problem Leser-Schreiber-Problem Schlafender-Friseur-Problem Alle diese Probleme können mit Hilfe von Semaphoren und Mutexen gelöst werden. Problem des schlafenden Friseurs - 1 Friseur - 1 Friseur-Stuhl - n Stühle für wartende Kunden - Gibt es keine wartenden Kunden, schläft der Friseur. - Kommt ein Kunde herein, muss er den schlafenden Friseur wecken. - Kommt ein Kunde herein, während ein anderer Kunde frisiert wird, setzt er sich hin (falls Plätze frei sind) oder verlässt den Laden. Problem des schlafenden Friseurs const int CHAIRS = 5; semaphore customers = 0; semaphore barbers=0; semaphore mutex=1; int waiting = 0; void barber() { while(true){ down(customers); down(mutex); waiting--; up(barbers); up(mutex); cut_hair(); } } void customer() { down(mutex); if(waiting<CHAIRS){ waiting++; up(customers); up(mutex); down(barbers); get_haircut(); } else { up(mutex); } } Kritische Abschnitte und Semaphoren in Java Kritische Abschnitte in Java durch den Modifier synchronized markieren: synchronized void foo() { i++; } Kritische Abschnitte und Semaphoren in Java Kritische Abschnitte in Java durch den Modifier synchronized markieren: synchronized void foo() { i++; } Semaphoren durch die Klasse Semaphore: