Wenn ich ein C-Programm schreibe und es in eine .exe
Datei kompiliere , .exe
enthält die Datei Rohmaschinenanweisungen an die CPU. (Meiner Ansicht nach).
Wenn ja, wie kann ich die kompilierte Datei auf jedem Computer ausführen, auf dem eine moderne Version von Windows ausgeführt wird? Jede Familie von CPUs hat einen anderen Befehlssatz. Wie kommt es also, dass jeder Computer mit dem entsprechenden Betriebssystem die Anweisungen in meiner .exe
Datei verstehen kann , unabhängig von der physischen CPU?
Außerdem haben Sie auf Websites, die sich auf der Download-Seite einer Anwendung befinden, häufig einen Download für Windows, Linux und Mac (häufig zwei Downloads für jedes Betriebssystem für 86- und 64-Bit-Computer). Warum gibt es nicht mehr Downloads für jede CPU-Familie?
quelle
Antworten:
Ausführbare Dateien hängen sowohl vom Betriebssystem als auch von der CPU ab:
Befehlssatz: Die Binärbefehle in der ausführbaren Datei werden von der CPU gemäß einem Befehlssatz decodiert. Die meisten Consumer-CPUs unterstützen die Befehlssätze x86 (32 Bit) und / oder AMD64 (64 Bit). Ein Programm kann für einen dieser Befehlssätze kompiliert werden, jedoch nicht für beide. Es gibt Erweiterungen für diese Befehlssätze. Support für diese kann zur Laufzeit abgefragt werden. Solche Erweiterungen bieten beispielsweise SIMD-Unterstützung. Durch die Optimierung von Compilern wird möglicherweise versucht, diese Erweiterungen zu nutzen, sofern sie vorhanden sind. In der Regel wird jedoch auch ein Codepfad angeboten, der ohne Erweiterungen funktioniert.
Binärformat : Die ausführbare Datei muss einem bestimmten Binärformat entsprechen, damit das Betriebssystem das Programm korrekt laden, initialisieren und starten kann. Windows verwendet hauptsächlich das Portable Executable-Format, während Linux ELF verwendet.
System-APIs: Das Programm verwendet möglicherweise Bibliotheken, die auf dem ausführenden System vorhanden sein müssen. Wenn ein Programm Funktionen von Windows-APIs verwendet, kann es nicht unter Linux ausgeführt werden. In der Unix-Welt wurden die zentralen Betriebssystem-APIs auf POSIX standardisiert: Ein Programm, das nur die POSIX-Funktionen verwendet, kann auf jedem kompatiblen Unix-System wie Mac OS X und Solaris ausgeführt werden.
Wenn also zwei Systeme dieselben System-APIs und -Bibliotheken anbieten, mit demselben Befehlssatz ausgeführt werden und dasselbe Binärformat verwenden, wird ein für ein System kompiliertes Programm auch auf dem anderen System ausgeführt.
Es gibt jedoch Möglichkeiten, mehr Kompatibilität zu erzielen:
Auf Systemen, auf denen der AMD64-Befehlssatz ausgeführt wird, werden normalerweise auch x86-ausführbare Dateien ausgeführt. Das Binärformat gibt an, welcher Modus ausgeführt werden soll. Die Bearbeitung von 32-Bit- und 64-Bit-Programmen erfordert zusätzliche Anstrengungen des Betriebssystems.
Bei einigen Binärformaten kann eine Datei mehrere Versionen eines Programms enthalten, die für verschiedene Befehlssätze kompiliert wurden. Solche „Fat Binaries“ wurden von Apple beim Übergang von der PowerPC-Architektur zu x86 unterstützt.
Einige Programme werden nicht zu Maschinencode kompiliert, sondern zu einer Zwischendarstellung. Dies wird dann im laufenden Betrieb in tatsächliche Anweisungen übersetzt oder kann interpretiert werden. Dies macht ein Programm unabhängig von der spezifischen Architektur. Eine solche Strategie wurde auf dem UCSD p-System angewendet.
Ein Betriebssystem kann mehrere Binärformate unterstützen. Windows ist ziemlich abwärtskompatibel und unterstützt weiterhin Formate aus der DOS-Ära. Unter Linux können mit Wine die Windows-Formate geladen werden.
Die APIs eines Betriebssystems können für ein anderes Host-Betriebssystem erneut implementiert werden. Unter Windows können Cygwin und das POSIX-Subsystem verwendet werden, um eine (meistens) POSIX-kompatible Umgebung zu erhalten. Unter Linux implementiert Wine viele der Windows-APIs neu.
Durch plattformübergreifende Bibliotheken kann ein Programm von den Betriebssystem-APIs unabhängig sein. Viele Programmiersprachen haben Standardbibliotheken, die dies versuchen, z. B. Java und C.
Ein Emulator simuliert ein anderes System, indem er das fremde Binärformat analysiert, die Anweisungen interpretiert und eine Neuimplementierung aller erforderlichen APIs anbietet. Emulatoren werden häufig verwendet, um alte Nitendo-Spiele auf einem modernen PC auszuführen.
quelle
99% der aktuellen Windows-PCs verfügen über einen 64-Bit-Prozessor, auf dem auch 32-Bit-Software ausgeführt werden kann. Das andere Prozent hat 32-Bit-Prozessoren. Software für 32-Bit-Prozessoren läuft also überall. Software, die für 64-Bit-Prozessoren entwickelt wurde, läuft auf jedem PC, der dem Ersteller der Software am Herzen liegt.
MacOS X und iOS unterstützen "fat binaries" - was Sie herunterladen, kann Versionen für verschiedene Prozessoren enthalten. Niemand entwickelt mehr Anwendungen für PowerPC-Prozessoren, aber vor ein paar Jahren konnte eine ausführbare Datei einen PowerPC, eine 32-Bit-Intel- und eine 64-Bit-Intel-Version enthalten, und die richtige wurde ausgeführt. Wenn Sie heutzutage unter iOS eine App herunterladen, erhalten Sie auf Ihrem Gerät eine Version, die für den Prozessor geeignet ist. Laden Sie es auf ein anderes Gerät herunter und Sie erhalten eine andere Version. Für den Benutzer völlig unsichtbar.
quelle
Eine exe enthält mehr Informationen als nur rohen Maschinencode. Das Betriebssystem liest dies beim Laden und kann herausfinden, wie es ausgeführt werden soll.
Wenn Sie kompilieren, legen Sie im Allgemeinen eine Ziel-CPU fest. Wenn Sie dies nicht tun, wählt der Compiler Ihre aktuelle CPU aus und beschränkt sich darauf, nur Anweisungen auszuwählen, die für Ihre CPU und ältere Versionen dieser CPU gelten. Wenn Sie eine ausgefallene neue Anweisung verwenden möchten, die für bestimmte Revisionen Ihrer Ziel-CPU spezifisch ist, können Sie dem Compiler entweder mitteilen oder sie manuell mit intrinsischem oder Inline-Assembly-Code codieren. Ihr Programm stürzt jedoch ab, wenn es auf einer CPU ausgeführt wird, die diese Anweisung nicht unterstützt.
quelle