Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Prozesskoordination Prof. Dr. W. Riggert. Ausgangssituation Prozesse müssen häufig untereinander kommunizieren, z.B. Pipes in UNIX. Zwei Prozesse heißen.

Ähnliche Präsentationen


Präsentation zum Thema: "Prozesskoordination Prof. Dr. W. Riggert. Ausgangssituation Prozesse müssen häufig untereinander kommunizieren, z.B. Pipes in UNIX. Zwei Prozesse heißen."—  Präsentation transkript:

1 Prozesskoordination Prof. Dr. W. Riggert

2 Ausgangssituation Prozesse müssen häufig untereinander kommunizieren, z.B. Pipes in UNIX. Zwei Prozesse heißen nebenläufig, wenn sie unabhängig voneinander verarbeitet werden. Für nebenläufige Prozesse ist es gleich, ob erst A und dann B erst B und dann A A und B gleichzeitig ausgeführt werden. Mit Nebenläufigkeit ist ein Wettstreit um knappe Betriebsmittel eines Rechners verbunden. Zwei nebenläufige Prozesse sind also bezüglich ihres Ablaufs unabhängig, um Ressourcen konkurrieren sie sehr wohl unter- einander. Daher kann durch wechselseitige oder gemeinsame Nutzung der Betriebsmittel ein Konflikt entstehen.

3 Konfliktbeispiel Eine Druckerwarteschlange besteht aus durchnummerierten Einträgen und entsprechenden Dateinamen. Die Variable OUT verweist auf den nächsten zu druckenden Eintrag, IN auf den nächsten freien. Auf beide Variable haben alle Prozesse Zugriff. Seien die Einträge 0-3 leer und 4-6 belegt, so dass OUT=4 und IN=7 ist. Prozess A und B entscheiden sich gleichzeitig, eine Datei zu drucken : 1. A liest IN ein und speichert ihren Wert 7 lokal. 2. Der Prozessor wechselt von A nach B. 3. B liest IN ein, speichert seine zu druckende Datei an Eintrag 7 und erhöht IN auf Der Prozessor wechselt zu A und findet in IN noch den Wert 7. Er überschreibt den dort gefundenen Dateinamen von B und setzt seinerseits IN auf Das Spoolverzeichnis ist konsistent, so dass der Druckprozess keinen Fehler erkennt. Der Druckauftrag von B wird allerdings nie ausgeführt

4 Bei einer parallelen Verarbeitung kann man im allgemeinen keine Angaben über die relative Geschwindigkeit der beteiligten Prozesse machen. Benutzen mehrere Prozesse eine Ressource gemeinsam, kann es zu beliebigen Verzahnungen kommen : Führt ein Prozess die Zuweisung x = 275 aus, ein anderer parallel dazu x = 389, so ist nicht klar, ob x schließlich den Wert 275 oder 389 erhält oder gar einen völlig anderen Wert, der aus einer Mischung der Bitmuster von 275 und 389 entsteht. Wechselseitiger Ausschluss 1

5 Wechselseitiger Ausschluss 2 Bei zwei nebenläufigen Prozessen lässt sich der relative Bearbeitungsfortschritt nicht vorhersagen. concurrency-example.cc #include #include "concurrent.h" int a=1, b=2; f(){ a=a+b; a=2*a; } g(){ a=5; } main(){ concurrent(f,g); // nebenlaeufiger Aufruf cout << a; } Die Aufrufe von f und g erfolgen nebenläufig. Welcher Wert von a wird am Ende ausgegeben?

6 Wechselseitiger Ausschluss 2a Lösungsvielfalt: Mehrere zeitliche Abfolgen für die Ausführung der Wertzuweisungen sind möglich :

7 Wechselseitiger Ausschluss 3 Wenn zwei unabhängige Prozesse auf gemeinsame Daten zugreifen, kann es zu Kollisionen kommen, da der Scheduler zu einem unvorhersehbaren Zeitpunkt zwischen ihnen umschalten kann. Dies ist nur solange kein Problem, wie jeder Prozess seine eigenen Variablen besitzt.

8 Kritischer Bereich 1 Definition Es sei nun eine Anzahl F von Prozessen gegeben, die eine gemeinsame Ressource x benutzen. Ist A eine Anweisungsfolge eines Prozesses P aus F, in der x benutzt wird, d.h. existiert eine Anweisungsfolge in mehreren Prozessen, in der auf die gemeinsame Ressource x zugegriffen wird, dann heißt A kritischer Bereich von P. Um unvorhergesehene Effekte zu vermeiden, ist darauf zu achten, dass sich nicht zwei Prozesse aus F gleichzeitig in kritischen Bereichen befinden, von denen einer x verändert, d.h. falls ein Prozess eine gemeinsame Ressource nutzt, schließt er alle anderen von deren Gebrauch aus. Da dieser wechselseitige Ausschluss eine Einschränkung der Parallelität bedeutet, sollten die kritischen Bereiche möglichst klein sein.

9 Bernstein-Bedingung Wie findet man möglichst kleine kritische Bereiche? oder umgekehrt wann können Anweisungen A1,...,An gefahrlos parallel ausgeführt werden ? Dies ist dann der Fall, wenn sie konfliktfrei sind, d.h. für i<>j keine in Ai gelesene oder geschriebene Variable in Aj geschrieben wird (Bernstein 1966).

10 Kritischer Bereich 2 Parallele Prozesse sind so zu programmieren, dass ihre kritischen Bereiche als unteilbare Aktionen unter Ausschluss der übrigen Prozesse ausführt werden. Um diesen Zustand zu erreichen, sind vier Bedingungen notwendig : 1.Gegenseitiger Ausschluss (mutex): Wenn sich ein Prozess in seinem kritischen Bereich befindet,darf kein anderer Prozess den entsprechenden Bereich ausführen. 2. Kein Verhungern (starvation): Ein Prozess soll nicht unendlich warten, bis er den kritischen Bereich betreten darf. 3. Sofortige Entscheidung (indefinite postponement): Wenn mehr als ein Prozess den kritischen Bereich betren wollen, muss sofort entschieden werden, welcher die Erlaubnis erhält. 4. Echter Wettbewerb: Prozesse ausserhalb des kritischen Bereichs dürfen keinen anderen Prozess daran hindern, diesen zu betreten.

11 Verbot von Interrupts Ablauf : Jeder Prozess sperrt vor Eintritt in seinen kritischen Bereich alle Unter- brechungsmöglichkeiten durch andere Prozesse und lässt Interrupts erst nach Verlassen des kritischen Bereichs wieder zu. Problem : Die Prozessverarbeitung erfolgt korrekt, jedoch hat ein Benutzerprozess das Recht, systemseitige Unterbrechungen zu verhindern und damit Systemaufgaben zu blockieren.

12 Sperrsynchronisation 1 Prozesse synchronisieren sich über die Sperre eines Objekts x : Prozesse bewerben sich um die Sperre von x. Ist diese gesetzt, müssen sie warten. Ansonsten darf einer der Prozesse sie setzen, um alle anderen auszuschließen Benötigt ein Prozess die Sperre nicht mehr, hebt er sie auf, so dass andere Prozesse an die Reihe kommen können.

13 Sperrsynchronisation 2 Beispiel: 1. Sei S eine Betriebsmittelvariable und mit S=0 initialisiert. 2. Ein Prozess P erhält die Berechtigung, das Betriebsmittel anzufordern, setzt S=1 und tritt in den kritischen Bereich ein. 3. S=1 zeigt konkurrierenden Prozessen zu P an, dass sie warten müssen, bis S auf den Wert 0 zurückgesetzt ist. 4. S=0 bedeutet, dass sich kein anderer Prozess im kritischen Bereich befindet, S=1 heißt, ein Prozess hat den kritischen Bereich betreten. Problem : Liest ein Prozess P die Variable mit S=0 ein und findet sofort ein Prozess- wechsel zu R statt, kann auch R die Variable S von 0 auf 1 setzen. Kehrt das Ausführungsrecht zu P zurück (obwohl R sich noch im kritischen Bereich befindet), weist er S den Wert 1 zu. Beide Prozesse befinden sich nun im kritischen Bereich.

14 Striktes Alternieren 1 Beispiel: Für zwei Prozesse gelte folgende Sequenz : Prozess 0 : while (S != 0) warte Kritischer Bereich S := 1 nichtkritischer Bereich Prozess 1 : while (S != 1) warte Kritischer Bereich S := 0 nichtkritischer Bereich

15 Striktes Alternieren 2 Ablauf : 1. S ist mit dem Wert 0 initialisiert 2. Prozess 0 stellt fest, dass S den Wert 0 besitzt und betritt den kritischen Bereich 3. Prozess 1 erkennt ebenfalls, dass S=0 gilt und wartet 4. Prozess 1 befindet sich im Zustand bereit, bis Prozess 0 den kritischen Bereich verlässt und S auf 1 setzt. 5. Durch die wechselseitige Benutzung von S können beide Prozesse alternierend ihren kritischen Bereich betreten.

16 Striktes Alternieren 3 Problem : 1. Die fortwährende Überprüfung des Wertes der Variablen S verschwendet Rechenzeit. 2. Die dritte notwendige Bedingung wird verletzt, da bei ungleich schnellen Prozessen ein Prozess den anderen blockiert. 3. Durchläuft Prozess 0 seinen kritischen Bereich sehr schnell und besitzt Prozess 1 einen langen nichtkritischen Bereich, befinden sich beide Prozesse im nichtkritischen Bereich mit S=1. Prozess 0 kann dann nicht in einen kritischen Bereich eintreten.

17 Warten und Benachrichtigen 1 Das strenge Schema der Synchronisation ist oft zu unflexibel : Es kann während des Ausführens einer Anweisungsfolge notwendig werden, die Arbeit zu unterbrechen und darauf zu warten, dass ein anderer Prozess einen bestimmten Zustand erreicht hat oder eine bestimmte Variable setzt. Dazu muß unter Umständen auch vorübergehend eine Sperre aufgehoben werden. Man könnte dies natürlich durch Zerlegen der Anweisungsfolge erreichen, der logische Zusammenhang der Anweisungen würde aber verloren gehen.

18 Warten und Benachrichtigen 2 Es gibt zwei prinzipiell unterschiedliche Möglichkeiten, das Warten auf ein Bestimmtes Ereignis zu programmieren: aktives Warten (busy waiting): Das Programm prüft innerhalb einer Schleife ständig, ob das erwartete Ereignis schon eingetreten ist. passives Warten durch Blockieren: Der Prozess benutzt einen Betriebssystemaufruf zum Warten. Er beauftragt das Betriebssystem, seine Ausführung solange zu suspendieren, bis das erwartete Ereignis eingetreten ist. Betriebssysteme beinhalten unterschiedliche Blockiermechanismen für diesen Zweck. Das Betriebssystem muss beim Auftreten eines Ereignisses jeweils prüfen, ob ein oder mehrere Prozesse aufgeweckt werden müssen.

19 Warten und Benachrichtigen 3 aktives Warten (busy waiting) : Der wartende Prozess führt eine Schleife der folgenden Form aus: while (!bedingung) {} «Fortsetzung» Da er dabei aktiv bleibt, belegt er unnötig Ressourcen. Man könnte das aktive Warten abmildern zu (polling and sleeping) while (!bedingung) { sleep(period) ; } «Fortsetzung» Auch dabei bleibt aber der Prozess unnötig semi-aktiv. Günstiger ist es, einen Prozess ganz schlafen zu legen, bis sich die gewünschte Bedingung eingestellt hat.

20 Warten und Benachrichtigen 4 Prozess P ruft in einem für x synchronisierten Block die Funktion wait() auf. Anschließend geschieht in einer unteilbaren Aktion : P hebt die Sperre von x auf P reiht sich in die Menge der Prozesse ein, die darauf warten, die Sperre von x (wieder) zu bekommen, und legt sich schlafen. Beim Aufwecken wird die Sperre nur aufgehoben, aber nicht weitergegeben. Auch kann beim Aufwecken nicht davon ausgegangen werden, dass die Bedingung erfüllt ist, auf die man gewartet hat. Denn beim Schlafenlegen teilt ein Prozess ja nur mit, um welche Sperre er sich beim Aufwecken bewirbt.

21 Warten und Benachrichtigen 5 Ein mit wait() schlafengelegter Prozess kann erst dann fortfahren, wenn er durch einen anderen wieder aufgeweckt wird. Im ungünstigsten Fall wird er nie mehr aufgeweckt. Die Funktion wait darf nur von einem Prozess aufgerufen werden, der gerade die Sperre des zugehörigen Objekts hat.

22 Erzeuger-/Verbraucher-Problem 1 Ein Erzeuger produziert Informationen, ein oder mehrere Verbraucher verarbeiten sie weiter. Hier : Zwei Prozesse benutzen gemeinsam einen Puffer fester Größe. Sind die Verbraucher in der Weiterverarbeitung unterschiedlich schnell, müssen die Informationen zwischengelagert = gepuffert werden. 1. Der Erzeuger will Informationen in einen vollen Puffer stellen - der Erzeuger legt sich schlafen und wird vom Verbraucher geweckt, wenn dieser Informationen entnimmt 2. Der Verbraucher will einem leeren Puffer Informationen entnehmen - er legt sich schlafen und wird vom Erzeuger geweckt, falls dieser Informationen in den Puffer stellt.

23 Erzeuger-/Verbraucher-Problem 2 Ablauf : 1. S bestimmt die Anzahl der Informationen im Puffer 2. Der Erzeuger überprüft, ob S=n gilt. Falls ja, legt er sich schlafen, falls nein, stellt er weitere Informationen in den Puffer und erhöht S. 3. Der Verbraucher prüft, ob S=0 gilt. Falls ja, legt er sich schlafen, falls nein, entnimmt er dem Puffer Informationen und dekrementiert S. Problem: 1. Der Puffer ist leer und der Verbraucher hat S gerade überprüft. 2. Ein Prozesswechsel unterbricht den Verbraucher und startet den Erzeuger, ohne dass der Verbraucher sich schlafen legen konnte. 3. Der Erzeuger schreibt Informationen in den Puffer und erhöht S. Er erkennt, dass S vorher den Wert 0 hatte und weckt den Verbraucher. 4. Der Verbraucher hat aber nicht geschlafen, so dass das Wecksignal verloren geht. Erhält er aber das Ausführungsrecht, erkennt er noch, dass sein S den Wert 0 besitzt und legt sich schlafen. 5. Der Erzeuger füllt den Puffer und legt sich ebenfalls schlafen. Beide Prozesse schlafen für immer.

24 Erzeuger-/Verbraucher-Problem 3 Im Beispiel wird sowohl beim Einfügen neuer Informationen, als auch beim Lesen der Informationen aktives Warten benutzt. Wenn ein Erzeuger und ein Verbraucher nebenläufig auf den Puffer zugreifen, sind damit grundlegende Anforderungen erfüllt: Das Senden und Empfangen von Informationen ist zu einem gewissen Grad Zeitlich entkoppelt. Solange Informationserzeugung und -verbrauch ungefähr mit gleicher Frequenz erfolgen, gibt es keine Probleme. Ist die Informationserzeugung über längere Zeit zu schnell (Puffer voll), wird sie gebremst, also mit der Verbrauchergeschwindigkeit synchronisiert. Das gleiche gilt auch umgekehrt. Die Implementierung ist dennoch unzulänglich: Erzeuger und Verbraucher greifen auf gemeinsame Daten zu und modifizieren diese. Die Zugriffe sind keine atomaren Operationen, sondern unterbrechbare Befehlsfolgen. Dabei kann es zu nichtdeterministischem Programmverhalten (race conditions) kommen.

25 Semaphor 1 Sperrobjekte zur Sicherung kritischer Abschnitte sind unter demBegriff Semaphor bekannt. Eingeführt 1965 von E.W. Dijkstra ist ein Semaphor ursprünglich eine Zählsperre S, deren Operationen P(S) =Passieren und V(S) =Verlassen statt LOCK(S) und UNLOCK(S) genannt wurden. Anschaulich hat es den Charakter einer Tür mit Riegel und frei/besetzt-Anzeige. P wartet bis Zustand den Wert FREI hat, und setzt ihn dann auf BESETZT. Dagegen setzt V einfach den Wert von Zustand auf FREI.

26 Semaphor 2 Damit ein Semaphor funktioniert, müssen P und V selbst unteilbare Aktionen/ ununterbrechbare Operationen sein, da sonst der wechselseitige Ausschluss nicht garantiert werden kann. Zur Unterstützung werden eigene Maschinenbefehle (test-and-set/reset) benutzt, weil diese jeweils ununterbrechbar sind. Semaphore gibt es in verschiedenen Varianten: aktives Warten / Blockieren Zähler / binäre Variable Initialisierung mit 0 / mit einem Wert k > 0 Der Ablauf : Der erste Prozess, der in den kritischen Bereich kommt, markiert den Eintritt und arbeitet weiter. Versucht ein zweiter Prozess den kritischen Bereich zu betreten, sieht er die Markierung und wartet. Ist der erste Prozess beendet, löscht dieser die Markierung und ermöglicht dem wartenden Prozess den Eintritt.

27 Binärer Semaphor Ein binärer Semaphor s besitzt entweder den Wert 0 oder 1. Es gibt zwei Zugriffsfunktionen P(s) und V(s). Implementation mit aktivem Warten: P(s): while s=0 do { nichts }; s:=0; V(s): s:=1; Beide Funktionen sind ununterbrechbar implementiert, so dass immer nur ein Prozess den Smaphorwert verändern kann. Ferner wird noch eine Funktion zum Erzeugen und Initialisieren eines Semaphors benötigt.

28 Lösung des Synchronisationsproblems begin init_semaphor(s,1) parbegin p0: repeat P(s); kritischer Bereich v(s); until false; p1: repeat P(s); kritischer Bereich v(s); until false; parend; end. Der gegenseitige Ausschluss wird also nicht mehr im Anwendungsprogramm realisiert, sondern durch Semaphoren. P steht daher für: Das Tor zum kritischen Bereich passieren V für den kritischen Bereich verlassen

29 Semaphor mit passivem Warten Ein Prozess, der innerhalb von P(s) warten muss, führt kein Polling mehr aus. Er wird mithilfe des Betriebssystems blockiert und wieder geweckt, sobald der kritische Bereich frei ist. Möglicherweise müssen mehrere Prozesse auf einen Semaphor warten. Damit das Warten nicht zum Verhungern führt, wird eine Warteschlange nach dem FIFO-Prinzip eingeführt. P(s): if s=1 then s:=0 else trage den Prozess in die Warteschlange von s ein und blockiere ihn V(s): if Queue von s = leer then s:=1 else entferne einen Prozess aus der Queue von s und wecke ihn

30 Allgemeiner Semaphor Ein allgemeiner Semaphor ist eine Erweiterung des binären Typs. Die Semaphorvariable kann einen beliebigen Wert aus der Menge der Natürlichen Zahlen annehmen. Die Zugriffsfunktionen haben folgendes Aussehen: P(s): if s>0 then s:=s-1 else trage den Prozess in die Warteschlange von s ein und blockiere ihn V(s): if Queue von s = leer then s:=s+1 else entferne einen Prozess aus der Queue von s und wecke ihn Mit diesem Semaphor kann ein kritischer Bereich verwaltet werden, in dem sich eine begrenzte Zahl von Prozessen aufhalten kann = Verwalten mehrerer gleichartiger Ressourcen

31 Definition und Wertung Definition: Ein Semaphor besteht aus einer geschützten Variablen s, zwei Zugriffsfunktionen P(s) und V(s), einer Initialisierungsfunktion und einer Warteschlange Vorteile: Semaphoren sind universell und für viele Synchronisationsprobleme einfach anwendbar es wird passiv gewartet und keine CPU-Zeit verschwendet Warteschlangen nach dem FIFO-Prinzip regeln eine faire Zuteilung Nachteile: Wenn P(s) erfolglos ist, muss der Prozess zwingend warten und kann keine anderen Programmteile ausführen die nicht korrekte Verwendung von V(s) erzeugt Deadlocks. Die Synchronisation wird also durch ein Hilfsmittel und nicht durch den Schutz eines Codeteils realisiert. Da die kritischen Bereiche über den Code aller Prozesse verteilt sein können, wird ein Überblick erschwert.

32 Lösung des Erzeuger-/Verbraucher- Problems 1 Semaphore mutex = 1 kontrolliert krit. Bereich Semaphore empty = N zählt die leeren Puffereinträge Semaphore full = 0 zählt die belegten Puffereinträge Erzeuger while (TRUE) { erzeuge_eintrag down (empty) dekrementiere empty down (mutex) Eintritt in den krit. Bereich einfügen_eintrag Eintrag in Puffer einfügen up (mutex) Verlassen des krit. Bereichs up ( full) inkrementieren des Belegt-Zählers } Verbraucher while (TRUE) { down (full) dekrementiere full down (mutex) Eintritt in den krit. Bereich entnehme_eintrag Eintrag aus Puffer entnehmen up (mutex) Verlassen des krit. Bereichs up ( empty) inkrementieren der leeren Einträge }

33 Lösung des Erzeuger-/Verbraucher- Problems 2 Die Lösung benutzt drei Semaphoren : 1. Einen Semaphor zum Zählen der belegten Puffereinträge 2. Einen Semaphor zum Zählen der freien Puffereinträge 3. Einen Semaphor, um sicherzustellen, dass Erzeuger und Verbraucher nicht simultan auf den Puffer zugreifen. Dieser Semaphor wird mit Eins initialisiert, um zu gewährleisten, dass sich nur ein Prozess zu einem Zeitpunkt im kritischen Bereich befindet Der Semaphor mutex wird zur Durchsetzung des wechselseitigen Ausschlusses verwendet, full und empty werden eingesetzt, um bestimmte Ereignisreihen- folgen zuzulassen, also zur Synchronisation

34 Philosophenproblem 1 E.W. Dijkstra hat 1965 ein klassisches Beispiel angegeben, das beide Aspekte, Verklemmung, Fairness und Semaphoren demonstriert : Fünf Philosophen sitzen um einen Tisch. Jeder hat einen Teller mit Spaghetti vor sich. Außerdem stehen insgesamt fünf Gabeln zur Verfügung, die anfangs zwischen den Tellern liegen. Jeder Philosoph denkt eine Weile nach, wird dann hungrig und will essen. Java-AppletJava-Applet von SUN Essende Philosophen in Berlin Essende Mönche aus Augsburg

35 Philosophenproblem 2 Damit ein Philosoph essen kann, braucht er die beiden Gabeln links und rechts von seinem Teller. Er versucht also, diese in beliebiger Reihenfolge aufzunehmen. Hat er beide, isst er eine Weile. Danach legt er beide Gabeln wieder ab. Das Problem besteht nun darin, ein Programm zu schreiben, das Verklemmungen und Verhungern vermeidet.

36 Philosophenproblem 3 Lösungsidee : Philosoph while (TRUE) { think () Philosoph denkt take_fork(i) nimm linke Gabel take_fork (i+1 MOD 5) nimm rechte Gabel eat () put_fork (i) lege linke Gabel ab put_fork (i+1 MOD 5) lege rechte Gabel ab

37 Philosophenproblem 4 Eine Verklemmung entsteht, wenn zufällig alle Philosophen gleichzeitig ihre rechte Gabel aufnehmen. Andererseits verhungert ein Philosoph, wenn ihm seine Nachbarn immer die Gabeln wegschnappen, wenn er hungrig ist. Eine scheinbare Lösung des ersten Problems ist es, bei Erhalt einer Gabel zu prüfen, ob die andere frei ist und andernfalls die Gabel wieder hinzulegen. Dies kann aber zu zyklischem Aufnehmen und Ablegen der jeweils rechten Gabel führen, ohne dass jemals gegessen wird - Verhungern

38 Philosophenproblem 5 Die nächste Lösungsidee verwendet Sperrsynchronisation zwischen allen Philosophen über ein binäres Semaphor mutex : Wenn Philosoph hungrig wird, ruft er mutex auf. Hat er die Sperre, führt er den gesamten Zyklus "Gabeln aufnehmen - essen - Gabeln ablegen" unter Ausschluss aller anderen Philosophen aus. Dies ist eine korrekte Lösung; allerdings ißt nur jeweils ein Philosoph obwohl bei fünf Gabeln zwei essen könnten. Die Lösung schränkt damit die Parallelität ein,

39 Philosophenproblem 6 Eine Lösung mit größtmöglicher Parallelität verwendet ein verfeinertes Modell : Jeder Philosoph hat die möglichen Zustände THINKING, HUNGRY, EATING. EATING kann er nur erreichen, wenn er HUNGRY ist und seine beiden Nachbarn nicht im Zustand EATING sind. Die zugehörigen Abfragen müssen unter wechselseitigem Ausschluss erfolgen; hierzu dient ein globales binäres Semaphor mutex. Jeder Philosoph hat zudem ein eigenes allgemeines Semaphor, mit dem er sich selbst blockiert, wenn er seine Gabeln nicht bekommen konnte. Damit bleibt sein Bedarf angemeldet. Dieses Semaphor wird von einem Nachbarn wieder freigegeben, wenn dieser fertig ist und auch der andere Nachbar gerade nicht ißt.

40 Philosophenproblem 7 a Lösung : N = 5 LEFT = (i-1 MOD N) RIGHT =( i+1 MOD 5) THINKING = 0, HUNGRY = 1, EATING = 2 semaphor mutex = 1 wechselseitiger Ausschluss semaphor s (N) ein Semaphor für jeden Philosophen Philosoph while (TRUE) { think () take_forks (i) nimm beide Gabeln oder blockiere eat () put_forks (i) lege beide Gabeln ab take_forks down (mutex) tritt in krit. Bereich ein state (i) = HUNGRY test (i) versuche beide Gabeln zu erhalten up (mutex) verlasse krit. Bereich down ( s(i)) blockiere, falls Gabeln nicht frei waren

41 Philosophenproblem 7 b put_forks down (mutex) tritt in krit. Bereich ein state (i) = THINKING test (LEFT) prüfe, ob linker Nachbar jetzt essen kann test (RIGHT) up (mutex) verlasse krit. Bereich test if (state (i) == HUNGRY && state (LEFT) != EATING && state (RIGHT) != EATING) state (i) =EATING up ( s(i)) ein Philosoph kann nur in den Zustand EATING gelangen, wenn seine beiden Nachbarn nicht essen, d.h. keine Gabel besitzen. Der Semaphor s blockiert hungrige Philosophen, falls die benötigten Gabeln in Gebrauch sind.

42 Priorität Höhere Priorität bedeutet Vorrang : Wird zu einem Zeitpunkt ein Prozess P ausführbereit, der höhere Priorität hat als der laufende Prozess T, wird T unterbrochen und P ausgeführt (preemptive scheduling). Hierbei ist darauf zu achten, dass nicht zu viele Prozesse mit der höchsten Priorität versehen werden, da sie sich dann wieder gegenseitig behindern. Man kann folgende Faustregel für die Prioritätsvergabe aufstellen: Ein dauernd laufender Prozess sollte geringere Priorität haben als einer, der eher seltene Ereignisse behandelt.


Herunterladen ppt "Prozesskoordination Prof. Dr. W. Riggert. Ausgangssituation Prozesse müssen häufig untereinander kommunizieren, z.B. Pipes in UNIX. Zwei Prozesse heißen."

Ähnliche Präsentationen


Google-Anzeigen