Folien Kapitel 6 - Universität Ulm
Folien Kapitel 6 - Universität Ulm
Folien Kapitel 6 - Universität Ulm
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
6 Prozessmanagement<br />
Prozessmanagement in verteilten Systemen geschieht auf zwei Ebenen<br />
• in einem Server die von verschiedenen Klienten eintreffenden Anfragen bearbeiten<br />
• ein feiner abgestuftes Prozesskonzept an sich bereitstellen, um die obige Aufgabe effizienter<br />
zu lösen<br />
6.1 Iterative und nebenläufige Server<br />
6.1.1 Iterative Server<br />
• jeweils immer ein Auftrag nach dem anderen bearbeiten<br />
Programmfragment:<br />
forever<br />
receive (request, client);<br />
perform request;<br />
send (reply, client);<br />
endforever;<br />
Prof. Dr. Michael Weber; Verteilte Systeme, SS99, <strong>Universität</strong> <strong>Ulm</strong><br />
Eigenschaften und Bewertung<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 1<br />
• um während der Bearbeitung neue Aufträge annehmen zu können, ist für eingehende Aufträge<br />
ein Puffer bereitzustellen<br />
• dies muss vom entsprechend zu wählenden Kommunikationssystem übernommen werden<br />
• die Antwortzeit für einzelne Aufträge kann sehr lang werden<br />
• die Bearbeitungsreihenfolge ist immer First-Come-First-Served<br />
• für einfache Dienste mit geringer Auftragsbearbeitungszeit und nicht allzu hoher Anfragehäufigkeit<br />
geeignet<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 2
6.1.2 Nebenläufige Server<br />
• ein nebenläufiger Server kann mehrere Aufträge gleichzeitig in Bearbeitung haben<br />
• dies ist durch die Einführung mehrerer Kontrollflüsse innerhalb des Servers möglich<br />
Vorteile gegenüber der iterativen Methode:<br />
• die mittlere Antwortzeit pro Klient wird verbessert<br />
• stehen Wartezeiten für Ein-/Ausgabeoperationen an, so kann diese Zeit zur Bearbeitung anderer<br />
Aufträge genutzt werden<br />
• es ist unter Umständen möglich, die Reihenfolge der Bearbeitung geeignet zu wählen<br />
• die Ausnutzung mehrerer Prozessoren bei Multiprozessormaschinen kann unterstützt werden<br />
Bild 6.1: Nebenläufiger Server<br />
Realisierungsvarianten hängen davon ab, ob die vom Betriebssystem bereitgestellte Multiprozessfähigkeit<br />
genutzt werden soll/kann oder nicht<br />
Multiplex-Server<br />
Klienten<br />
Klient<br />
Klient<br />
Klient<br />
Master<br />
erzeugt<br />
Server<br />
Slave<br />
Slave<br />
Slave<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 3<br />
• keine Unterstützung des Betriebssystems<br />
• verwendet nur einen einzelnen echten Kontrollfluß<br />
• Kontrollfluß wird vom Server selbst auf mehrere Aufträge verteilt<br />
• für jeden Auftrag verwaltet der Server eine Kontrolldatenstruktur, in der er den aktuellen<br />
Bearbeitungszustand des Auftrags speichert<br />
• an geeigneter Stelle wechselt der Server von der Bearbeitung eines Auftrags zur Bearbeitung<br />
eines anderen<br />
Nachteile:<br />
• Ist der Server durch einen blockierenden Systemaufruf blockiert, kann er die Wartezeit nicht<br />
für andere Aufgaben nutzen.<br />
• Die Aufteilung der einzelnen Aufträge in Bearbeitungsportionen ist schwierig.<br />
• Die Programmstruktur des Servers entspricht nicht herkömmlichen Programmierparadigmen<br />
und erscheint unnatürlich.<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 4
Programmfragment eines Multiplex-Servers:<br />
record ControlBlock<br />
request;<br />
state;<br />
endrecord;<br />
var ControlBlock_List :<br />
linear_list of ControlBlock ;<br />
forever<br />
receive (request, client);<br />
-- nicht blockierend!<br />
insert into ControlBlock_List;<br />
choose request from ControlBlock_List;<br />
perform next part of request;<br />
if (request = done) then<br />
send (reply, client);<br />
delete request from ControlBlock_List;<br />
endif;<br />
endforever;<br />
Realisierung mit Prozessen<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 5<br />
• Betriebssystem stellt eine Unterstützung für mehrere Kontrollflüsse zur Verfügung<br />
• für jeden Auftrag einen eigenen Prozess verwenden<br />
Programmbeispiel:<br />
forever<br />
receive (request, client);<br />
if (fork = 0) then -- fork = 0 prüft, ob Kindprozess<br />
perform request;<br />
send (reply, client);<br />
endif;<br />
endforever;<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 6
Bewertung<br />
• Nebenläufigkeit ist mit dem Prozessmanagement des Betriebssystems einfach zu erreichen<br />
• die gesamte Verwaltungsarbeit wird vom Betriebssystem übernommen<br />
• der Programmierer hat wenig Aufwand<br />
• Ein neu erzeugter Prozess ist „schwergewichtig”<br />
• er verfügt über alle Ressourcen, z.B. einen komplett eigenen Adressraum, um vollkommen<br />
eigenständig als Betriebssystemprozess zu leben<br />
• der große Verwaltungsaufwand durch Anlegen von Kopien des Stack und des Heap des<br />
Vaterprozesses, Erstellen von Ein-/Ausgabetabellen etc. verursacht eine lange Erzeugungsdauer<br />
• ein Prozesswechsel ist aufwendig, da diese gesamte Information getauscht werden muß<br />
• da das Betriebsystem keine Kenntnis über die Semantik der Aufträge besitzt, kann es keine<br />
Optimierung der Bearbeitungsreihenfolge vornehmen<br />
• das übliche Zeitscheibenverfahren kommt auch hier zum Tragen<br />
6.2 Threads<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 7<br />
• kein Verzicht auf die betriebssystemseitige Unterstützung von Nebenläufigkeit<br />
• vermeiden der Schwergewichtigkeit von Prozessen<br />
• unabhängige Threads mit jeweils eigenem Kontrollfluß laufen in einem gemeinsamen<br />
Adressraum ab<br />
• der Prozess dient lediglich als Ausführungsumgebung und ist selbst nicht aktiv<br />
Bild 6.2 Single-threaded und multi-threaded System<br />
dress<br />
a) Single-threaded b) multi-threaded<br />
Computer Prozess Kontrollfluß Programm-Zähler<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 8
Eigenschaften threadbasierter Systeme<br />
• Threads laufen innerhalb eines gemeinsamen Adressraums ab<br />
- Zugriff auf gemeinsame Speicherobjekte<br />
- Interthreadkommunikation kann über gemeinsame Variablen erfolgen und benötigt keinen<br />
Nachrichtenaustausch<br />
- bei nebenläufigem Zugriff auf die gemeinsamen Ressourcen ist eine Synchronisation unter<br />
den beteiligten Threads notwendig<br />
• Das Erzeugen eines weiteren Threads ist viel weniger aufwendig als einen neuen Prozess zu<br />
kreieren<br />
- Adressraum und sonstige gemeinsam benutzte Ressourcen sind bereits angelegt<br />
- der Kontextwechsel zwischen Threads ist einfacher als bei Prozessen<br />
- nur wenige Zustandsinformationen müssen gewechselt werden<br />
- die Zustandsinformation von Threads beinhaltet: Programmzähler, Registersatz, threadlokalen<br />
Stack und Heap, Liste der Kindthreads, Ausführungszustand (running, blocked,<br />
ready)<br />
- dies ist nur wenig mehr Kontextinformation, als bei einem lokalen Prozeduraufruf<br />
- im Gegensatz dazu besitzt ein Prozess folgende Kontextinformation: kompletten virtuellen<br />
Adressraum (d.h. Seitentabelle und Seiten), Liste geöffneter Dateien, Liste der Kindprozesse,<br />
Kontrollstrukturen verwendeter Betriebssystemressourcen, wie Timer, Signale<br />
oder Semaphore, und Managementdaten, wie Accountinginformationen und Benutzerdaten.<br />
Organisationsformen threadbasierter Systeme<br />
• Dispatcher/Worker-Modell<br />
- ein Thread empfängt die Aufträge und<br />
- teilt sie anderen Bearbeitungsthreads zu, die<br />
- bereitstehen (Thread-Pool) oder<br />
- jeweils neu erzeugt werden<br />
- dieses Modell nennt man auch Master/Slave-Modell<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 9<br />
• Team-Modell<br />
- alle Threads sind gleichberechtigt<br />
- hat ein Thread seine bisherige Aufgabe abgearbeitet, bedient er sich selbst mit einem neuen<br />
Auftrag aus dem Eingangspuffer<br />
- der Puffer ist eine gemeinsam benutzte Ressource und muß gegen konkurrierenden Zugriff<br />
geschützt werden<br />
• Pipeline-Modell<br />
- die Threads sind logisch in einer Kette angeordnet<br />
- jeder Thread bearbeitet nur einen Teil des Auftrags und gibt ihn dann zur Weiterbearbeitung<br />
an den nachfolgenden Thread<br />
- der Auftrag muss eine dazu entsprechende Struktur haben<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 10
a) Dispatcher/Worker-Modell b) Team-Modell<br />
c) Pipeline-Modelll<br />
Dispatcher<br />
Kommunikationspuffer Kommunikationspuffer<br />
Kommunikationspuffer<br />
Bild 6.3 Organisationsformen threadbasierter Server<br />
6.2.1 Implementierung von Threads<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 11<br />
Die Verwaltungsfunktionen für Threads können auf unterschiedlichen Ebenen implementiert<br />
werden<br />
• im Betriebssystemkern: Kernel-level Threads<br />
• im Arbeitsbereich eines Benutzers: User-level Threads mit einem entsprechenden Laufzeitsystem<br />
User<br />
Space<br />
Kernel<br />
Space<br />
Bild 6.4 Implementierung von Threads<br />
a) User-level Threads b) Kernel-level Threads<br />
Laufzeitsystem<br />
Kern<br />
Thread<br />
Kern<br />
Thread<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 12
Vorteile von User-level Threads:<br />
• das Betriebssystem braucht nicht verändert zu werden<br />
• alle Systemaufrufe an das Betriebssystem werden über das Thread-Laufzeitsystem abgewikkelt<br />
• die threadbasierte Anwendung ist weitgehend unabhängig vom Betriebssystem und damit<br />
leichter portabel<br />
• die Umschaltung zwischen Threads ist schneller, da dies im Usermodus geschieht<br />
• das Laufzeitsystem muß lediglich Programmzähler, Registersatz und Kellerzeiger austauschen<br />
• es werden keine Kern-Traps ausgelöst, um dann dort die Threads zu wechseln<br />
• ein Wechsel in den Kern würde einen Prozesswechsel bewirken, so daß die Idee der Threads<br />
verloren geht.<br />
• die verwendeten Schedulingalgorithmen können an die Threads und damit an die Anwendung<br />
angepaßt werden<br />
• es ist möglich, zum gleichen Zeitpunkt unterschiedliche Schedulingstrategien für verschiedene<br />
threadbasierte Programme ablaufen zu lassen.<br />
• User-level Threads skalieren gut, da sehr wenig Informationen pro Thread geführt werden<br />
müssen.<br />
Nachteile von User-level Threads:<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 13<br />
• Problem blockierender Systemaufrufe<br />
- der Thread blockiert und mit ihm das Laufzeitsystem, welches vom Betriebssystem als ein<br />
einzelner Prozess gesehen wird<br />
- die Kontrolle kann somit nicht an einen anderen Thread übergeben werden<br />
• das Problem zu lösen, bedeutet ein Ändern der Systemroutinen<br />
- dies ändert auch die Semantik dieser Routinen und macht Anwenderprogramme unter<br />
Umständen fehlerhaft<br />
- zweite Variante: im Betriebssystem Select-Jackets um die Systemroutinen programmieren,<br />
die einen asynchronen Mechanismus sonst blockierender Aufrufe anbieten<br />
• Rechnende Threads müssen sich nicht freigeben<br />
• das Laufzeitsystem hat keine Möglichkeit laufende Threads zu unterbrechen<br />
- diese müssen spezielle Routinen des Laufzeitsystems aufrufen, um ein Scheduling zu erlauben<br />
Kernel-level Threads vermeiden die Nachteile, haben aber dafür nicht die Vorteile der User-level<br />
Threads zu bieten<br />
• jeder Aufruf einer Threadverwaltungsoperation wird zu einem Systemaufruf<br />
• die kernbasierte Implementierung vermeidet die Probleme blockierender Aufrufe<br />
Es ist ein Trend zu Kernel-level Threads in Betriebssystemen zu erkennen (siehe z.B. Windows<br />
NT, Solaris)<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 14
6.2.2 Programmieren mit Threads<br />
• Programmieren mit Threads ist Programmieren nebenläufiger Programme<br />
Globale Variablen<br />
• Threads können auf globale Variablen zugreifen, die durch andere Threads des gleichen<br />
Adressraums beeinflußt werden<br />
Aufruf einer Systemfunktion<br />
Fehler: errno = 42;<br />
Kontrolle an anderen Thread<br />
if (errno != 0) {<br />
Bild 6.5 Threads und globale Variablen<br />
Thread-Wechsel<br />
Aufruf einer Systemfunktion<br />
Kein Fehler: errno = 0;<br />
Kontrolle an anderen Thread<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 15<br />
Lösungen des Problems:<br />
• globale Variablen verbieten<br />
- dann wären Threads in allen Betriebssystemen, die mit solchen Variablen operieren (z.B.<br />
Unix), nicht möglich<br />
• pro Thread Kopien globaler Variablen anlegen<br />
- dies führt zu einer zusätzlichen Sichtbarkeitsebene<br />
- system-globale Variablen (sichtbar für alle Threads)<br />
- thread-globale Variablen (sichtbar innerhalb eines Threads)<br />
- lokale Variablen (sichtbar innerhalb einer Prozedur eines Threads)<br />
- erneutes Problem:<br />
- Programmiersprachen kennen die Zwischenform nicht<br />
- man benötigt neue Systemfunktionen für threadspezifische globale Variablen<br />
- z.B.: create_global, write_global, read_global<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 16
Systemaufrufe<br />
Verwendung von (unterbrechbaren) Systemfunktionen<br />
• ein Thread wird bei der Ausführung einer Systemfunktionen unterbrochen<br />
• ein anderer Thread greift dann potentiell auf die gleichen Datenstrukturen zu, in dem er die<br />
gleiche Funktion ebenfalls aufruft<br />
send(m1)<br />
Nachricht im Puffer<br />
zusammenbauen<br />
Puffer inkonsistent<br />
Bild 6.6 Threads und Systemfunktionen<br />
Thread-Wechsel<br />
send(m2)<br />
Nachricht im Puffer<br />
zusammenbauen<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 17<br />
Problem:<br />
• die meisten Systemfunktionen sind nicht reentrant programmiert<br />
• d.h. die Funktion geht davon aus, daß sie in einem Kontrollfluß zusammenhängend durchlaufen<br />
wird<br />
Lösungen:<br />
• Funktionen reentrant nachprogrammieren<br />
• Zugang zu den Funktionen durch Semaphore schützen<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 18
Threadbibliothek<br />
Threadfunktionen werden meist in einer Threadbibliothek angeboten<br />
Wichtige Vertreter:<br />
• POSIX 1003<br />
• Threadpaket von OSF DCE<br />
• Leightweight-Process-Paket von Sun<br />
typische Komponenten und Aufrufe<br />
• Thread-Verwaltung<br />
- create, exit, join, detach<br />
• Thread-Kontrolle<br />
- cancel, setcancel<br />
• Semaphore<br />
- init, destroy, lock, trylock, unlock<br />
• Bedingungs-Variablen<br />
- init, destroy, wait, signal<br />
• Verwaltung thread-globaler Variablen<br />
- create, set, get<br />
Michael Weber, Verteilte Systeme, Sommersemester 2000, <strong>Kapitel</strong> 6, Seite 19