Prof. Frederik Armknecht Sascha Müller Daniel Mäurer Grundlagen der Informatik 3 Wintersemester 09/10 ösungsvorschlag zur 11. Übung 1 Präsenzübungen 1.1 Schnelltest a) Was gehört zu den Aufgaben eines Geräte-Treibers? Vereinheitlicht die Nutzungsschnittstelle für die geräteunabhängige S- Schicht. Konvertiert sequentielle itströme in Datenblöcke. Prüft den Status der Interrupt-eitungen. Versteckt die Unterschiede verschiedener Controller. b) Welche der Aussagen über Kommunikationsbusse sind richtig? usse sind Kommunikationsverbindungen zwischen mehreren Teilsystemen. usse verhindern die ildung eines Kommunikations-Flaschenhalses. usse verwenden gemeinsame eitungen. Die usgeschwindigkeit ist unabhängig von physikalischen Faktoren. ei synchronen ussen müssen die Komponenten die gleiche Geschwindigkeit haben. Asynchrone usse sind weniger Aufwändig weil sie kein Protokoll brauchen. c) Wie läuft die us-arbitrierung bei einer Daisy Chain ab? Request, Warten auf Poll, inhibit-eitung belegen, us belegen, inhibit eitung freigeben Request, Warten auf Grant, us belegen Request, Warten auf Grant, us belegen, Release Request, Warten auf Poll, Inhibit-eitung belegen, us belegen d) Was ist ein Semaphor? Ein Signalmast zur Nachrichtenübermittlung mit beweglichen Flügeln. Eine Datenstruktur mit zwei speziellen Nutzungsoperationen. Eine Variablenart zur Speicherung der Anzahl von Weckrufen. Semaphoren können Deadlocks verhindern. Die einzige Umsetzungsmöglichkeit eines wechselseitigen Ausschlusses. Mit Semaphoren lässt sich wechselseitiger Ausschluss verhindern. 1.2 Scheduling (Klausuraufgabe WS 06-07) Gegeben seien fünf Prozesse mit jeweiligen Start- und CPU-Zeiten, sowie deren Priorität (Prozesse mit kleinerem Prioritätswert kommen vor Prozessen mit größeren Werten): 1
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 Prozess Startzeit CPU-Zeit Statische Priorität P1 0 8 6 P2 2 4 7 P3 3 2 3 P4 3 7 4 P5 6 3 2 Zeichnen Sie für jedes der folgenden Schedulingverfahren ein Gantt-Diagramm der Prozesse. etrachten Sie nur die Prozesszustände bereit () und laufend (). a) Statische Priorität (non-preemptive): 0 P1 P2 P3 P4 P5 5 10 15 20 25 30 t b) Shortest Job First (non-preemptive): 0 P1 P2 P3 P4 P5 5 10 15 20 25 30 t c) Shortest remaining time next (preemptive): 0 P1 P2 P3 P4 P5 5 10 15 20 25 30 t d) erechnen Sie die mittlere Wartezeit für jedes Schedulingverfahren. 2
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 Prozess Prio. SJF SRTN P1 0 0 9 P2 18 11 2 P3 8 5 0 P4 10 14 14 P5 2 4 2 Mittlere Wartezeit 7,6 6,8 5,4 1.3 Speisende Philosophen Ein sehr bekanntes Problem der Synchronisation ist das Problem der speisenden Philosophen, das auf Folie 165 erwähnt wurde. Es besteht folgende Situation: An einem runden Tisch sitzen fünf Philosophen. Vor jedem Philosophen steht ein Teller mit Nudeln und zwischen je zwei Tellern liegt ein Stäbchen (es handelt sich offensichtlich um chinesische Philosophen). Die Philosophen agieren unabhängig voneinander und von Zeit zu Zeit wird jeder dieser Philosophen von den Nudeln auf seinem Teller essen. Er benötigt dazu die beiden Stäbchen rechts und links von seinem Teller. Abbildung 1: Der Tisch der speisenden Philosophen Verwenden Sie die aus der Vorlesung bekannten Mittel wie Mutex oder Semaphore, um den wechselseitigen Ausschluss der Stäbchen zu garantieren. Die grundlegende ösung des Problems besteht darin, dass die Philosophen in einem durch einen Mutex (binäre Semaphore) geschützten ereich entweder beide oder kein Stäbchen aufnehmen. Weil bei dieser Abstraktion die einzelnen Stäbchen unwichtig geworden sind, kann man zusätzlich eine Semaphore für jeden Philosophen einführen, anstatt auf einzelnen Stäbchen zu warten. Ein Philosoph schaut innerhalb eines durch den Mutex geschützten ereichs nach, ob einer seiner Nachbarn isst. Ist dem so, legt er sich außerhalb des Mutex auf seiner eigenen Semaphore schlafen. Andernfalls belegt er seine Semaphore und fängt an zu essen. Verläßt ein Philosoph den Essensbereich, so schaut er, ob einer seiner Nachbarn gerade hungrig ist (auf das Stäbchen wartet). Ist dies der Fall so weckt er diesen Nachbarn auf. In der ösung im Modern Operating Systems (Ed. 1) von Andrew Tanenbaum werden beide Tests, beim etreten und Verlassen, in einer Funktion realisiert (Der Parameter i steht dabei immer für die Nummer des Philosophen. Es gibt einen Mutex mutex und für jeden Philosophen i ein Semaphor s[i].): 1 void test ( int i) 3
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 2 { 3 /* wenn i hungrig ist und kein Nachbarn von i isst */ 4 if ( state [ i] == HUNGRY && state [ EFT ]!= EATING 5 && state [ RIGHT ]!= EATING ) { 6 state [ i] = EATING ; /* i zum essen schicken /* 7 up (&s[i ]); 8 } 9 } Damit ist das Aufnehmen der Stäbchen und das Ablegen denkbar einfach, wobei besonderes Augenmerk auf die Position des down(&s[i]) gelegt werden sollte. Dieses muss außerhalb des Mutex geschehen, weshalb sich der Prozess in der Testroutine selber ein up schickt sofern er selbst hungrig ist und seine Nachbarn nicht essen. 1 void take_ sticks ( int i) 2 { 3 down (& mutex ); 4 state [ i] = HUNGRY ; /* Hunger melden */ 5 test ( i); /* testen ob essensbereit 6 up (& mutex ); 7 down (&s[i ]); /* auf Freigabe des Essens warten */ 8 } eim Verlassen des Essensbereiches muss nur darauf geachtet werden, dass ich alle eventuellen Nachbarn, die jetzt essen könnten, aufwecke. 1 void put_ sticks ( int i) 2 { 3 down (& mutex ); 4 state [ i] = THINKING ; /* Essen beenden */ 5 test ( EFT ); /* Nachbarn ggf. aufwecken */ 6 test ( RIGHT ); 7 up (& mutex ); 8 } 1.4 Deadlock Zwei Funktionen S und T arbeiten mit den exklusiven etriebsmittel A,, C, D und E. Da diese etriebsmittel jeweils nur von einem Prozess bzw. Thread gleichzeitig verwendet werden können, müssen sich nebenläufige Prozesse, die S und T ausführen, die etriebsmittel teilen. Zur Regelung des Zugriffs werden Semaphoren angefordert und reserviert. Die Prozesse benötigen teilweise mehrere etriebsmittel gleichzeitig zur Durchführung einer Operation. Alle Semaphore werden am Anfang mit 1 initialisiert. 1 void S() { 2 while (1) { 3 A. down (); 4. down (); 5 C. down (); 6 A.up (); 7.up (); 4
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 8 D. down (); 9 C.up (); 10 D.up (); 11 } 12 } 1 void T() { 2 while (1) { 3 E. down (); 4 D. down (); 5 E.up (); 6 A. down (); 7 D.up (); 8 A.up (); 9 } 10 } Geben Sie bei beiden Teilaufgaben im Falle eines möglichen Deadlocks sowohl die Ablaufreihenfolge, welche zu einem Deadlock führt, als auch den dazugehörenden Ressourcengraphen an. Ansonsten begründen Sie, warum es keinen Deadlock geben kann. a) Angenommen Sie starten genau einen Prozess oder Thread, der Funktion S ausführt, und genau einen Prozess oder Thread, der Funktion T ausführt. Kann es zu einem Deadlock kommen? Zu einem Deadlock kann es bei zwei Prozessen dann kommen, wenn der eine ein etriebsmittel A blockiert und ein weiteres etriebsmittel anfordert, und der zweite Prozess blockiert und A anfordert (es entsteht ein Zyklus im Zugriffsgraphen). Es kann hier zu keinem Deadlock kommen, weil S und T sich nur A und D teilen und S A freigibt, bevor S D anfordert (danach wird A nicht mehr von S angefordert). Ebenso fordert T nur A an, wenn er im esitz von D ist und gibt beide danach wieder frei. b) Nehmen Sie jetzt an, dass es genau zwei Prozesse bzw. Threads gibt die Funktion S ausführen, und einen, der Funktion T ausführt. Kann dabei ein Deadlock entstehen? Ja, es kann zu einem Deadlock kommen: S 1 besitzt C und wartet auf D (hat A und wieder freigegeben). S 2 besitzt A und und wartet auf C. T besitzt D und wartet auf A. C S 1 S 2 A D T Hinweis: In einem Ressourcengraph werden alle etriebsmittel und Prozesse durch Knoten repräsentiert. Eine gerichtete Kante von einem etriebsmittel zu einem Prozess bedeutet, dass der Prozess das etriebsmittel zu einem Zeitpunkt reserviert hat. Eine gerichtete 5
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 Kante von einem Prozess zu einem etriebsmittel zeigt an, dass der Prozess das etriebsmittel gerade reservieren möchte. Enthält der resultierende Graph einen Zyklus, liegt ein Deadlock vor. 2 Hausübungen 2.1 Threads Zur Sortierung von Zahlen verwendet ein Programm den Sortieralgorithmus Slowsort, der wie folgt definiert ist 1. Die Parameter i und j geben hierbei die untere und obere Grenze des Datenfeldes A an. 1 procedure slowsort (A,i,j) 2 if i >= j then return 3 m := (i+j )/2 4 slowsort (A,i,m) 5 slowsort (A,m+1,j) 6 if A[j] < A[m] then vertausche A[j] und A[m] 7 slowsort (A,i,j -1) 8 end a) Implementieren Sie Slowsort in C. Erzeugen Sie sich zum Testen eine main-methode und ein Datenfeld mit 400 Einträgen, das absteigend sortiert ist (399,..., 1, 0). Die sortierte iste soll vor dem Programmende ausgegeben werden. Siehe separate Datei auf Homepage. b) Sie stellen fest, dass die Ausführungszeit leider sehr hoch ist. ei genauerer etrachtung stellen Sie fest, dass von zwei Prozessorkernen nur einer ausgelastet ist. Sie entscheiden sich daher, das Sortierverfahren auf zwei Threads aufzuspalten. Verändern Sie Ihre Implementierung und lassen Sie Slowsort in zwei Threads das Datenfeld parallel sortieren. Nutzen Sie hierfür die standardisierte pthreads ibliothek. 2 Jeder Thread soll eine Hälfte der iste sortieren. Siehe separate Datei auf Homepage. c) Welche Veränderungen stellen Sie fest? (Falls Sie ein Einprozessorsystem verwenden, gehen Sie davon aus, dass Sie einen Prozessor mit zwei Kernen verwenden.) Es werden zwei Prozessorkerne ausgenutzt. Die aufzeit ist geringer. Der Algorithmus sortiert nur noch Teillisten. d) Das Datenfeld ist nach Programmende nicht vollständig korrekt sortiert. Was könnten Sie tun, um für dieses eispiel am Ende ein korrekt sortiertes Datenfeld zu haben? Würde dieser Ansatz auch allgemein für andere Datenfelder funktionieren? egründen Sie. 1 Quelle: Wikipedia 2 In Unix-artigen Systemen bereits vorhanden. Unter Windows können Sie alternativ die ibliothek http: //sourceware.org/pthreads-win32/ nutzen. 6
ösungsvorschlag zur 11. Übung Grundlagen der Informatik 3, WS 09/10 Da man weiß, dass das ursprüngliche Datenfeld absteigend sortiert ist und dadurch seine beiden Hälften korrekt sortiert sind, könnte man die beiden Hälften miteinander vertauschen, womit dann das komplette Datenfeld korrekt sortiert wäre. Allgemein funktioniert diese ösung nicht, da dies voraussetzt, dass die untere Hälfte des Datenfeldes nur Werte enthält, die auch sortiert in der untere Hälfte stehen würden (analog mit der oberen Hälfte). e) Könnte man an der Stelle von Threads auch mehrere Prozesse verwenden (z.. durch fork)? egründen Sie. Nein, da sich verschiedene Prozesse keinen Speicherbereich teilen und auch nicht auf fremde Speicherbereiche zugreifen dürfen. Um Ihnen den Einstieg zu erleichtern, können Sie auf folgendem Programmrahmen aufbauen. 1 # include < pthread.h> 2 # include <stdio.h> 3 void * thread ( void * threadid ) 4 { 5 printf (" Das ist Thread %d.\n", ( int ) threadid ); 6 pthread_exit ( NU ); 7 } 8 int main ( int argc, char * argv []) 9 { 10 // Zwei Threads erzeugen, die mit der Funktion " thread " starten. 11 pthread_ t threads [ 2]; 12 pthread_create (& threads [0], NU, thread, ( void *)0); 13 pthread_create (& threads [1], NU, thread, ( void *)1); 14 15 // Auf das Ende beider Threads warten. 16 pthread_ join ( threads [0], NU ); 17 pthread_ join ( threads [1], NU ); 18 19 printf (" Alle Threads wurden beendet.\ n"); 20 } eachten Sie, dass Programme, die Threads enthalten, mit dem Aufruf gcc -pthread <datei.c> kompiliert werden müssen, um die nötigen Thread-ibliotheken einzubinden. 7