Martin-Luther-Universität Halle-Wittenberg
Fachbereich: Informatik
- Dr. Holger Blaar -
Seminar Sommersemester 2002
„Verteilte Systeme –
entfernter Unterprogrammaufruf“
Vorgelegt von: Daniel Klein
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
1
Inhaltsübersicht
1.
2.
3.
4.
Allgemeines
4
1.1.
1.2.
1.3.
4
5
5
Parameterübergabe
7
2.1.
2.2.
9
9
Wie erhält man die Stub- Unterprogramme ?
Wie werden Zeiger übergeben ?
Binden
10
3.1.
3.2.
11
11
Vorteile des dynamischen Binden
Nachteile des dynamischen Binden
RPC – Semantik beim Auftreten von Fehlern
12
4.1.
4.2.
12
12
4.3.
4.4.
4.5.
5.
Funktionsweise eines RPC
Ziele eines RPC
Ablauf eines RPC
Der Client kann den Server nicht lokalisieren
Die Anfragenachricht vom Client an den Server
geht verloren
Die Antwortnachricht vom Server an den Client
geht verloren
Der Server fällt aus, nachdem er eine Anfrage
erhalten hat
Der Client fällt aus, nachdem er eine Anfrage
gesendet hat
13
13
14
Implementationsaspekte
15
5.1.
5.2.
5.3.
5.4.
5.5.
5.6.
16
17
19
20
21
22
RPC- Protokolle
Bestätigungen
Kritische Pfade
Kopieren
Verwaltung von Stoppuhren
Problemkreise
6.
Zusammenfassung
23
7.
Literaturverzeichnis
24
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
2
Abbildungsverzeichnis
1
2
3
4
5
6
7
8
9
10
11
12
Ein konventioneller Unterprogrammaufruf
Ablauf eines RPC
Parameterübergabe
Ausfall des Servers
Stop-and-wait-Protokoll
Blast-Protokoll
Verlust von Bestätigungen
kritischer Pfad
Liste
Prozeßtabelle
Leser – gesteuerter Ansatz
Schreiber – gesteuerter Ansatz
4
6
7
13
17
18
19
19
21
21
23
23
Die Abbildungen und Beispiele dieser Seminararbeit sind in Anlehnung an die,
in dem Literaturverzeichnis aufgeführten, Bücher erstellt.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
3
1. Allgemeines
Das Client – Server Modell stellt einen brauchbaren Weg zur Strukturierung verteilter
Bertriebssysteme dar. Es hat jedoch einen Nachteil, dass Basisparadigma auf dem die
Kommunikation aufbaut, ist die Ein- und Ausgabe von Daten.
Viele Forscher halten es für einen Fehler, auf Basis dieses Paradigmas, Systeme für verteilte
Berechnungen zu konstruieren, da das Ziel verfolgt wird, dass verteilte Berechnungen für den
Benutzer genauso aussehen wie zentrale Berechnungen.
Von Birrell und Nelson wurde daher 1984 das Konzept des RPC (Remote Procedure Call)
vorgeschlagen. Ihre Idee bestand darin, dass ein Programm ein Unterprogramm aufrufen
kann, welches sich auf einem anderen Rechner befindet.
Nach dem Aufruf eines Unterprogramms wird der aufrufende Prozess suspendiert, und die
Ausführung des aufgerufenen Unterprogramms auf einem anderen Rechner durchgeführt.
Mit Hilfe von Parametern können hierbei Informationen zwischen den beiden Rechnern
übertragen werden. Der so vorgenommene Nachrichtenaustausch soll für den Programmierer
nicht sichtbar sein.
1.1. Funktionsweise eines RPC
Zur Klärung der Funktionsweise wird zunächst der nachfolgend dargestellte konventionelle
Unterprogrammaufruf sowie die jeweils zugehörige Änderung des Stacks betrachtet:
count = read (int, feld, intn )
lokale Variablen
des Hauptprogramms
lokale Variablen
des Hauptprogramms
SP
lokale Variablen
des Hauptprogramms
SP
int
feld
intn
Rücksprungadresse
lokale Variablen
von read
SP
-a-b-cSP – Stack Pointer, int – eine Integer Zahl, feld – ein Feld, intn – eine Integer Zahl
Abbildung 1: Ein konventioneller Unterprogrammaufruf
a) Der Stack vor dem Aufruf von read.
b) Während der Ausführung von read legt der Aufrufer die Parameter in umgekehrter
Reihenfolge auf den Stack.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
4
c) Nachdem das Unterprogramm read ausgeführt wurde, legt es seinen Ergebniswert in einem
Register ab, entfernt die Rücksprungadresse vom Stack und übergibt Kontrolle wieder an
den Aufrufer. Dieser nimmt die Parameter wieder vom Stack.
In Bezug auf die Übergabe von Parametern gibt es drei Übergabemodi zu unterscheiden, die
nachfolgend dargestellt sind:
Call by Value
Bei diesem Übergabemodus werden die Werteparameter (int/intn) einfach auf den Stack
kopiert und stellen für das aufgerufene Unterprogramm eine initialisierte lokale Variable
dar. Das aufgerufene Unterprogramm kann den Wert dieser lokalen Variablen ändern, ohne
den Wert der Originalvariablen im aufrufenden Unterprogramm zu beeinflussen.
Call by Reference
In der Programmiersprache C ist ein Referenzparameter ein Zeiger auf die Adresse einer
Variablen und nicht der Wert der Variablen selbst. In der, in diesem Unterpunkt dargestellten,
Abbildung ist feld ein derartiger Referenzparameter. Auf dem Stack wird also nur die
Anfangsadresse des Feldes abgelegt. Wenn das aufgerufene Unterprogramm diesen Parameter
nutzt, um etwas in dem Feld abzuspeichern, wird somit das Feld im aufrufenden
Unterprogramm ebenfalls verändert.
Call by copy / restore
Dieser, nicht in C zur Verfügung stehende, Modus ist Call by Value ähnlich, jedoch werden
hierbei dieVariablen nach Beendigung des aufgerufenen Unterprogramms wieder an das
aufrufende Unterprogramm zurückkopiert.
Welcher Übergabemodus zur Verfügung gestellt wird, hängt von den Entwicklern einer
Programmiersprache ab. Während zum Beispiel in C Integer Typen immer als Wert und
Felder immer als Referenz übergeben werden, kann in Pascal der Übergabemodus für jeden
Parameter frei gewählt werden.
1.2. Ziele eines RPC
Es ist wichtig, dass der Aufruf eines entfernten Unterprogramms genauso aussieht wie ein
lokaler Aufruf. Ein RPC soll also transparent gestaltet sein.
Weder das aufgerufene noch das aufrufende Unterprogramm sollen erkennen, dass das jeweils
andere Programm auf einem anderen Rechner ausgeführt wird.
1.3. Ablauf eines RPC
In diesem Abschnitt wird der Ablauf eines RPC unter Berücksichtigung der geforderten
Transparenz dargestellt. Angenommen read (vergleiche Kapitel 1.1.) ist ein entferntes
Unterprogramm, so ist in der Bibliothek eine andere Version von read enthalten – ein
sogenannter Client – Stub. Dieser verpackt die Parameter in eine Nachricht und beauftragt
den Kern, die Nachricht an den Server zu versenden.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
5
Nach Aufruf von send ruft der Client- Stub receive auf und bleibt solange blockiert, bis eine
Antwort eintrifft.
Trifft die Nachricht beim Zielrechner ein, wird sie vom Kern an den Server- Stub übergeben,
der wiederum an den aktuellen Server gebunden ist. Vom Server-Stub werden die in der
Nachricht enthaltenen Parameter ausgepackt und das Unterprogramm aufgerufen.
Der Server führt nun seine Arbeit aus und gibt die Ergebnisse zurück. Nachdem der Aufruf
beendet ist, erhält der Server- Stub die Kontrolle zurück. Dieser verpackt zuerst die
Ergebnisse in eine Nachricht und sendet diese mittels send an den Client, um danach wieder
receive aufzurufen und auf die nächste Nachricht zu warten.
Danach ruft der Server wieder receive auf und wartet auf die nächste Nachricht.
Ist die Nachricht wieder auf dem Client-Kern angekommen, erkennt dieser, dass sie für den
Client- Prozess bestimmt ist und kopiert sie in den entsprechenden Puffer. Danach wird der
Client entblockiert. Vom Client – Stub wird die Nachricht überprüft, die Ergebnisse werden
ausgepackt und auf dem Stack abgelegt.
Nachdem der Aufrufer die Kontrolle zurückerhält, weiß er nur, dass die Daten vorliegen, aber
er weiß nicht, dass der Aufruf entfernt erfolgt ist.
Nachfolgend wird die gesamte Verfahrensweise eines RPC noch einmal grafisch und in
zusammengefasster Form dargestellt:
Client - Rechner
Server - Rechner
Client - Stub
Aufruf
Server-Stub
Parameter
einpacken
Parameter
auspacken
Client
Rückkehr
Aufruf
Server
Ergebnis
auspacken
Ergebnis
einpacken
Kern
Rückkehr
Kern
Nachrichtentransport über das Netzwerk
Abbildung 2: Ablauf eines RPC
1.
2.
3.
4.
5.
6.
7.
Der Client ruft das Unterprogramm im Client-Stub in gewohnter Weise auf.
Der Client – Stub erzeugt eine Nachricht und übergibt sie dem Kern.
Der Kern sendet die Nachricht an den entfernten Kern.
Der entfernte Kern übergibt die Nachricht an den Server- Stub.
Der Server- Stub packt die Parameter aus und ruft das Unterprogramm im Server auf.
Der Server führt das Unterprogramm aus und übergibt die Ergebnisse an den Server-Stub.
Der Server-Stub verpackt die Ergebnisse in eine Nachricht und übergibt sie dem Kern.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
6
8. Der entfernte Kern sendet die Nachricht an den Kern des Clients.
9. Der Client-Kern übergibt die Nachricht an den Client-Stub.
10. Der Client – Stub packt die Ergebnisse aus und übergibt sie dem Client.
Insgesamt betrachtet, liegt der Vorteil des Schemas in der Transparenz für den Client, da
entfernte Dienste durch einfache Unterprogrammaufrufe in Anspruch genommen werden
können, ohne das ein Aufruf von send und receive erfolgen muß. Die Details des
Nachrichtenaustausches sind durch 2 Bibliotheksroutinen verborgen.
Weder Client noch Server merken etwas von der entfernten Ausführung.
2. Parameterübergabe
Betrachten wir nun einmal die Probleme, die bei der Parameterübergabe auftreten. Dabei wird
insbesondere auf das Parameter-Verpacken (parameter marshalling) eingegangen.
Folgendes Beispiel:
sum (i,j)
Bei diesem Beispiel sind die Parameter zwei Integer-Werte (i,j) und das Ergebnis ist eine
Summe.
Client- Rechner
Server - Rechner
Client - Stub
.....
....
.....
n = sum (4,7);
.........
....
....
Server-Stub
Nachricht
sum
4
7
sum (i,j)
int i,j;
{
return (i+j);
}
sum
4
7
Kern
Kern
Abbildung 3: Parameterübergabe
Wie läuft dieses Beispiel nun als RPC ab? Zuerst nimmt der Client-Stub beide Parameter
sowie Namen oder Nummer des Unterprogramms, das aufgerufen werden soll und fügt die
Informationen zusammen in eine Nachricht ein.
Nachdem die Nachricht beim Server eingetroffen ist, erkennt dieser, welches Unterprogramm
aufgerufen werden soll und führt den entsprechenden Aufruf durch.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
7
Hat der Server seine Arbeit erledigt, erhält der Server-Stub die Kontrolle zurück. Der ServerStub verpackt das Ergebnis in eine Nachricht, die wieder an den Client – Stub gesendet wird.
Der Client – Stub packt die Nachricht aus und übergibt das Ergebnis an das ClientUnterprogramm.
Wenn Client- und Serverrechner identisch, und die Parameter nur von einem skalaren Typ
sind (Integer, Character...), arbeitet das Modell gut.
Jedoch treten in der Praxis, in großen verteilten Systemen, eine Vielzahl verschiedener
Rechnertypen auf. Ein Problem hierbei sind die unterschiedlichen Zeichencodes. Während
IBM-Großrechner den EBCDIC – Zeichencode benutzen, wird von IBM-Personalcomputern
der ASCII – Zeichencode verwendet. Treten diese beiden Rechnertypen als Client und Server
auf, ist die Parameterübergabe aufgrund der unterschiedlichen Interpretation der Zeichen nicht
möglich.
Auch die unterschiedliche Darstellung der Integer-Zahlen als 1-Komplement oder
2-Komplement stellt sich als Problem dar.
Ein weiteres Problem ist die Reihenfolge in der die Bytes nummeriert werden. Während bei
dem sogenannten big endian Format die Nummerierung von „links nach rechts“ (SPARC Format) erfolgt, verhält es sich bei dem little endian Format genau umgekehrt (INTEL –
Format).
An folgendem Beispiel werden die Probleme der unterschiedlichen Formate genauer
betrachtet: (die Zahlen über den Kästchen geben jeweils die Byte- Adressen an)
Client ist ein 386 – Intel Rechner (little endian Format)
Server ist ein Sun SPARC- Rechner (big endian Format)
übertragen werden soll eine Integer- Zahl und eine Zeichenkette der Länge 4
jeder der beiden Parameter belegt ein 32-Bit Wort
-
3
Folgende Darstellung
ergibt sich im little- endian Format:
Es wird also die Zahl 5 und das Wort
„JILL“ dargestellt“.
1
2
3
5
0
0
0
J
I
L
L
4
5
6
1
0
0
0
0
5
L
L
I
J
7
0
2
6
5
4
Wird das ganze nun an den „Server“ (big-endian)
gesendet, erhält man auch das Wort „JILL“, aber
die Zahl 83.886.080 (5*224).
7
0
Als Lösungsansatz kommt in Betracht, jedes Wort nach dem
Empfang einfach zu invertieren.
Nun erhält man die Zeichenkette „LLIJ“ und den Integer
Wert 5. Dieser Lösungsansatz ist also unbrauchbar.
Seminar Verteilte Systeme
Daniel Klein
1
2
3
0
0
0
5
L
L
I
J
4
5
6
entfernter Unterprogrammaufruf
7
8
Man benötigt genaue Informationen, ob eine Zeichenkette oder eine Integer- Zahl übertragen
wird. Nur auf diese Art und Weise kann das Problem gelöst werden.
Eine mögliche Lösung ist es, einen Netzwerk- Standard oder kanonische Überdeckung
festlegen, d.h. sowohl Client als auch Server müssen vor dem Versenden einer Nachricht
diese in den Standard überführen.
Ein Nachteil dabei ist die mögliche Ineffizienz. Wenn Server und Client das big endian
Format nutzen, aber als Netzwerk- Standard das little endian Format vorgesehen ist, erfolgen
2 Konvertierungen, obwohl keine nötig gewesen wäre.
Aus diesem Problem heraus ergibt sich ein neuer Lösungsansatz. Beim Versand der Nachricht
wird das interne Format des Client verwendet, wobei im ersten Byte der Nachricht angegeben
wird, welches Format dies ist. Wenn die Nachricht dem Server zugeht, überprüft dieser das
erste Byte und erkennt, welches Format verwendet wurde. Je nach Übereinstimmung der
Formate erfolgt nun eine Konvertierung oder nicht.
Analog der Umwandlung der endian-Formate erfolgt die Umwandlung von 1-er / 2-er
Komplement und ASCII / EBCDIC – Code.
Zusammenfassend läßt sich feststellen, dass der wesentliche Kernpunkt die Kenntnis über das
Format der Nachricht und die vom Client genutzte Darstellung ist.
2.1. Wie erhält man die Stub- Unterprogramme ?
In vielen RPC-basierten Systemen werden die Stub-Unterprogramme automatisch erzeugt.
Mittels der Server-Spezifikaton kann ein Übersetzer konstruiert werden, der sowohl Clientals auch Server –Stub erzeugt. Werden beide Unterprogramme aus derselben Spezifikation
erzeugt, werden die Programmierer entlastet, die Fehlerwahrscheinlichkeit verringert und die
Transparenz des Systems bezüglich unterschiedlicher interner Darstellung von Daten erhöht.
2.2. Wie werden Zeiger übergeben ?
Dies ist ein relativ schwieriges Thema, da ein Zeiger nur im Adreßraum des Prozesses eine
sinnvolle Bedeutung besitzt, in dem er benutzt wird.
Es reicht nicht aus, nur den Wert des Zeigers an den Server zu übergeben, da die übergebene
Adresse beim Server mitten im Programmtext liegen könnte.
Eine mögliche Lösung ist, Zeiger und Referenzparameter grundsätzlich zu verbieten. Dies ist
jedoch eine wenig befriedigende Vorgehensweise.
Betrachten wir noch einmal das Beispiel aus Abschnitt 1.1. count = read (int, feld, intn ).
Hierbei weiß der Client- Stub, dass der zweite Parameter auf ein Feld von Zeichen zeigt.
Weiß der Client-Stub auch noch die Größe des Feldes, dann gibt es eine Lösung, indem das
Feld in eine Nachricht kopiert und an den Server gesendet wird.
Anschließend ruft der Server- Stub den Server mit einem Zeiger auf dieses Feld auf, wobei es
keine Rolle spielt, ob der Zeiger einen anderen numerischen Wert als beim Client hat.
Die Änderungen des Servers wirken sich nun unmittelbar auf den Nachrichtenpuffer des
Servers-Stubs aus. Nachdem der Server seine Arbeit beendet hat, wird die Original-Nachricht
zurück an den Client- Stub gesendet. Dieser kopiert das Feld in den Client.
Als Fazit bleibt festzustellen, dass der Übergabemodus call by reference durch den Modus
call- by- copy/restore ersetzt wurde.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
9
3. Binden
Bei diesem Unterpunkt wird nun die Frage geklärt, wie der Client den Server lokalisiert.
Eine Möglichkeit besteht darin, die Netzwerk-Adresse des Servers fest in den Client
einzuprogrammieren. Der Nachteil dabei ist die geringe Flexibilität. Soll beispielsweise ein
anderer Server gewählt werden oder hat sich die Schnittstelle geändert, so sind aufwendige
Änderungen notwendig.
Eine andere Möglichkeit besteht im Verfahren des dynamischen Bindens zur Verbindung
von Client und Server. Betrachten wir dazu einmal die folgende Server-Spezifikation:
# include <header.h>
specification of file_server, version 3.1;
long read (in char name [MAX_PATH] , out char buf [BUF_SIZE] ,
in long byte bytes, in long position);
long write (in char name [MAX_PATH] , in char buf [BUF_SIZE] ,
in long byte bytes, in long position);
int create
(in char name [MAX_PATH] , in int mode);
int delete
(in char name [MAX_PATH] );
end;
Folgende Bedeutung haben die dargestellten Elemente der Server- Spezifikation:
file_server – Name des Servers
version 3.1 - Versionsnummer
read, write, create, delete - Liste von Unterprogrammen, die der Server anbietet
int.... - für alle Unterprogramme sind die Typen der Parameter angegeben
in, out - jeder Parameter ist als in, out oder in out Parameter spezifiziert (Richtung
relativ zum Server, ein in-Parameter wird an den Server gesendet)
name - informiert Server, welche Datei „genutzt“ werden soll
byte - gibt an, wie viele Bytes übertragen werden müssen
position- ab welcher Stelle soll gelesen bzw. geschrieben werden
buf - verweist auf die Adresse, an die der Server die Daten ablegt, die der Client
angefordert hat
In / Out Parameter, von denen in der dargestellten Spezifikation keiner vorhanden ist, werden
vom Client an den Server gesendet, dort modifiziert und dann wieder an den Client
zurückgesendet.
Die dargestellte formale Spezifikation wird als Eingabe für den Stub – Generator genutzt.
Dieser erzeugt daraus den Server- als auch den Client-Stub. Eine Ablage der Stub –
Programme erfolgt in entsprechenden Bibliotheken.
Wenn ein Client-Programm eines der Unterprogramme, die in der Spezifikation enthalten
sind, aufruft, wird das zugehörige Client-Stub-Programm dazu gebunden. Genauso
funktioniert es bei dem Server.
Bei Beginn der Ausführung eines Servers sendet der Server an ein Programm (Binder) eine
Nachricht, um seine Existenz bekannt zu geben. Es wird also der Server registriert bzw. seine
Schnittstelle exportiert.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
10
In der Nachricht der Servers sind folgende Parameter enthalten:
Name des Servers
Versionsnummer der Servers
eindeutiger Bezeichner (häufig ein 32 bit Wort)
Handle (dient der Lokalisierung des Servers, z.B. eine Ethernet -oder IP-Adresse)
Ebenso kann sich der Server bei Beendigung seiner Dienste beim Binder abmelden.
Darstellung der Schnittstelle des Binders:
Aufruf
Eingabe
Ausgabe
Name, Version, Handle, eindeutige Bezeichner
Register
Deregister Name, Version, eindeutige Bezeichner
Name, Version
Handle, eindeutige Bezeichner
Lookup
Ausgehend von der Ausgangsfrage dieses Kapitales „Wie lokalisiert der Client den Server ?“
läßt sich nun der folgende Verfahrensweg feststellen. Ruft der Client zum ersten Mal ein
Unterprogramm auf, dann erkennt der Client- Stub, dass bisher noch keine Serveranbindung
vorliegt. Demzufolge wird vom Client-Stub eine Nachricht, die Name und Versionsnummer
enthält, an den Binder gesendet, um eine entsprechende Schnittstelle zu importieren. Der
Binder prüft danach, ob die angeforderte Schnittstelle von einem Server exportiert wird. Ist
dies nicht der Fall, schlägt der Aufruf des Unterprogramms fehl.
Wenn aber ein passender Server existiert, so sendet der Binder das Handle und den
eindeutigen Bezeichner an den Client- Stub. Das Handle wird vom Client-Stub genutzt, um
eine erste Nachricht an den Server zu senden.
3.1. Vorteile des dynamischen Binden
Ein großer Vorteil besteht in der Flexibilität des Ex- und Importes von Schnittstellen. Für den
Fall, dass mehrere Server die gleiche Schnittstelle anbieten, kann der Binder die Clients
zufällig auf die Server aufteilen und so die Auslastung der beteiligten Rechner ausgleichen.
Weiterhin besteht die Möglichkeit, die Server in regelmäßigen Abständen zu überprüfen.
Wenn ein Server nicht mehr antwortet, kann er vom Binder abgemeldet werden.
Weiterhin kann der Binder die Authentifikation unterstützen. Seitens der Servers kann z.B.
eine „User-Liste“ angegeben werden, die die Benutzer enthält, die seine Dienste nutzen
dürfen. Vom Binder werden alle Anfragen von Clients abgelehnt, die nicht in dieser Liste
enthalten sind.
Außerdem kann der Binder überprüfen, ob sowohl Client als auch Server dieselbe
Schnittstellenversion benutzen.
3.2. Nachteile des dynamischen Binden
Durch das zusätzliche Importieren und Exportieren von Schnittstellen wird mehr Zeit
benötigt. Da aber etliche Client-Prozesse kurzlebig sind und jeder Prozeß den Vorgang des
dynamischen Bindens neu durchlaufen muß, können Auswirkungen in einem erheblichen
Maße auftreten.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
11
Außerdem besteht die Gefahr, dass in großen verteilten Systemen der Binder zum Engpaß
wird und somit mehrere Binder benötigt werden. Um nun aber die Konsistenz zwischen den
Bindern zu halten, müssen bei jeder Registrierung oder Entregistrierung eine große Anzahl
von Nachrichten verschickt werden.
4. RPC – Semantik beim Auftreten von Fehlern
Bei Zusammenfassung der ersten drei Kapitel sind wir dem Ziel, nämlich das ein entfernter
Unterprogrammaufruf wie ein lokaler Unterprogrammaufruf aussieht, schon recht nahe
gekommen.
Was passiert aber, wenn durch Auftreten eines Fehlers Client oder Server nicht mehr richtig
funktionieren ?
4.1. Der Client kann den Server nicht lokalisieren.
Ein solches Szenario kann auftreten, wenn es zum Zeitpunkt der Anfrage des Clients keinen
passenden Server gibt, der gerade ausgeführt wird.
Dies kann geschehen, wenn der Client mit einer veralteten Version des Client-Stubs übersetzt
wurde, da das Client – Programm längere Zeit nicht genutzt wurde. Ist in der Zwischenzeit
der Server weiterentwickelt worden und sind in diesem Zusammenhang neue Schnittstellen
und Stubs implementiert worden, so kann der Binder für das „veraltete“ Client-Programm
keinen passenden Server finden. Als Ergebnis wird vom Binder ein Fehler zurückgemeldet.
Wie behandelt man nun aber einen solchen Fehler? Eine mögliche Vorgehensweise ist, dass
als Ergebnis des Unterprogrammaufrufes bei Auftreten eines Fehlers eine „-1“ als Wert
zurückgeliefert wird. Mit dieser Lösung kommt man leider aber nicht weit. Bei einer
Summenbildung der beiden Parameter +7 und -8 ist der Rückgabewert ebenfalls –1, hier soll
aber kein Fehler zum Ausdruck gebracht werden.
Eine Möglichkeit zur Lösung des Problems ist die Ausnahmebehandlung. Beim Auftreten
eines Fehlers besteht bei einigen Programmiersprachen (z.B. Ada) die Möglichkeit, spezielle
Unterprogramme bei entsprechenden Fehlern aufzurufen.
Durch diese Vorgehensweise treten jedoch auch Nachteile auf. Erstens stellt nicht jede
Programmiersprache die Möglichkeit einer Ausnahmebehandlung zur Verfügung und
zweitens wird durch den Zwang einer zusätzlichen Ausnahmebehandlung die Transparenz,
die erreicht werden soll, zerstört.
4.2. Die Anfragenachricht vom Client an den Server geht verloren.
Dieser Fehler ist recht einfach zu lösen. Beim Versand der Nachricht durch den Kern wird
eine Stoppuhr gestartet. Wenn nach Ablauf der Stoppuhr keine Bestätigung oder Antwort
eingegangen ist, dann sendet der Kern die Nachricht erneut.
Da die Nachricht verloren gegangen ist, kann der Server nicht zwischen dem Original und der
wiederholten Anfrage unterscheiden.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
12
4.3. Die Antwortnachricht vom Server an den Client geht verloren.
Die Lösung dieses Problems stellt sich als relativ schwierig heraus. Zwar liegt die
Möglichkeit nahe, wiederum eine Stoppuhr einzuführen und falls keine Antwort innerhalb des
Zeitintervalls eintrifft, die Anforderung zu wiederholen, jedoch weiß der Client-Kern nicht,
warum keine Antwort eintrifft.
Es besteht die Möglichkeit, dass die Anfrage oder die Antwort verloren gegangen ist oder das
der Server einfach nur viel zu tun hat.
Man muß nunmehr die Nachrichten klassifizieren. Es gibt Nachrichten, die beliebig oft
wiederholt werden können, ohne das es zu Verfälschungen des Ergebnisses kommt. Ein
Beispiel wäre hier das Lesen der ersten 1024 Byte einer Datei. Diese Form der Nachrichten
wird als idempotent bezeichnet.
Was passiert aber bei einer Anforderung an einen Bank –Server, eine Million Euro von einem
Konto auf ein anderes zu überweisen? Angenommen der Server erhält die Anfrage, bearbeitet
diese und sendet eine Antwortnachricht, die aber verloren geht. Der Client erhält nunmehr
keine Antwort und sendet seine Anfrage erneut. Die wiederholte Anfrage wird vom Server
wie eine neue Anforderung behandelt und die Überweisung ein zweites Mal ausgeführt.
Anhand dieses Beispiels erkennt man, dass nicht alle Anfragen idempotent sind.
Eine mögliche Lösung des Problems ist die Vergabe von fortlaufenden Sequenznummern
durch den Client-Kern. Der Server-Kern ist dann in der Lage, sich die jeweils letzte
Sequenznummer jedes Client-Kerns zu merken und Original sowie wiederholte Anfrage
voneinander zu unterscheiden. Somit ist der Server in der Lage, wiederholte Nachrichten
abzulehnen.
Zusätzliche Sicherheit kann man erhalten, wenn ein Bit im Nachrichtenkopf zur
Unterscheidung zwischen Original und wiederholter Anfrage genutzt wird.
4.4. Der Server fällt aus, nachdem er eine Anfrage erhalten hat
Ein weiteres Problem stellt der Ausfall des Servers dar. Zu unterscheiden gibt es hierbei zwei
verschiedene Möglichkeiten:
1.) der Server fällt aus, nachdem
er die Anfrage ausgeführt hat
2.) der Server fällt aus, bevor er die Anfrage
ausgeführt hat
Server
Server
REQ
No
REP
Empfang
Ausführung
Ausfall
REQ
Empfang
Ausfall
No
REP
Abbildung 4: Ausfall des Servers
Das Problem liegt nun darin, dass diese beiden Fälle unterschiedlich zu behandeln sind. Im
ersten Fall muß dem Client ein Fehler gemeldet werden, im zweiten Fall reicht es aus, die
Anfrage neu zu starten. Leider ist der Client nicht der Lage, die beiden Fälle zu unterscheiden,
da ihm nur bekannt ist, dass seine Stoppuhr abgelaufen ist.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
13
Um das Problem zu lösen, gibt es die folgenden drei Ansätze:
mindestens- einmal Semantik (at least once semantics)
Hierbei wird nun gewartet, bis der Server neu gestartet bzw. bis der Client an einen neuen
Server angebunden worden ist. Danach wird ein neuer Versuch unternommen, solange bis der
Client eine Antwort zurückerhält. Diese Vorgehensweise stellt sicher, dass ein RPC
mindestens einmal, möglicherweise aber auch öfters ausgeführt wird. Dies stellt jedoch für
eine nicht idempotente Aufgabe ein großes Problem dar.
höchstens- einmal Semantik (at most once semantics)
Bei diesem Ansatz besteht die Vorgehensweise darin, sofort aufzugeben und dem Client einen
Fehler zu melden. Dabei wird sichergestellt, dass ein RPC höchstens einmal, aber
möglicherweise auch gar nicht ausgeführt wird.
nichts wird garantiert
Dieser Ansatz geht davon aus, nichts zu garantieren. Beim Ausfall eines Servers erhält der
Client keine Hilfe oder Garantie. Als Folge dessen kann jeder RPC keinmal oder mehrmals
ausgeführt werden. Der eigentliche Vorteil dieses Ansatzes liegt in der einfachen
Implementierbarkeit.
Alle drei dargestellten Vorgehensweisen sind wenig befriedigend, da man erreichen möchte,
dass ein RPC genau einmal ausgeführt wird (exactly once semantics). Leider gibt es keine
Möglichkeit, dies im Allgemeinen sicherzustellen.
Somit stellt die Möglichkeit eines Server-Ausfalls ein wesentliches Unterscheidungsmerkmal
zwischen einem Ein-Prozessor-System und einem verteilten System dar.
4.5. Der Client fällt aus, nachdem er eine Anfrage gesendet hat.
Was geschieht nun, wenn der Client ausfällt, nachdem er eine Anfrage gesendet hat. Die vom
Server vorgenommenen Berechnungen werden von niemandem mehr benötigt. Diese
Berechnungen (auch Waisen genannt) können zu erheblichen Problemen führen,
beispielsweise zur Verschwendung von Rechenzeiten.
Zur Beseitigung derartiger Probleme werden in der weiteren Abhandlung vier Lösungsansätze
vorgestellt.
Ausrottung (extermination)
Bei der ersten denkbaren Lösung werden, vor dem Versand des RPC, die Absichten des
Client in eine Protokollierungsdatei eingetragen. Diese Datei wird auf einem Medium
(z.B. Festplatte) abgespeichert, das den Ausfall übersteht. Bei Neustart des Client wird die
Protokollierungsdatei ausgewertet und die Waisen werden eliminiert.
Jedoch treten folgende Nachteile bei diesem Ansatz auf:
dadurch, das bei jedem RPC ein Schreibvorgang auf der Festplatte (oder anderem
Medium) stattfindet, entstehen hohe Kosten
die Waisen können wiederum RPCs ausführen (Abkömmlinge), die nicht mit
erfasst werden
bei einer Teilung des Netzwerkes durch Ausfall eines Verbindungsknotens können
Waisen ggf. nicht eliminiert werden
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
14
Reinkarnation (reincarnation)
Bei diesem Ansatz wird die Zeit in aufsteigende numerierte Epochen eingeteilt. Wird der
Client nach einem Ausfall neu gestartet, sendet er eine Nachricht an alle Rechner, in der der
Beginn einer neuen Epoche angekündigt wird. Erhält ein Rechner eine solche Nachricht,
bricht er alle entfernten Berechnungen ab.
Zwar besteht die Möglichkeit, dass das Netzwerk bei Ankündigung der neuen Epoche geteilt
war und einige Waisen überlebt haben, dies stellt jedoch kein Problem dar. Kommt die
Antwort einer solchen Waise beim Client an, erkennt dieser, anhand von Nummern, dass sie
zu einer anderen Epoche gehört.
sanfte Reinkarnation (gentle reincarnation)
Erhält ein Rechner die Ankündigungsnachricht einer neuen Epoche, so überprüft er, ob er eine
entfernte Berechnung durchführt. Wenn das so ist, versucht er den Besitzer zu finden. Nur
wenn der Besitzer nicht gefunden werden kann, wird die Berechnung abgebrochen.
Verfallszeitpunkten (expiration)
Hierbei erhält jeder RPC eine feste Zeitdauer T für die Ausführung seiner Berechnungen.
Reicht die Zeit für die Berechnung nicht aus, muß er explizit ein neues Zeitintervall
anfordern. Wartet der Client nach einem Ausfall nun eine Zeitdauer T, so kann er sicher sein,
dass es keine Waisen mehr gibt.
Das Problem liegt in der richtigen Auswahl des Zeitintervalls T.
Zusammenfassend lässt sich feststellen, dass keiner der vier dargestellten Lösungsansätze für
die Praxis besonders geeignet ist, da das Abbrechen einer Waisen unvorhersehbare Folgen
haben kann. Beispielsweise kann ein Waise Sperren für Dateien und Datenbanksätze besitzen,
die dann nie wieder aufgehoben werden. Es besteht aber auch die Möglichkeit, dass der Waise
Einträge verursacht hat, die Prozesse zu einem späteren Zeitpunkt starten, so dass der
Abbruch eines Waisen nicht immer alle seine Spuren beseitigt.
5. Implementationsaspekte
Ein entscheidender Punkt eines verteilten Systems ist seine Leistungsfähigkeit, die wesentlich
von der Kommunikationsgeschwindigkeit beeinflusst wird.
Hierbei ist die Implementation der Kommunikation oftmals entscheidender als das
zugrundeliegende Konzept.
In den nachfolgenden Darstellungen werden einige Implementationsaspekte, unter besonderer
Beachtung der Leistungsfähigkeit, dargestellt.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
15
5.1. RPC-Protokolle
Zuerst wird die Auswahl des Protokolls betrachtet. Dies ist ein wichtiger Punkt, da hier ein
großer Einfluß auf die Leistungsfähigkeit des Systems genommen werden kann.
Am Anfang muß man sich für ein verbindungsorientiertes oder verbindungsloses Protokoll
entscheiden. Nutzt man ein verbindungsorientiertes Protokoll, wird die Verbindung, die bei
Bindung von Client und Server aufgebaut wird, für die gesamte Kommunikation genutzt. Der
Vorteil dieser Verbindung liegt in der starken Vereinfachung der Kommunikation. Somit muß
sich ein Kern nach dem Versand einer Nachricht nicht darum kümmern, ob die Nachricht
verlorengegangen ist. Desweiteren muß sich der Kern nicht um den Austausch von
Bestätigungen kümmern.
All diese Aufgaben werden auf einer niedrigeren Ebene von der Software erledigt, die die
Verbindung zur Verfügung stellt. Gerade im Hinblick auf Weitbereichsnetze ist dieser Vorteil
unbedingt nötig.
Der Nachteil verbindungsorientierter Protokolle ist der Verlust von Leistungsfähigkeit, gerade
in LAN- Netzen, da hier die zusätzliche Software im Weg steht. In diesen Netzen ist der
Vorteil, dass keine Pakete verloren gehen, nicht entscheidend, da die Übertragung in einem
LAN-Netz sehr sicher ist.
Im Ergebnis der Betrachtung ist festzustellen, dass die meisten verteilten Betriebssysteme, die
für räumlich enge Netze entwickelt wurden, verbindungslose Protokolle benutzen.
Im zweiten Schritt ist zu entscheiden, ob ein allgemeines Standardprotokoll oder ein speziell
für RPC- Anwendungen entworfenes Protokoll genutzt werden soll. Leider gibt es auf diesem
Gebiet noch keine Standardisierung und somit bedeutet es, ein RPC-Protokoll benutzen zu
wollen, oft selbst eines entwerfen zu müssen.
Ein genereller Trend, welche der beiden genannten Möglichkeiten mehr genutzt wird, lässt
sich nicht feststellen, da sich die Entscheidungen der Systementwickler etwa gleichmäßig auf
die beiden Alternativen verteilen.
Betrachten wir zuerst die Entscheidung zugunsten eines Standardprotokolls. Hierbei bietet
sich die Möglichkeit IP oder UDP als Protokoll einzusetzen. Diese Variante bietet folgende
Vorteile:
diese Protokolle sind bereits entworfen und sparen damit viel Arbeit
es sind bereits viele Implementierungen verfügbar
fast alle UNIX-Systeme können diese Pakete senden und empfangen
IP- und UDP – Pakete können direkt über viele existierende Netzwerke übertragen
werden
Es ist also zusammenfassend festzustellen, dass IP und UDP einfach zu benutzen sind und gut
in existierende Netze (z.B. Internet) und UNIX-Systeme passen. Dadurch vereinfacht sich das
Programmieren von Client und Server in UNIX-Systemen.
Aber trotz allem sind auch Schwachstellen dieser Vorgehensweise festzustellen. Hierbei
kristallisiert sich wiederum die geringe Leistungsfähigkeit heraus.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
16
Die Ursache ist darin zu sehen, dass IP nicht als Endbenutzerprotokoll entworfen wurde,
sondern als Basis für den Aufbau sicherer TCP- Verbindungen über größere Netzwerke
gedacht ist. Beispielsweise ist hier die Behandlung von Zwischenstationen zu sehen, die
Pakete in kleinere Stücke aufteilen, so dass sie über ein Netzwerk mit einer kleineren
maximalen Paketgröße übertragen werden können. Das Problem liegt darin, dass dies in
einem LAN-basierten Netzwerk niemals der Fall sein wird, und trotzdem die Felder in den
Paketköpfen, die dafür vorgesehen sind, vom Sender ausgefüllt und vom Empfänger überprüft
werden müssen.
Betrachten wir nun die andere Alternative, nämlich die Nutzung eines speziellen RPCProtokolls. Vorteilhaft ist hierbei, dass ein solches Protokoll im Gegensatz zu IP nicht
versucht, Pakete zu behandeln, die über mehrere verschiedene Netzwerke geschickt werden
und dabei verloren gehen können.
Aber auch folgende Nachteile entstehen durch den Einsatz eines speziellen RPC- Protokolls:
ein solches Protokoll muß entworfen, implementiert, getestet und in existierende
Systeme eingebettet werden
die Akzeptanz für ein weiteres neues Protokoll ist gering
Im letzten Schritt erfolgt nun die Betrachtung der Paket- und Nachrichtengröße. Unabhängig
von der Menge der zu versendenden Daten, verursacht ein RPC einen konstanten Aufwand.
Demzufolge ist also effizienter, eine 32 Kbyte große Datei in einem 32 Kbyte RPC zu lesen,
als das Lesen der Datei in 32 1Byte RPCs vorzunehmen.
Es ergibt sich also die Notwendigkeit, dass Protokolle und Netzwerke eine große Übertragung
gestatten. Leider sind einige RPC-Systeme hierbei auf geringe Größen begrenzt.
5.2. Bestätigungen
In diesem Unterabschnitt sind Themengebiete Fehlerkontrolle, Flusskontrolle und Verlust von
Bestätigungen zu unterscheiden. Wenden wir uns zuerst der Fehlerkontrolle zu.
Fehlerkontrolle
Die Frage dieses Abschnitts lautet: „ Wenn ein großer RPC auf viele kleine Pakete aufgeteilt
wird, wie soll dann die Bestätigung erfolgen ?“
4 KByte Daten
0 1 2 3
Betrachten wir mal einen 4 Kbyte
großen Datenblock, der auf einen Server geschrieben werden soll.
Client
Eine Möglichkeit, die als Stop-and-waitProtokoll bezeichnet wird, besteht darin,
jedes einzelne Paket zu bestätigen. Nach
Erhalt der Bestätigung eines Paktes sendet
der Client das nächste Paket. Diese
Vorgehensweise hat den Vorteil, dass der
Client, wenn er für ein Paket keine
Bestätigung erhält, da es verloren gegangen
ist, nur dieses noch einmal wiederholen
muß.
Seminar Verteilte Systeme
Daniel Klein
Server
0
ACK 0
1
ACK 1
Zeit
2
ACK 2
3
ACK 3
Abbildung 5: Stop-and-wait-Protokoll
entfernter Unterprogrammaufruf
17
Client
Server
0
1
Zeit
2
3
ACK 0-3
Eine andere Möglichkeit ist das BlastProtokoll. Hierbei sendet der Client alle
Pakete auf einmal und bekommt für die
gesamte Nachricht eine Bestätigung. Wird
beim Blast-Protokoll ein einzelnes Paket nicht
korrekt übertragen, gibt es verschiedene
Alternativen. Der Server kann die ganze
Nachricht ablehnen oder nichts unternehmen
und warten, dass der Client die ganze
Nachricht wiederholt. Er hat aber auch die
Möglichkeit, die korrekt übertragenen Pakete
zwischenzuspeichern und das fehlerhafte
Paket noch einmal anzufordern. Dies wird
auch auswählende Wiederholung genannt.
Abbildung 6: Blast-Protokoll
Ein Vorteil des Stop-and-wait-Protokolls, als auch des Ablehnens beim Auftreten eines
Fehlers, liegt in der relativ einfachen Implementierbarkeit. Die auswählende Wiederholung
erhöht zwar den Verwaltungsaufwand, senkt aber die Netzbelastung.
Im Ergebnis kommt es auf das jeweilige Netz an. Bei sehr zuverlässigen LAN- Netzwerken
gehen Pakete selten verloren, so dass das Verfahren der auswählenden Wiederholung hier
eher ineffizient ist. In einem Weitbereichsnetz sieht es da ganz anders aus, hier stellt die
auswählende Wiederholung häufig eine gute Lösung dar.
Flußkontrolle (flow control)
Hierbei wird die Funktionalität der Netzwerkbausteine betrachtet. Einerseits sind sie in der
Lage, aufeinanderfolgende Pakete fast ohne Lücke zu senden, andererseits sind sie aufgrund
begrenzter Pufferkapazitäten nicht in der Lage, beliebig viele, kurz aufeinanderfolgende
Pakete, zu empfangen.
Kann nun ein Empfänger ein eintreffendes Paket nicht akzeptieren, so tritt ein so genannter
Überschreitungsfehler (overrun error) auf und das Paket geht verloren.
Betrachten wir nun einmal Überschreitungsfehler im Hinblick auf die vorgestellten Protokolle
genauer. Beim Stop-and-wait-Protokoll kommt der Fehler nicht zum Tragen, da mit dem
Versand eines neuen Paketes gewartet wird, bis eine Bestätigung für das Vorgängerpaket
eingetroffen ist.
Wird das, eigentlich effizientere, Blast-Protokoll eingesetzt, können dagegen sehr wohl
Überschreitungsfehler auftreten. Betrachtet man in diesem Zusammenhang
Lösungsstrategien, muß man unterschiedliche Problemstellungen betrachten.
Kann der Empfänger nicht zwei Pakete unmittelbar nacheinander annehmen, so besteht die
Möglichkeit, dass der Sender zwischen dem Versand zweier Pakete einen bestimmten
Zeitraum wartet und dem Empfänger somit die Möglichkeit gibt, jedes Paket zu bearbeiten,
um beim Eingang des nächsten Paketes wieder bereit zu sein. Wählt man hier einen kurzen
zeitlichen Zwischenraum, so kann der Sender aktiv auf das Ende des Zwischenraumes warten,
wählt man einen langen Zwischenraum, kann der Sender eine Stoppuhr starten und während
dieser Zeit andere Aufgaben erledigen.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
18
Häufig ist das aktive Warten die bessere Lösung und die dabei entstehende ungenutzte Zeit
ein notwendiges Übel.
Besteht andererseits das Problem darin, dass die Pufferkapazität des Netzwerkbausteines
begrenzt ist, sagen wir auf n-Pakete, so kann der Sender n-Pakete senden und dann eine
ausreichend lange Pause machen.
Verlust von Bestätigungen
Zur Analyse des Problems wird nachfolgende Abbildung benötigt:
1
Client
Server
2
3
1: Anfrage, 2: Antwort , 3: Bestätigung
Abbildung 7: Verlust von Bestätigungen
Bevor der Server nicht die Bestätigung (Schritt 3) vom Client erhält, löscht er die Antwort
nicht. Geht nun die Bestätigung verloren, so merkt sich der Server die Antwort theoretisch
ewig. Eine Möglichkeit der Behandlung besteht nun darin, das Bestätigungen wiederum
bestätigt werden. Dies hätte jedoch eine steigende Komplexität zur Folge.
Daher wird in der Praxis vom Server häufig eine Stoppuhr gestartet und die Antwort nach
Eintreffen einer Bestätigung oder nach Ablauf der Stoppuhr gelöscht. Weiterhin besteht die
Möglichkeit, eine neue Anfrage des Client als Bestätigung zu werten.
5.3. Kritische Pfade
Da der RPC-Code entscheidend für die Leistungsfähigkeit eines Systems ist, sehen wir uns
die Folge von Instruktionen (kritischer Pfad), die bei jedem RPC ausgeführt werden, genauer
an. Betrachten wir dazu nachfolgende Abbildung:
Client- Rechner
- Rufe Stub-Routine auf
Client
Client-S
Stub
Kern
1
Server - Rechner
- Führe Auftrag aus
6
Server
- Bereite Nachrichtenpuffer vor
- Verpacke Parameter in den Puffer
- Fülle den Nachrichtenkopf aus
2
- Führe Systemaufruf aus
- Rufe Server auf
- Lege Parameter auf dem Stack ab
- Packe Parameter aus
.
- Kontextwechsel in den Kern
- Kopiere Nachricht in den Kern
- Bestimme Zieladresse
3
- Trage Adresse in den
Nachrichtenkopf ein
- Initialisiere Netzwerkschnittstelle
- Starte Stoppuhr
- Kontextwechsel in den Server-Stub
- Kopiere Nachricht in den Server-Stub
- Überprüfe ob der Stub wartet
- Entscheide, welcher Stub die Nachricht erhält
- Überprüfe, ob Paket korrekt übertragen wurde
- Prozeßunterbrechung
4
Server - Stub
5
Kern
Abbildung 8: kritischer Pfad
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
19
Untersuchen wir nun die dargestellten Schritte einmal genauer:
1. Der Client ruft den Client – Stub auf.
2. Der Client- Stub erzeugt zuerst einen Puffer, indem er die Nachricht zusammenstellen
kann. Nun werden die Parameter in ein geeignetes Format konvertiert und zusammen mit
den restlichen Kopffeldern in den Nachrichtenpuffer eingetragen. Als letztes wird eine
entsprechende Ausnahmebehandlung des Kernes ausgelöst.
3. Der Kern erhält die Kontrolle und rettet einerseits die Prozessorregister und die
Adreßtabelle, andererseits lädt er die Adreßtabelle, die er benutzt, während er sich im
Kernmodus befindet. Dies wird auch Kontextwechsel genannt. Nun wird die Nachricht in
den Adreßraum des Kernes kopiert, da Kern- und Benutzerkontext disjunkt sind. Danach
wird die Zieladresse in den Nachrichtenkopf eingetragen, die Nachricht der
Netzwerkschnittstelle übergeben und die Übertragungsstoppuhr gestartet. Nun kann der
Client entweder aktiv auf eine Antwort warten oder sich vorerst einer anderen Aufgabe
zuwenden.
4. Ist die gesamte Nachricht beim Server angekommen, wird ein Unterbrechungssignal
ausgelöst. Es wird nun von der Behandlungsroutine die eingetroffene Nachricht überprüft
und ein Ziel-Stub bestimmt, der die Nachricht erhalten muß. Wartet kein Stub auf die
Nachricht, so wird sie abgelehnt oder zwischengespeichert. Anderenfalls wird sie in den
Stub kopiert. Nun wird wiederum ein Kontextwechsel durchgeführt, d.h. der Zustand zum
Zeitpunkt des Aufrufes von receive durch den Stub wird wiederhergestellt.
5. Der Server-Stub packt die Parameter aus, legt sie auf dem Stack ab und ruft den Server
auf.
6. Nach der Ausführung des Aufrufes durch den Server wird der kritische Pfad in
umgekehrter Rheinfolge wieder abgearbeitet.
Es stellt sich nun die Frage, welcher Teil des kritischen Pfades die meiste Zeit verbraucht. Bei
Untersuchungsergebnissen von Schroeder und Burrows (1990) stellte sich heraus, dass hier
gravierende Unterschiede zwischen einem „leeren RPC“ (ohne Daten) und einem RPC mit
einem 1440 Byte großem Feld als Parameter bestehen. Man muß jedoch auch in Betracht
ziehen, dass die von Schroeder und Burrows durchgeführten Untersuchungen auf der
Spezifikation einer DEC-Firefly-Multiprozessor-Workstation beruhen und die Ergebnisse
dieser Untersuchungen somit nicht auf andere Architekturen einfach übertragbar sind.
5.4. Kopieren
Ein entscheidender Punkt bei Betrachtung der Ausführungszeit ist das Kopieren, wenn der
Adreßraum des Kerns und des Benutzerprozesses disjunkt sind. In Abhängigkeit von der
verwendeten Hard- und Software sowie der Art des Aufrufes können zwischen einem und
acht Kopiervorgänge notwendig sein.
Im Idealfall wird die Nachricht mittels DMA direkt vom Client- Stub auf das Netzwerk
ausgegeben. Nach Ankunft der Nachricht beim Kern, wird die Seite, die das Paket enthält, in
den Adreßraum des Servers eingeblendet.
Im ungünstigsten Fall können jedoch bis zu acht Kopiervorgänge notwendig sein.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
20
Im allgemein ist es einfacher, Kopiervorgänge auf der sendenden Seite, als auf der
empfangenden Seite zu vermeiden.
5.5. Verwaltung von Stoppuhren
Durch elektromagnetische Störungen oder
Überschreitungsfehler können Pakete verloren gehen.
Aus diesem Grund starten die meisten Protokolle eine
Stoppuhr, wenn sie eine Nachricht versenden und eine
Antwort erwarten. Trifft die Antwort nicht vor dem
Ablauf der Stoppuhr ein, wird die Nachricht erneut
versandt.
Es muß berücksichtigt werden, dass für die Verwaltung
der Stoppuhren eine nicht unerhebliche Rechenzeit
notwendig ist. Beim Setzen einer Stoppuhr wird eine
Datenstruktur erzeugt, die angibt, wann die Stoppuhr
abläuft und was für diesen Fall zu unternehmen ist. In
einer Liste werden dann alle noch nicht abgelaufenen
Stoppuhren eingetragen. Diese Liste ist normalerweise
nach Ablaufzeitpunkten (timeouts) sortiert.
Beim Eintreffen einer Antwort vor Ablauf der Stoppuhr
muß die Liste entsprechend bereinigt werden. Da
normalerweise relativ wenig Stoppuhren ablaufen, ist
der meiste Aufwand zur Pflege der Liste umsonst.
aktuelle Zeit
14200
14205
Prozeß 3
.
14212
Prozeß 2
.
14216
Prozeß 0
0
Abbildung 9: Liste
Wie genau sind nun aber Stoppuhren? Meistens beruhen die Werte, auf die Stoppuhren
gesetzt werden, auf wilden Schätzungen. Ein schlechter Schätzwert beeinflußt ja auch nicht
die Korrektheit eines Protokolls, sondern „lediglich“ die Leistungsfähigkeit.
Wird der Wert der Stoppuhr zu kurz bemessen, sind unnütze Wiederholungen der Fall, wird
er andererseits zu lang bemessen, wird die Wartezeit unnötig verlängert, wenn ein Paket
verloren gegangen ist.
aktuelle Zeit
14200
Prozeßtabelle
0
14216
1
0
2
14212
3
14205
Abbildung 10: Prozeßtabelle
Seminar Verteilte Systeme
In Anbetracht der relativ aufwendigen Pflege der Liste
stellt sich die Frage, ob es nicht eine effizientere
Möglichkeit der Verwaltung von Stoppuhren gibt. Hierbei
besteht die Möglichkeit, eine Prozeßtabelle zu verwalten,
in der sich alle benötigten Informationen befinden. In
dieser Tabelle existiert für jeden relevanten Prozeß ein
Feld, das die benötigten Informationen enthält. Eine
Stoppuhr wird angehalten, in dem der Eintrag eines
Prozesses auf Null gesetzt wird. Mit nur wenigen
Anweisungen ist es nun möglich, eine Stoppuhr zu setzen
oder anzuhalten.
Um einen reibungslosen Ablauf zu gewährleisten, wird
seitens des Kerns die Prozesstabelle regelmäßig überprüft,
indem die Ablaufzeitpunkte mit der aktuellen Zeit
verglichen werden.
Hierbei kommen sogenannte Kehralgorithmen zum
Einsatz, die die periodische und sequentielle Überprüfung
einer Tabelle gewährleisten.
Daniel Klein
entfernter Unterprogrammaufruf
21
5.6. Problemkreise
Dieser Unterpunkt unterteilt sich in drei Abschnitte:
a) Probleme mit globalen Variablen
b) Problem mit einem schwachen Typkonzept
c) Problem der Client – Client Schnittstelle
globale Variablen
In einem Einprozessorsystem sind globale Variablen zulässig. Beispielsweise stellt UNIX die
globale Variable errno zu Verfügung. Schlägt ein Systemaufruf fehl, enthält errno einen
Fehlerstatus, aus dem das Problem erkennbar ist.
Betrachten wir nun zwei Unterprogramme, die direkt auf errno zugreifen. Nehmen wir weiter
an, dass eines der Unterprogramme lokal und das Andere entfernt ausgeführt wird.
Da den Übersetzern nicht bekannt ist, welche Variablen und welche Unterprogramme sich wo
befinden, wird eines der beiden Unterprogramme nicht korrekt auf errno zugreifen.
Dadurch, dass es nicht möglich ist, den Zugriff von lokalen Unterprogrammen auf entfernte
globale Variablen zu gewährleisten, wird das geforderte Prinzip der Transparenz verletzt, das
unter anderem besagt, dass sich Programme aufgrund eines RPC nicht anders verhalten
sollten.
Problem mit einem schwachen Typkonzept
Ein weiteres Problem gibt es mit Programmiersprachen, die ein schwaches Typkonzept
verwenden (z.B. C). Bei einem strengen Typkonzept (z.B. Pascal) kennt der Übersetzer und
damit die Stub –Routione alle notwendigen Informationen über die Parameter. Mit diesem
Wissen kann die Stub - Routine die Parameter problemlos verpacken.
Verwendet man aber C als Programmiersprache, ist es zum Beispiel zulässig, ein
Unterprogramm zu schreiben, das das Vektorprodukt zweier Vektoren berechnet, ohne
anzugeben, wie groß die Vektoren sind. Da beide Vektoren durch einen Wert abgeschlossen
werden, der nur dem aufrufenden und dem aufgerufenen Unterprogramm bekannt ist, kennt
der Client – Stub nicht die Größe der Parameter und kann sie damit nicht einpacken.
Ähnliche Probleme treten auf, wenn ein Zeiger auf einen komplexen Graphen übergeben
werden soll. In einem RPC gibt es im Gegensatz zu einem Einprozessorsystem keine
Möglichkeit, auf den gesamten Graphen zuzugreifen.
Problem der Client – Client Schnittstelle
Betrachten wir nun einmal das folgende UNIX-Pipeline
grep rat <f5 | sort >f6
Hierbei stellen sowohl grep als auch sort Clients für die Standardeingabe und die
Standardausgabe dar. Wie aber interagieren die beide Programme. Ist nun sort ein Client der
Daten vom Server grep anfordert, oder verhält es sich genau umgekehrt?
Obwohl nun beide Programme als Client programmiert sind, muß einer von beiden ein Server
sein. Hier erkennt man die Grenze des Client-Server-Modells, da es für derartige Fälle einfach
nicht geeignet ist.
Das Problem tritt im allgemeinen bei UNIX-Pipelines der Form
p1 <f1 | p2 | p3 > f2 auf.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
22
Als Lösungsansatz besteht die Möglichkeit, die gesamte Pipeline Leser – gesteuert (readdriven) aufzubauen. Folgende Abbildung veranschaulicht diesen Ansatz:
Leseanfrage
Datei
Server
Leseanfrage
Daten
p3
p2
p1
?
Leseanfrage
Leseanfrage
Daten
Daten
Abbildung 11: Leser- gesteuerter Ansatz
Sowohl p1, p2 als auch p3 verhalten sich als Client, wobei p1 Leseanfragen an den DateiServer, p2 Leseanfragen an p1 und p3 Leseanfragen an p2 sendet.
Das Problem des Ansatzes besteht aber darin, dass sich der Datei-Server nicht als Client
verhält und eine Leseanfrage an p3 sendet, um die abschließende Ausgabe aufzusammeln.
Somit ist der Ansatz nicht brauchbar.
Eine weitere mögliche Vorgehensweise ist der Schreiber-gesteuerte Ansatz (write-driven)
der in nachfolgender Abbildung dargestellt ist.
?
Schreibanfrage
Schreibanfrage
p1
OK
p2
Schreibanfrage
Schreibanfrage
Datei
Server
p3
OK
OK
Abbildung 12: Schreiber- gesteuerter Ansatz
Auch hierbei verhalten sich p1,p2 und p3 als Client wobei p1 an p2, p2 an p3 und p3 an den
Datei – Server schreibt. Es fehlt aber ein Client, der p1 auffordert, die Eingabedatei
entgegenzunehmen.
6. Zusammenfassung
Trotz gewisser Fehlermöglichkeiten, hat sich das RPC zu einem weitverbreiteten Konzept
entwickelt, das vielen Betriebssystemen zugrunde liegt. Nach wie vor sind die verteilten
Systeme mit all ihren Problemen ein aktueller Gegenstand der Forschung.
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
23
7. Literaturverzeichnis
[1] Tanenbaum, A.S., Moderne Betriebssysteme, Hanser, Muenchen Wien, 1995
[2] Coulouris, G., Dollimore, J., Kindberg, T., Distributed Systems, Addison-Wesley,
Wokingham, 1995
Seminar Verteilte Systeme
Daniel Klein
entfernter Unterprogrammaufruf
24