Warum müssen VMs „Stapelmaschinen“ oder „Registriermaschinen“ usw. sein?

48

(Dies ist eine extrem neue Frage).

Ich habe ein wenig über virtuelle Maschinen gelernt.

Es stellte sich heraus, dass viele von ihnen sehr ähnlich wie physische oder theoretische Computer konzipiert sind.

Ich habe gelesen, dass die JVM zum Beispiel eine "Stapelmaschine" ist. Das bedeutet (und korrigiert mich, wenn ich mich irre), dass es den gesamten 'temporären Speicher' auf einem Stapel speichert und Operationen auf diesem Stapel für alle seine Opcodes ausführt.

Zum Beispiel wird der Quellcode 2 + 3in Bytecode übersetzt, ähnlich wie:

push 2
push 3
add

Meine Frage lautet:

JVMs werden wahrscheinlich mit C / C ++ und dergleichen geschrieben. Wenn ja, warum führt die JVM den folgenden C-Code nicht aus: 2 + 3..? Ich meine, warum braucht es einen Stack oder in anderen VMs "Registern" - wie in einem physischen Computer?

Die zugrunde liegende physische CPU übernimmt dies alles. Warum führen VM-Writer den interpretierten Bytecode nicht einfach mit "üblichen" Anweisungen in der Sprache aus, mit der die VM programmiert ist?

Warum müssen VMs Hardware emulieren, wenn die eigentliche Hardware dies bereits für uns erledigt?

Wieder sehr neue Fragen. Danke für Ihre Hilfe

Aviv Cohn
quelle
5
Haben Sie überlegt, worauf nicht virtuelle Maschinen basieren?
5
@MichaelT Du meinst physische Maschinen?
Aviv Cohn
Natürlich sind die meisten Javascript-VMs keine Stack-Maschinen oder Register-Maschinen - V8 / IonMonkey / Chakra / etc. sind VMs, die Javascript implementieren. Eine "VM" ist lediglich ein Interpreter oder JIT-Compiler, der jede vom Designer gewünschte Sprache implementieren kann.
Billy ONeal
@BillyONeal Wenn ich zum Beispiel eine VM für eine Sprache schreibe und diese in C schreibe: Die VM analysiert die Bytecode-Zeile "print" hi und führt Folgendes aus: Wird printf("hi");dies als VM betrachtet? Es gibt keine "Stapel" oder "Register" und so weiter.
Aviv Cohn
@Prog: Ja, das ist richtig.
Billy ONeal

Antworten:

51

Eine Maschine, ob virtuell oder nicht, benötigt ein Berechnungsmodell, das beschreibt, wie die Berechnung auf ihr ausgeführt wird. Per Definition implementiert es, sobald es berechnet wird, ein Rechenmodell. Die Frage ist dann: Welches Modell sollen wir für unsere VM wählen? Physische Maschinen sind durch die Möglichkeiten eingeschränkt, die Hardware effektiv und effizient bietet. Wie Sie jedoch bemerken, gibt es für virtuelle Maschinen keine derartigen Einschränkungen. Sie werden in Software unter Verwendung beliebig hoher Sprachen definiert.

Tatsächlich gibt es virtuelle Maschinen auf hohem Niveau, wie Sie beschreiben. Sie werden Programmiersprachen genannt . Der C-Standard zum Beispiel widmet den Großteil seiner Seiten der Definition eines Modells für die sogenannte "C-abstrakte Maschine", das beschreibt, wie sich C-Programme verhalten, und erweitert (als-ob-Regel), wie ein konformer C-Compiler (oder -Interpreter) arbeitet. sollte Verhalten.

Natürlich nennen wir das normalerweise keine virtuelle Maschine. Unter einer VM versteht man normalerweise eine niedrigere Ebene, die näher an der Hardware liegt, nicht direkt programmiert werden soll und so konzipiert ist, dass sie effizient ausgeführt werden kann. Diese Auswahlverzerrung bedeutet, dass etwas, das zusammensetzbaren Code auf hoher Ebene akzeptiert (wie das, was Sie beschreiben), nicht als VM angesehen wird, da Code auf hoher Ebene ausgeführt wird.

Aber um auf den Punkt zu kommen, hier sind einige Gründe, eine VM (wie in, etwas, auf das ein Bytecode-Compiler abzielt) auf Registerbasis oder dergleichen zu machen. Stapel- und Registriermaschinen sind extrem einfach. Es gibt eine Abfolge von Anweisungen, einen Status und eine Semantik für jede Anweisung (eine Funktion Status -> Status). Keine komplexen Baumreduzierungen, keine Operatorpräzision. Das Parsen, Analysieren und Ausführen ist sehr einfach, da es sich um eine minimale Sprache handelt (syntaktischer Zucker wird wegkompiliert) und so konzipiert ist, dass sie eher maschinell als vom Menschen gelesen werden kann.

Im Gegensatz dazu ist das Parsen selbst der einfachsten C-ähnlichen Sprachen ziemlich schwierig, und das Ausführen erfordert nicht-lokale Analysen wie das Überprüfen und Weitergeben von Typen, das Auflösen von Überladungen, das Verwalten einer Symboltabelle, das Auflösen von Zeichenfolgen- IDs und das Umwandeln von linearem Text in einen prioritätsgesteuerten AST , und so weiter. Es baut auf Konzepten auf, die für den Menschen selbstverständlich sind, die jedoch von Maschinen akribisch rückentwickelt werden müssen.

JVM-Bytecode wird beispielsweise von ausgegeben javac. Es muss praktisch nie von Menschen gelesen oder geschrieben werden, daher ist es normal, es auf den Verbrauch durch Maschinen abzustimmen. Wenn Sie es für den Menschen optimiert haben, würde die JVM bei jedem Start den Code lesen, analysieren und dann in eine Zwischendarstellung konvertieren, die sowieso so einem vereinfachten Maschinenmodell ähnelt . Könnte auch den mittleren Mann ausschneiden.


quelle
Sie sagen also, dass das Kompilieren aller Anweisungen auf dem Stapel (dh System.out.println("hi");Kompilieren einer Anweisung auf einem Stapel, int a = 7Kompilieren einer Anweisung auf dem Stapel usw.) die Ausführung des Programms einfacher und effizienter macht?
Aviv Cohn
2
@Prog Grundsätzlich ja. Aber nicht nur die Ausführung, sondern auch die Analyse. Alles was programmatisch gemacht wird.
Trotzdem verstehe ich nicht, warum zu 2 + 3kompiliert wird push 2 push 3 add. Der addSchritt am Ende wird ohnehin von der JVM ausgeführt, indem der C-Code ausgeführt wird 2 + 3. Es gibt keine andere Möglichkeit für die Programmierer der JVM, dies zu tun. Warum kompilieren Sie es nicht 2 + 3und lassen Sie die JVM sofort den C-Code ausführen 2 + 3(vorausgesetzt, er ist in C geschrieben)?
Aviv Cohn
@Prog Der JVM-Autor kann nicht einfach 2 + 3in den JVM-Quellcode schreiben, da die JVM mit jedem Programm zusammenarbeiten muss, das Operationen in beliebiger Reihenfolge ausführt . Die Erstellung von C-Quellcode und die Verlagerung auf eine C-Implementierung führen zu demselben Problem in der C-Implementierung (und können nicht einfach und geschweige denn effizient durchgeführt werden). Es muss eine Datenstruktur geben, die das Programm beschreibt, damit es interpretiert und JIT-kompiliert werden kann, und "von Menschen lesbarer Quellcode" ist aus den oben genannten Gründen eine schreckliche Wahl der Datenstruktur.
7
@Prog Sie scheinen sich zu sehr auf den speziellen Fall von 2 + 3 zu konzentrieren. Was ist mit a + b? Die hinzuzufügenden Werte stammen dann nicht von i.argument{1,2}, sondern werden aus lokalen Variablen geladen. Was ist frobnicate(x[i]) + (Foo.bar() * 2)? Bei Verwendung dieses Entwurfs gibt es nur eine addOperation (für int) und sie funktioniert unabhängig davon, wie die Addenden berechnet wurden. Außerdem wäre eine Anweisung, die nur ganzzahlige Literale hinzufügt , sinnlos: Das Ergebnis könnte genauso gut vorberechnet werden (dh stattdessen add(2,3)sollte es sein push(5)).
20

Diese Antwort konzentriert sich auf die JVM, gilt jedoch für jede VM.

Warum müssen VMs Hardware emulieren, wenn die eigentliche Hardware dies bereits für uns erledigt?

Dies ist nicht der Fall, macht die VM jedoch viel einfacher und portabler: Eine VM, die Hardware emuliert, kann dasselbe Rechenmodell wie jede andere Hardware-CPU verwenden.

Insbesondere die JVM wurde mit Blick auf die Portabilität entwickelt. Sie wurde sogar so entwickelt, dass sie sogar in Hardware implementiert werden kann (das ist heutzutage kaum zu glauben, aber der Ursprung von Java lag in der eingebetteten Welt - speziell bei Controllern für interaktives Fernsehen ).

Wenn Sie ein solches Ziel haben, ist es wünschenswert, dass die VM so nah wie möglich an einer physischen Maschine arbeitet, da die Übersetzung in tatsächlichen Maschinencode einfacher und damit schneller wird. Sobald Sie die Opcodes der VM haben, müssen Sie theoretisch nur noch die Opcodes der CPU übersetzen, auf der das Programm tatsächlich ausgeführt wird. In der Praxis ist das nicht ganz so einfach.

Ich meine, warum braucht es einen Stack oder in anderen VMs "Registern" - wie in einem physischen Computer?

Die Verwendung eines stapelbasierten virtuellen Maschinenmodells hat den Vorteil, dass es leicht auf Register- und Stapelmaschinen übertragen werden kann, während das Gegenteil nicht unbedingt der Fall ist. Eine auf Registern basierende VM müsste Annahmen über die Anzahl der Register, die Größe der Register usw. treffen. Bei einer Stapelmaschine sind solche Annahmen nicht erforderlich.

Die zugrunde liegende physische CPU übernimmt dies alles. Warum führen VM-Writer den interpretierten Bytecode nicht einfach mit "üblichen" Anweisungen in der Sprache aus, mit der die VM programmiert ist?

Nun, das ist, was solche VMs tun, sie interpretieren Bytecode. Sogar die JVM tut dies tatsächlich, zumindest bevor JIT (Just-in-Time) einsetzt: Sie interpretiert die Bytecodes und führt die Anweisungen in der Sprache aus, in der die JVM geschrieben wurde (normalerweise C oder C ++), aber es gibt sogar eine geschriebene in JavaScript, Doppio ). Beachten Sie jedoch, dass selbst solche Anweisungen von einem Compiler in Maschinencode übersetzt wurden und dem, was der Java-Compiler erzeugt, sehr ähnlich sehen - sie verwenden nämlich Register und den Stack, um ihre Arbeit auszuführen. Beachten Sie, dass die Verwendung von "interpretierten" und "kompilierten" Sprachen an dieser Stelle etwas unscharf wird.

miraculixx
quelle
Natürlich kann alles, was in Software implementiert werden kann, in Hardware implementiert werden. Auch zur Zeit die JVM (Hotspot) ist ein JIT - Compiler - es ist nicht die Aussagen in der Sprache wurde die JVM in geschrieben ausführen Wenn ja, Java funktionieren würde schrecklich und würde bei weitem nicht so rentabel eine Plattform sein , wie es heute ist. . (Hölle, die meisten Javascript-Implementierungen wären schneller)
Billy ONeal
2
@BillyONeal "Anstatt Methode für Methode zu kompilieren, führt die Java HotSpot-VM das Programm sofort mithilfe eines Interpreters aus und analysiert den Code während der Ausführung, um die kritischen Hotspots im Programm zu erkennen. Dann richtet sie die Aufmerksamkeit von a globaler Native-Code-Optimierer für die Hotspots ", zitiert von oracle.com/technetwork/java/whitepaper-135217.html#2 , Abschnitt" Hotspot- Erkennung "
miraculixx,
Ja. "Native Code Optimizer" == JIT-Kompilierung. Es gibt eine Interpreter-Phase für Code, die nicht "heiß" zu sein scheint, um zu vermeiden, dass selten genutzte Dinge mit JIT bearbeitet werden. Das heißt aber nicht, dass überhaupt kein JITing durchgeführt wird.
Billy ONeal
Danke für die Antwort. Was ich Ihrer Antwort entnommen habe, ist, dass der Grund für die Emulation von Hardware in der VM (auch bekannt als "Stacks" oder "Register" usw.) darin besteht, dass es einfach ist, den Bytecode oder den Quellcode später in den tatsächlichen Maschinencode von a zu kompilieren physische CPU. Aber abgesehen davon - gibt es etwas zu gewinnen, wenn Sie Hardware in einer VM emulieren? Ich verstehe immer noch nicht, warum jemand, der eine VM entwirft, in der Tat von Software spricht und dies als "Stapelmaschine" oder "Registriermaschine" usw. betrachtet. Vermisse ich etwas?
Aviv Cohn
@Prog Ok, Sie haben eine Programmiersprache, sagen Sie X. Wie werden Sie die Programme ausführen? Sie können entweder die Quelle interpretieren oder sie zu Maschinencode kompilieren oder sie in einen Zwischencode kompilieren. Jetzt haben Sie eine andere Programmiersprache, Y, und möchten sie mit X implementieren. Wenn beide Implementierungen Interpreter sind, wird der Interpreter von Y auf dem Interpreter von X ausgeführt, und dies ist sehr langsam.
18446744073709551615
11

Warum müssen VMs „Stapelmaschinen“ oder „Registriermaschinen“ usw. sein?

Sie nicht. Wenn Sie eine virtuelle Maschine benötigen, kann dies alles sein.

Die vorhandenen virtuellen Maschinen haben sich als Lösungen für die folgenden Situationen erwiesen: Eine wirklich brillante Idee ist mir eingefallen, ich habe eine neue Programmiersprache erfunden! Aber ich muss Code generieren. (Was für eine langweilige Aufgabe!) Aber ich möchte keinen i8086-Code generieren, weil er hässlich ist, und ich möchte keinen 68k-Code generieren, weil alle anderen Intel verwenden. Es gibt auch VAX, aber ich habe kein VAX, weder einen Computer noch ein VAX-Buch. Daher werde ich Code für einen Prozessor generieren, der physikalisch nicht existiert, und diesen Prozessor in Software implementieren. Die Spezifikation dieser VM wird ein Kapitel in meiner Arbeit bilden. Theoretisch wird es möglich sein, es in den nativen Code eines beliebigen Prozessors zu kompilieren, aber das werde ich nicht sein.

Auf der anderen Seite wird die Notation "2 + 3" wahrscheinlich in absehbarer Zeit nicht von VMs verwendet, da sie eine Menge Transformationen erfordert, bevor etwas ausgeführt werden kann.

18446744073709551615
quelle
Danke für die Antwort. Aus Ihrer Antwort ging hervor, dass die Motivation für den Entwurf einer VM, die physische CPUs emuliert, darin besteht, dass später Compiler implementiert werden können, die mit dem tatsächlichen Maschinencode kompiliert werden. Aber abgesehen davon - gibt es irgendwelche Vorteile beim Entwurf einer VM im Hinblick auf eine "Stapelmaschine" oder eine "Registermaschine" usw.?
Aviv Cohn
1
Register erfordern Registerzuweisungsalgorithmen, die sowohl Theorie als auch Debugging erfordern. Eine Stapelmaschine (insbesondere eine Maschine mit Nulloperanden) kann die Daten einfach auf den Stapel legen. OTOH, Hardware implementiert normalerweise eine begrenzte Anzahl von Registern anstelle eines Stapels mit variabler Größe. So sind Stacks für die Software einfacher, Register für die Hardware einfacher und daher wahrscheinlich etwas schneller.
18446744073709551615
-2

Um die eigentliche Frage zu beantworten, die gestellt wurde. Der Begriff "virtuelle MASCHINE" bedeutet, dass ALLE Software / Hardware simuliert / emuliert wird. Wenn Sie zur Ausführung der Anweisungen die zugrunde liegende Software / Hardware verwenden, verfügen Sie nicht über eine VM, sondern über einen Compiler / Interpreter.

Kyrelel
quelle
Ist das nur deine Meinung oder kannst du es irgendwie bestätigen?
gnat
@Kyrelel das ist nicht wahr. "ALL" Hardware wird in "System" oder "Full" VM emuliert. Nicht alle VMs sind voll. Beispielsweise wird die BSD-VM-Schicht "virtuelle Maschine" genannt, obwohl dort keine Hardware emuliert ist.
Netch
Ich denke nicht, dass die Frage notwendigerweise die Terminologie
Ryan,