pdf

Transcription

pdf
Literatur
[9-1]
https://de.wikipedia.org/wiki/Stapelspeicher
[9-2]
https://de.wikipedia.org/wiki/Reflexion_(Programmierung)
[9-3]
http://openbook.rheinwerkverlag.de/javainsel9/javainsel_25_001.htm
[9-4]
http://openbook.rheinwerkverlag.de/javainsel9/javainsel_11_002.htm
[9-5]
http://www-inst.eecs.berkeley.edu/~cs162/sp06/hand-outs/p149presser-linker-loader.pdf
[9-6]
https://de.wikipedia.org/wiki/Linker_(Computerprogramm)
AVS – SS 2016 - Teil 9/RPC II
2
Übersicht
•
•
•
•
•
Stubs und Marshalling
Stackaufbau eines Programms
Dynamische Aufrufe: Call
Reflexion
Laden und Binden
3
AVS – SS 2016 - Teil 9/RPC II
Realisierung der RPCs
• Der Transfer der Kontrolle wird auf den Transfer von Daten
zurück geführt.
• Die Realisierung der Umsetzung der Kontrolle nach Daten und
umgekehrt erfolgt in einem Stub:
• Stub = Realisierung der Schnittstelle zum Netzwerk mit der
Aufgabe der Umsetzung von Remote Procedure Calls
Object1
Object2
Datenfluss
Call, Kontrollfluss
Stub
BS
AVS – SS 2016 - Teil 9/RPC II
RPC-Protokoll
Netz
Stub
BS
4
Aufgaben des Stubs
• Serialisieren = Konvertieren der internen Repräsentation von
Daten in ein mit allen Kommunikationspartnern abgesprochenes
Format, meist ASCII.
• Deserialisieren = Umgekehrter Vorgang des Serialisierens und
damit Wiederherstellung der semantisch gleichen Repräsentation
• Bitte beachten: Semantisch gleich bedeutet nicht unbedingt
dieselbe Bitkombination
• Realisieren des Protokolls: Reihenfolge, Formate von PDU
• Feststellen und Behandeln von Fehlern
• Anpassung unterschiedlicher unterliegenden Schnittstellen, d.h.
Stubs sind eigentlich Wrapper
5
AVS – SS 2016 - Teil 9/RPC II
Marshalling als Teilaufgabe der Stubs
• Marshalling = Konvertieren der internen Daten in ein externes
Format und umgekehrt
• Externe Data Repräsentation = XDR = Struktur und Format der
Daten in der Kommunikation zwischen Stubs
Object1
Object2
Lokale
Daten
Repräsen
tation
Lokale
Daten
Repräsen
tation
Stub
RPC-Protokoll
Stub
XDR
BS
AVS – SS 2016 - Teil 9/RPC II
Netz
BS
BS = Betriebssystem
mit dem Transportsystem
6
Serialisieren in Java und PHP
Java
• In Java geht die Serialisierung über das Interface java.io.serializable sowie
über die I/O-Stream-Klassen.
• Siehe
–
–
–
–
–
http://docs.oracle.com/javase/6/docs/platform/serialization/spec/protocol.html
https://docs.oracle.com/javase/tutorial/jndi/objects/serial.html
http://openbook.rheinwerk-verlag.de/javainsel9/javainsel_17_010.htm
http://www.tutorialspoint.com/java/java_serialization.htm
http://www.vogella.com/tutorials/JavaSerialization/article.html
PHP
• In PHP erfolgt dies ganz einfach durch den Aufruf von serialize() und
unserialize().
• Siehe
– http://php.net/manual/de/function.serialize.php
– http://php.net/manual/de/language.oop5.serialization.php
– http://serialize.onlinephpfunctions.com/
7
AVS – SS 2016 - Teil 9/RPC II
Serialisierung I
Datentyp
Beispiel
int (8,16,32,64 bit)
ASCII-Hex
subrange-int
ASCII-Hex
float, double, extended
Dezimal
char (1 Byte)
ASCII-Hex
char (UNICODE)
ASCII-Hex
String
ASCII-Hex
boolean
Dezimal
Aber wie sieht es aus mit BigInt?
Aber wie sieht es aus mit BigDecimal?
BCD-Arithmetik?
Aber wie sieht es aus mit Enumeration Types?
Z.B. mit Symbol(String)+Wert(int)
AVS – SS 2016 - Teil 9/RPC II
8
Serialisierung II – Pointer/Adressen
Drei Strategien
1) Einfach verbieten
Nachteil: wird häufig gebraucht, z.B. Output-Parameter
read(…)
2) Alle referenzierten Objekte müssen mit serialisiert werden.
– Aber wie weit muss das gehen?
Liste von Objekten: die ganze Liste?
3) Alle referenzierten Objekte müssen lokal verfügbar sein, d.h. die
Objekte müssen zwischen Knoten verschiebbar sein und zum
Ort, an dem die Routine arbeitet, hin wandern.
Alle drei Strategien gibt es in der Praxis.
Alle drei Strategien sind nicht schön.
AVS – SS 2016 - Teil 9/RPC II
9
Was muss serialisiert werden?
• Die Liste der Parameter
• Die (Liste der) Resultate
• Exception-Information
• Hier gibt es diverse Probleme, u.a.
– Wie werden Referenzen (Pointer) behandelt?
– Werden Zeichenketten (Strings) umkodiert?
– Werden Objekte übertragen, die ja Code enthalten?
• Wir gehen hier vereinfachend davon aus, dass
– nur primitive Daten übertragen werden: int, float, char,
– dass nur Maschinen desselben Typs betroffen sind,
– und dass es keine Seiteneffekte gibt.
AVS – SS 2016 - Teil 9/RPC II
10
Allgemeine Realisierung des Stubs auf der Client-Seite
(1) PROC call(Name,ParamList)
(2)
data:= serialize(ParamList)
(3)
msg:= createMSG(Name,data)
(4)
send(Server,msg)
(5)
result:= receive()
(6)
IF result=error THEN
(7)
throw Exception
(8)
ELSE
(9)
data:= unserialize(result)
(10)
return(data)
(11)
FI
// Client Beispiel
...
avg:= call("avg",1,2,3)
...
print avg
// avg = Durchschnitt
// eine 2 wird ausgegeben
• Das ist eine allgemeine Call-Routine des klassischen synchronen
RPC.
• Im Fehlerfalle wird eine Exception geworfen.
• Dies hier stellt nur das Prinzip dar.
AVS – SS 2016 - Teil 9/RPC II
11
Bemerkungen
• In Zeile (3) wird eine Nachricht mit den serialisierten
Informationen erstellt.
• Dieses Datenpaket ist eine Struktur mit zwei Werten:
– Name der Routine
– Parameterliste
• In Zeile (4) wird das Paket gesendet und in (5) die Antwort
empfangen.
• Zeilen (6) bis (8) zeigen die Fehlerbehandlung, d.h. das
Weiterreichen einer Exception.
• Bitte beachten Sie, dass der Pseudocode sich auf eine allgemeine
Routine bezieht, die alle möglichen Routinen entfernt aufrufen
vermag.
Der Name der aufzurufenden Routine wird als String übergeben.
AVS – SS 2016 - Teil 9/RPC II
12
Realisierung des Stubs auf der Server-Seite I
(1) PROC processCalls
Das ist eine Art
(2)
DO
Hauptprogramm
(3)
msg:= receive()
(4)
proc:= msg.Name
(5)
IF exists(proc) THEN
(6)
data:= unserialize(msg.ParamList)
(7)
TRY
(8)
result:= call(proc,data) • Das ist die Callee(9)
CATCH
Routine des synchronen
(10)
result:= Error
RPC.
(11)
ELSE
• Auf der Server-Seite
(12)
result:= Error
befindet sich eine
(13)
FI
Endlos-Schleife, die alle
(14)
data:= serialize(result)
Requests abarbeitet und
(15)
reply(data)
sonst nichts tut.
(16)
OD
AVS – SS 2016 - Teil 9/RPC II
13
Bemerkungen
• In Zeile (4) wird der Name der Routine extrahiert.
• In (5) wird anhand einer Tabelle geprüft, ob die betreffende Routine
geladen ist bzw. existiert.
• Der Aufruf in (8) muss in einem TRY-Block erfolgen, da die
aufgerufene Routine eine Exception werfen kann, die in in (10)
weitergereicht wird.
• In (15) wird entweder das Ergebnis zurück geliefert oder eine
Exception bzw. eine Fehlernummer.
• Bitte beachten Sie, dass der Pseudocode sich auf eine allgemeine
Call-Routine bezieht, die alle möglichen Routinen lokal aufrufen
vermag.
Der Name der aufzurufenden Routine wird als String übergeben.
AVS – SS 2016 - Teil 9/RPC II
14
Generieren des Stubs I
• Die skizzierten Stubs sind genereller Natur: sie werden ein
einziges Mal programmiert und werden für alle Routinen benutzt.
Das geht nur mit dynamisch parametrisierten Klassen oder in
dynamischen Sprachen, wie z.B. PHP.
• Alternativ werden Stubs per Generator (IDL-Compiler)
entsprechend der Signatur der Routinen individuell generiert.
Dieser Code kann dann nur für diese eine Routine verwendet
werden.
• Beispiel für Java:
AVS – SS 2016 - Teil 9/RPC II
15
Generieren des Stubs II
• Bei der Verwendung eines IDL-Compilers wird auf der
Klientenseite für jede Art von Routine ein eigener Stub generiert;
dieser hat genau die in der IDL spezifizierten Parameter samt
Typen.
• Auf der Seite des Servers erfolgt dies genauso, aber der Aufruf
muss dann in einem großen Switch erfolgen bzw. im Falle der
Objekt-Orientierung durch Polymorphie (intern durch Zeiger und
Tabellen).
AVS – SS 2016 - Teil 9/RPC II
16
Realisierung des Stubs auf der Server-Seite II
...
(7)
(8)
(9)
(10)
...
TRY
result:= call(proc,data)
CATCH
result:= Error
Dieser Teil muss vom IDL-Compiler
generiert werden...
...
(7)
(7a)
(7b)
(7c)
(7d)
(7e)
(9)
(10)
...
TRY
SWITCH proc
CASE „Routine1“: Routine1(data.p1,data.p2...);
CASE „Routine2“: Routine2(data.p1,data.p2...);
...
END_SWITCH
Die einzelnen Parameter müssen
CATCH
extrahiert und angegeben werden.
result:= Error
AVS – SS 2016 - Teil 9/RPC II
17
Bemerkungen
• Dieses Verfahren hat folgende Nachteile:
– Kein dynamisches Laden möglich
– Starre und feste Anzahl von Routinen
• Es fehlt die Flexibilität der Objekt-Orientierten Ansätze bzw. der
dynamischen Typen von Skript-Sprachen.
• So implementiert, können nur die im switch explizit berücksichtigten
Routinen aufgerufen werden.
• Was aber wenn ein allgemeiner Stub, der alle möglichen Routinen
aufrufen können soll, benutzt werden soll?
• Eine Lösung dazu wird nun präsentiert.
AVS – SS 2016 - Teil 9/RPC II
18
Flexibler Aufruf von Routinen I
• Über eine Nachricht
wird der Name einer
Routine dem Server
übergeben.
• Der Server muss nun
diese Routine
aufrufen, d.h.
der Aufruf muss schon
programmiert sein.
• Es geht also um das
call:
PROC processCalls
DO
pdu:= receive()
proc:= pdu.Name
IF exists(proc) THEN
data:= unserialize(pdu.ParamList)
TRY
result:= call(proc,data)
CATCH
result:= Error
ELSE
result:= Error
FI
data:= serialize(result)
reply(data)
OD
19
AVS – SS 2016 - Teil 9/RPC II
Aufbau des Datenbereichs eines Prozesses
Daten-Segment
Stack
SP ->
Belegt
durch
Stack
Richtung des
Wachsens des
Stacks
Frei
Heap
Belegt
durch
Heap
SP = Stackpointer
Register innerhalb der CPU
AVS – SS 2016 - Teil 9/RPC II
20
Aufruf einer Prozedur
Situation direkt nach Aufruf einer Routine
Name(Parameter1,Parameter2,…,ParameterN)
Mechanismus des Aufrufs
einer Subroutine
Stack
Parameter N
...
Parameter 2
Parameter 1
SP -> Return-Adr.
AVS – SS 2016 - Teil 9/RPC II
21
Call(Name,Parameterliste) I - Erinnerung
Das ist die Seite des Servers
PROC processCalls
DO
pdu:= receive()
proc:= pdu.Name
IF exists(proc) THEN
Das ist der Aufruf der
data:= unserialize(pdu.ParamList)
Routine im Server
TRY
result:= call(proc,data)
CATCH
result:= Error
ELSE
Hier wird eine Routine
result:= Error
aufgerufen, dessen Name
FI
in einem String vorliegt.
data:= serialize(result)
reply(data)
OD
AVS – SS 2016 - Teil 9/RPC II
22
Was macht call(Name,Parameterliste)? I
Stack
Parameter N
...
Situation direkt nach Aufruf
der Routine Namens call:
result:= call(proc,data)
Parameter 2
result
Parameter 1
^Parameter
^Name
SP -> Return-Adr.
Situation direkt nach
dem Aufruf von call()
Name(String)
• Auf dem Stack ist der Platz für das Resultat reserviert.
• Der 1. Parameter (proc) ist die Adresse eines Strings.
• Der 2. (data) ist die Adresse einer Tabelle mit den
Parametern der Routine, dessen Namen als String
vorliegt.
• ^Name bedeutet: Adresse-von(Name)
23
AVS – SS 2016 - Teil 9/RPC II
Was macht call(Name,Parameterliste)? II
Parameter N
Stack
...
result
Parameter 2
^Parameter
Parameter 1
^Name
Return-Adr.
Parameter N
push(Array an Param-Addr)
...
Parameter 2
SP -> Parameter 1
AVS – SS 2016 - Teil 9/RPC II
Der 1. Schritt in der Routine call
besteht darin, die Liste der
Parameter (2. Parameter) auf
den Stack zu kopieren.
Das erfolgt über die push-Operation.
24
Was macht call(Name,Parameterliste)? III
Stack
result
^Parameter
Dieser Block wird
nach oben kopiert
^Name
Return-Adr.
Parameter N
Der 2. Schritt in der Routine call
besteht darin, die Rückkehradresse
auf den Stack zu bringen.
Der 3. Schritt besteht darin, den
unteren Bereich so weit nach oben
zu kopieren, dass er unterhalb des
Resultats steht.
Damit wird der graue Bereich
überschrieben.
...
Parameter 2
push(Return-Adresse)
Parameter 1
SP -> Return-Adr.
25
AVS – SS 2016 - Teil 9/RPC II
Was macht call(Name,Parameterliste)? IV
Stack
result
Parameter N
...
Parameter 2
Parameter 1
SP -> Return-Adr.
Situation des
Stacks direkt
vor goto
AVS – SS 2016 - Teil 9/RPC II
push(Array an Param-Addr)
push(Return-Adresse)
Bestimmen der
Adresse der
Routine mit dem
Namen "Name"
Routine:= reflection(Name)
copy(Block,Stack)
pop(unterer Block)
goto Routine
Sprung direkt in
die Routine
Durch dieses Kopieren ist genau die Struktur
auf dem Stack entstanden, die erforderlich ist,
um die Routine direkt aufzurufen.
26
Was macht call(Name,Parameterliste)? V
PROC call(Name,ParameterListe)
push(Array an Param-Addr)
push(Return-Adresse)
Routine:= reflection(Name)
copy(Block,Stack)
pop(unterer Block)
goto Routine
• Es wird der aktuelle Aufruf von call auf dem Stack beseitigt.
• Stattdessen wird anhand der Parameter ein anderer Aufruf
simuliert.
• Die Rückkehradresse dieses neuen Aufrufs ist die alte zum
Aufrufer der call-Routine.
• Das Ganze lässt sich nur in Assembler realisieren.
AVS – SS 2016 - Teil 9/RPC II
27
Was macht reflection(Name)? I
• Reflexion = Möglichkeit, die eigenen Programmstrukturen zur
Laufzeit selbst zu analysieren und zu benutzen
• Introspektion = Selbstanalyse = Reflexion
• Die Routine reflection(Name) durchsucht die Symboltabellen zur
Laufzeit nach dem Namen der Routine und liefert deren Adresse
bzw. Null.
AVS – SS 2016 - Teil 9/RPC II
28
Was macht reflection(Name)? II
Hohe Adressen
Name
Adresse
"blabla"
^blabla
"abc"
^abc
...
...
• Die Symboltabellen beinhalten die
Zuordnung zwischen dem Namen zur
Compile-Zeit und der Adresse im Code.
• Diese Tabellen liegen zur Laufzeit im
RAM vor und werden bei der Reflexion
durchsucht.
Code von
blabla()
Code von
abc()
Niedrige Adressen
Code
AVS – SS 2016 - Teil 9/RPC II
29
Was macht reflection(Name)? III
• reflection(Name) durchsucht also die Tabelle mit allen geladenen
Routinen.
• Wenn der gesuchte Eintrag gefunden ist, wird die Start-Adresse
zurück geliefert.
• Diese Startadresse wird dann als Wert beim goto benutzt.
PROC call(Name,ParameterListe)
push(Array an Param-Addr)
push(Return-Adresse)
Routine:= reflection(Name)
copy(Block,Stack)
pop(unterer Block)
goto Routine
AVS – SS 2016 - Teil 9/RPC II
30
Woher kommen die Symboltabellen?
Syntax Analyse (Parser)
Compiler
Optimierung
Compiler
Codegenerierung
Compiler
Assemblieren
Assembler
Dies ist der Übersetzungsprozess
einer höheren Programmiersprache
Hier entstehen die Symboltabellen
Zusammensetzen
Laden
Linker/Linkage Editor
Lader/Loader
Ausführung
AVS – SS 2016 - Teil 9/RPC II
31
Struktur der Symboltabellen
• Es gibt zwei Tabellen(teile):
– Liste der Dinge, die definiert sind (export)
– Liste der Dinge, die benötigt werden (import)
• Dinge sind
– Variablen
– Klassen/Objekte
– Routinen
• Zu jedem der Dinge steht in der Tabelle
– Name als String
– Wert/Adresse
– Typ (Data, Code)
AVS – SS 2016 - Teil 9/RPC II
32
Das Binden von Programmen I
Die Symboltabellen befinden sich in den
Objekt-Dateien vor dem Binden.
AVS – SS 2016 - Teil 9/RPC II
33
Das Binden von Programmen II
Übersetzte Module
AVS – SS 2016 - Teil 9/RPC II
34
Das Binden von Programmen III
• Traditionell wird das Binden von Programmen vor der Laufzeit
gemacht und die Tabellen für die Symbole anschließend gelöscht.
• Für die Reflexion bleiben die Tabellen erhalten und werden in den
RAM für die Reflection-API eingelesen.
• Das Ganze geht aber auch zur Laufzeit:
– Der Lader und der Linker sind als parallel laufende Threads realisiert.
– Der Lader liest die externe Datei ein.
– Der Linker aktualisiert die Tabellen und bestimmt, welche Datei aus
welcher Bibliothek geladen werden soll.
– (So in etwa ist es in Java realisiert: Classloader)
• Mit diesem Mechanismus ist ein dynamisches (Nach-)Laden von
beliebigen Routinen möglich.
AVS – SS 2016 - Teil 9/RPC II
35
Bemerkungen
• Diese Überlegungen sind nicht theoretischer Natur.
• Der dynamisch generierte Aufruf anhand des Namens als String
wird so in Skriptsprachen realisiert.
• Das dynamische Nachladen (und Entladen) erfolgt so in Java.
• In Objekt-Orientierten Sprachen erfolgt der Aufruf von Methoden
anhand von Deskriptoren mit Zeigern, was erst die Polymorphie
erlaubt zu realisieren (ist also noch etwas komplizierter).
AVS – SS 2016 - Teil 9/RPC II
36
Wie geht das mit dem call in PHP?
Aufruf
$routine= "blabla";
$result= $routine($params);
Deklaration
function blabla($params) {
...
... $params[0]...
...
return(...);
}
• In PHP kann über eine Variablen mit dem Inhalt des Namens der
Routine indirekt eine Routine aufgerufen werden.
• Die Konstruktion mit der Parameterliste – wie oben - setzt ja
Assembler voraus, was hier nicht geht; daher wird die
Parameterliste als Array übergeben.
• In der Deklaration der aufzurufenden Routine muss dies
berücksichtigt werden.
AVS – SS 2016 - Teil 9/RPC II
37
Und wie in Java? I
Object call(String cname, String mname, double param)
throws ClassNotFoundException {
Object result= null;
Class className= Class.forName(cname);
Method[] methodNames= className.getDeclaredMethods();
for (Method method: methodNames) {
if(method.getName().equal(mname){
result= callIt(method,param);
}
}
return(result);
}
• Dies geht nur über die Reflection-API.
• Dies ist ein Beispiel für den vereinfachten Sonderfall, dass nur
ein Parameter vom Typ double übergeben werden soll.
AVS – SS 2016 - Teil 9/RPC II
38
Und wie in Java? II
Object callIt(Method method, double value) {
Object result= null;
Class[] params= method.getParameterTypes();
String methodName= method.getName();
if ((params.length==1) && (params[0]==Double.TYPE)) {
try {
result= method.invoke(null,value);
} catch (IllegalAccessException |
InvocationTargetException ex) {
System.out.println(ex);
}
}
return(result);
}
• Hier ist die fehlende callIt-Routine.
• Die Java-Version macht weniger als die PHP-Version, ist dafür aber
länger und komplizierter.
39
AVS – SS 2016 - Teil 9/RPC II
Wie geht das Laden in PHP?
Routinen
function load($name) {
require_once($name);
}
Klassen
function __autoload($Klasse) {
include("$Klasse.php");
}
Dass dies so überschaubar ist, wird aber durch Performanz-Verluste erkauft:
Der Compiler, der Lader und der Binder sind immer vorhanden und
laufen permanent mit.
AVS – SS 2016 - Teil 9/RPC II
40
Zusammenfassung
• Mit dem vorgestellten Verfahren kann genau der PHPMechanismus realisiert werden, der oben vorgestellt wurde:
entspricht routine= "blabla";
$routine= "blabla";
result= call(routine,params);
$result= $routine($params);
und im Prinzip arbeitet auch so der PHP-Prozessor....
AVS – SS 2016 - Teil 9/RPC II
41
Nach dieser Anstrengung etwas Entspannung....
AVS – SS 2016 - Teil 9/RPC II
42

Documents pareils