05.08.2013 Aufrufe

Folien Kapitel 6 - Universität Ulm

Folien Kapitel 6 - Universität Ulm

Folien Kapitel 6 - Universität Ulm

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!