Anstatt den Quellcode für das jeweilige Betriebssystem (auf das er abzielt) zu kompilieren, kompilieren Sie ihn einmal und führen ihn überall aus.
Für diese Frage würde ich es VM nennen (zum Beispiel sowohl für Java als auch für .NET). So wird die Ausführung von Programmen so etwas wie
------------ ---- ----
| Executable | -> | VM | -> | OS |
------------ ---- ----
Es ist durchaus sinnvoll, dass der Compiler für die jeweilige VM generisch bleibt. Die Implementierung der VM kann jedoch je nach dem Computer, auf dem sie installiert werden soll, variieren (z. B. (* nix, Windows, Mac) x (32 Bit, 64 Bit).
Meine Frage ist, anstatt VM für die jeweiligen Computer zu schreiben, warum ist der Compiler nicht für diesen bestimmten Computer geschrieben? Anstatt die entsprechende VM herunterzuladen, laden Sie den jeweiligen Compiler herunter, und dieser Compiler kümmert sich um den Maschinencode + das Betriebssystem für diesen bestimmten Computer. Endergebnis ist die Ausführung von nativem Code für jeden Computer. Auf jeden Fall müsste jeder Quellcode für diesen bestimmten Computer kompiliert werden, aber heutzutage können uns die automatisierten Systeme und SCM-Builds dabei helfen.
Sind meine Gründe für Verwirrung richtig oder fehlen mir hier einige technische Details?
Bearbeiten:
Tragbarkeit :
Ja, das ist ein Grund, aber ist Portabilität in heutigen automatisierten Systemen ein großes Problem? Wie oft müssen wir uns Sorgen machen, dass wir es nicht für andere Maschinen kompilieren müssen? Ein Code, der für einen nativen Computer kompiliert wurde, würde eine viel bessere Leistung bringen. Nehmen wir zum Beispiel Java, Sie können unter Windows keine Low-Level-Programmierung durchführen und müssen JNI wählen.
Nehmen Sie automatisierte Systeme wie TeamCity / Jenkins oder andere. Wir könnten ein solches automatisiertes System einrichten, bei dem Code, der über die Versionskontrolle übermittelt wird, zur ausführbaren Datei führt.
Antworten:
Denn dann hätten Sie keine tragbare ausführbare Datei mehr. Sie hätten eine ausführbare Datei für jede Plattform. Der Benutzer müsste entweder eine bestimmte ausführbare Datei für seine Plattform herunterladen oder den Code selbst auf seiner spezifischen Plattform kompilieren.
Bei einer VM benötigt jede Plattform nur ihre plattformspezifische VM. Anschließend können Sie dieselbe ausführbare Datei auf jeder Plattform verteilen .
quelle
Mit diesen VMs fehlt Ihnen etwas Großes. Sie tun genau das, was Sie sagen, aber automatisch. Es wird als Just-In-Time-Compiler bezeichnet, weshalb .NET (unter Windows) und Java der Geschwindigkeit von nativ kompiliertem C ++ - Code sehr nahe kommen.
Der Java / C # -Quellcode wird in Bytecode konvertiert. Dieser Bytecode wird dann auf dem Computer, auf dem er gerade ausgeführt wird, in Maschinencode kompiliert. Zum größten Teil führt die VM den nativen Code aus, anstatt den Bytecode erneut zu verarbeiten.
Ich habe einiges übersprungen und den Prozess zu stark vereinfacht, aber VMs erledigen eine beträchtliche Menge an Arbeit.
quelle
Program -> Compiled to byte code -> JIT for machine -> Execution
. Es sind 4 Schritte erforderlich. Ich frage, warum wir diese 4 Schritte verwenden? dann können wirProgram -> Machine specific compiler -> Execute
Wie viele Konzepte in der Informatik bietet die VM eine Abstraktionsschicht. Sie schreiben Ihren Code gegen eine 'Schnittstelle' (Bytecode / Zwischensprache) und die Abstraktionsschicht (die VM) behandelt die Implementierungsdetails (Kompilierung für den Zielcomputer und andere Aufgaben). Die Abstraktionsschicht stellt Ihnen einen Satz zur Verfügung von Diensten, deren Implementierungsdetails Sie nicht mehr benötigen. In diesem Fall müssen Sie sich nicht mehr um die Besonderheiten der zugrunde liegenden Hardware kümmern, da die Abstraktionsschichtdienste (VM) vorhanden sind. Der Kunde profitiert auf ähnliche Weise - er muss die Details seiner Plattform nicht kennen, nur um die Abstraktionsschicht verwenden zu können.
Natürlich gibt es Kompromisse mit jeder Abstraktion. Sie verlieren die Kontrolle über die Details auf der anderen Seite der Abstraktion und müssen sich auf diese Abstraktion verlassen, um eine sinnvolle Implementierung zu verwenden. Sie müssen die potenziellen Gewinne durch die Verwendung einer Abstraktion gegen die Kompromisse berücksichtigen. In bestimmten Anwendungen können die Nachteile die Vorteile abwägen - möglicherweise benötigen Sie ein hohes Maß an Kontrolle für jede einzelne Plattform.
Ihr Vorschlag, ein automatisiertes System für Kompilierungen auf verschiedenen Plattformen zu verwenden, ist genau das, was die .NET- und Java-Abstraktionsschichten bereits tun. Einer der Vorteile der JIT-Kompilierung besteht darin, dass der Compiler über spezifische Details zu dem Computer verfügt, auf dem er ausgeführt wird. Dies kann zu Optimierungspotenzialen führen, die sonst bei der Durchführung eines Release-Builds auf dem Computer eines Entwicklers nicht möglich wären.
Wenn Sie Bedenken haben, dass die Laufzeit aufgrund der nativen Codegenerierung und der gemeinsamen Programmausführung langsamer wird, haben Sie die Möglichkeit, den nativen Code während der Installation und nicht bei der ersten Ausführung des Programms zu generieren. Zu diesem Zweck bietet .NET nGen an, mit dem native Codegenerierung zur Installationszeit und nicht zur Laufzeit durchgeführt werden kann. Die CLR verwendet dann den zwischengespeicherten nativen Code, anstatt eine JIT-Kompilierung durchzuführen.
quelle
Sie vereinfachen dies sehr, plattformübergreifend ist mehr als nur das Kompilieren für einen bestimmten nativen Befehlssatz. Häufig kann derselbe C / C ++ - Code nicht einfach auf verschiedenen Plattformen neu kompiliert werden, da die APIs und Bibliotheken unterschiedlich sind. Beispielsweise unterscheidet sich die GUI-Entwicklung unter Windows und Ubuntu Linux erheblich. Die Socket-Kommunikation ist unter Unix und IBM z / OS usw. wahrscheinlich nicht identisch.
Um also "einmal schreiben, überall kompilieren (und verknüpfen)" zu erreichen, müssten Sie eine Art Abstraktionsschicht erstellen , in der Sie eine gemeinsame API für alle Dienste auf Betriebssystemebene erstellen. Dann müssen Sie dies für alle Plattformen implementieren und verteilen, die Sie unterstützen möchten.
Auch wenn Speichermodelle heutzutage immer ähnlicher werden, gibt es immer noch einige Unterschiede zwischen verschiedenen Plattformen, sodass Sie auch die Speicherzuordnung abstrahieren müssen. Am einfachsten ist es hier, einen eigenen "virtuellen" Speichermanager über dem nativen zu erstellen.
Wenn Sie schon dabei sind, werden Dateisysteme und Zugriffssteuerung (Access Control, ACL) zwischen Unix und Windows ziemlich unterschiedlich behandelt, sodass Sie auch diese Dinge abstrahieren müssen. Möglicherweise erstellen Sie sogar einen eigenen "Datei" -Wrapper für zugrunde liegende Implementierungen.
Dann haben wir Threading. Da Linux nur Prozesse und Windows Threads hat, müssen wir dies auch irgendwie abstrahieren.
Nun, zu diesem Zeitpunkt haben Sie so ziemlich eine virtuelle Maschine gebaut. Der Punkt hier ist, dass das Kompilieren von beliebigem Code für verschiedene Plattformen ziemlich einfach ist, das Erstellen von funktionierender Software jeder Komplexität, die auf jeder Plattform ausgeführt werden kann, jedoch alles andere als trivial ist.
quelle
Ein weiterer großer Vorteil des VM / JIT-Ansatzes ist die Binärkompatibilität. Angenommen, Sie haben eine Assembly / JAR / DLL, die Klasse A enthält, und eine andere Assembly, die Klasse B enthält, die von Klasse A abgeleitet ist. Was passiert, wenn Sie Klasse A ändern, z. B. ein privates Mitglied hinzufügen? Das Hinzufügen eines privaten Mitglieds sollte keinen Einfluss auf Klasse B haben, aber wenn die Assembly, die B enthält, bereits zu nativem Code kompiliert wurde, würden Sie wahrscheinlich seltsame Fehler und schwer zu reproduzierende Abstürze bekommen, da der native Code mit kompiliert wurde das alte Speicherlayout von A, so dass z. B. zu wenig Speicher für die Variablen von A reserviert würde, sodass die Mitglieder von A und die hinzugefügten Mitglieder B denselben Speicherorten zugeordnet würden.
Wenn Sie dagegen eine VM verwenden, verschwinden alle diese Probleme einfach, da beim Kompilieren des Codes zu nativem Code das Layout aller Klassen bekannt ist.
quelle
Stellen wir uns für eine Sekunde vor, dass dies so funktioniert. Ich schreibe eine Java-Anwendung, die ich dann kompiliere - der Compiler generiert dann eine ausführbare Datei für jede unterstützte Plattform. Ich habe jetzt die folgenden ausführbaren Dateien:
etc etc. etc.
Ich lade dann alle diese auf meine Website hoch und stelle jedem einen Download-Link zur Verfügung. Ich reiche all diese dann bei download.com, tucows.com, sofpedia.com ein und reiche sie einzeln ein.
Das scheint mir ein großer Schmerz zu sein! Wenn ich die Software auf Version 1.1 aktualisiere, muss ich diesen Vorgang wiederholen.
Und was passiert, wenn eine neue Plattform entsteht? Jetzt ist meine Software für diese Plattform nicht verfügbar, es sei denn, ich aktualisiere meine Entwicklersoftware, um sie zu unterstützen, kompiliere sie neu und veröffentliche meine Software für diese neue Plattform erneut.
Und was ist mit dem Benutzer? Sie besuchen meine Website und müssen dann aus einer Liste genau die richtige Version meiner Software auswählen, die sie für ihre Plattform verwenden möchten. Die meisten Benutzer wissen nicht, ob sie x86 oder x64 ausführen, daher werden sie wahrscheinlich die falsche Version herunterladen und eine Art Fehler erhalten. Was ist, wenn sie dann von Windows auf einen Mac wechseln, müssen sie jetzt zurückgehen und meine Software erneut herunterladen.
All dies ist sowohl für den Entwickler als auch für den Benutzer ein großes Problem, und all dies kann vermieden werden, indem nur ein Bytecode kompiliert und dann über eine VM ausgeführt wird, genau wie dies derzeit bei Java der Fall ist.
Wenn die Leistung fast identisch mit der Ausführung von nativem Code ist, stellt sich nicht so sehr die Frage, warum nicht mit nativem Code kompiliert wird, sondern warum die Kompilierung mit nativem Code durchgeführt wird.
quelle
Bei der Bearbeitung Ihres Beitrags stellen Sie die Nützlichkeit der Portabilität für VMs in Frage.
In meinem Berufsleben war Portabilität der wichtigste Faktor. Meine Bereitstellungsplattform ist sehr selten dieselbe Plattform, auf der ich sie bereitstelle. Daher kann ich unter Windows entwickeln und testen und an meine QS-Abteilung weitergeben, die unter Linux testen würde und in unserer Produktionsumgebung unter Solaris bereitstellen.
Dies ist nicht nur ein isoliertes und erfundenes Beispiel - dies war wahrscheinlich in den letzten 12 Jahren meiner mehrjährigen Karriere das A und O meiner beruflichen Existenz. Ich bin sicher, es gibt viele andere mit den gleichen oder ähnlichen Erfahrungen.
Wenn wir verschiedene (Cross-) Compiler (auf demselben Quellcode) verwendet haben, um verschiedene Binärdateien zu erstellen, können Sie sich nicht auf die Qualität jeder Binärdatei verlassen, wenn diese nicht auf einem eigenen Betriebssystem / einer eigenen Architektur getestet wurden.
Das wunderbare Open-Source-FreePascal-Compiler-Projekt hat den Slogan "Einmal schreiben, überall kompilieren" - obwohl dies zutrifft, würde ich nicht erwarten, dass seriöse Softwarehäuser dies tun und die Anwendungen ohne gründliche Tests auf allen Plattformen veröffentlichen.
quelle
Einfach: Testen.
Wenn der Code auf der virtuellen Maschine einwandfrei funktioniert, können Sie sicher sein, dass er auf anderer Hardware ausgeführt wird, auf der nachweislich eine funktionierende VM vorhanden ist.
Wenn Sie für verschiedene Plattformen kompilieren, müssen Sie viele Tests durchführen, da jede Plattform ihre eigenen Macken hat.
quelle
Portabilität ist mehr als nur das Kompilieren des Codes!
Das Verhalten eines C-Programms kann auf verschiedenen Plattformen unterschiedlich sein:
Funktioniert ohne Beanstandung auf einem IBM Power-Chip, jedoch mit einer Ausnahme auf einem SPARC-Chip.
Die Registrierungseinstellungen für Betriebssystem, Betriebssystemversion und Dateisystemumgebungsvariablen können sich alle auf die Ausführung eines kompilierten Programms auswirken.
Eine JVM garantiert so ziemlich das gleiche Verhalten unabhängig von der Hardware- und Softwareumgebung.
quelle