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