Sie erwähnen, warum der Code auch für ein Betriebssystem spezifisch sein muss, wenn er für eine CPU spezifisch ist. Dies ist eigentlich eher eine interessante Frage, die viele der Antworten hier angenommen haben.
CPU-Sicherheitsmodell
Das erste Programm, das auf den meisten CPU-Architekturen ausgeführt wird, wird im sogenannten inneren Ring oder Ring 0 ausgeführt . Wie ein bestimmter CPU-Bogen Ringe implementiert, ist unterschiedlich, aber es steht fest, dass fast jede moderne CPU mindestens zwei Betriebsmodi hat, von denen einer privilegiert ist und Bare-Metal-Code ausführt, der alle legalen Operationen ausführen kann, die die CPU ausführen kann, und der andere nicht vertrauenswürdig und führt geschützten Code aus, der nur einen definierten sicheren Satz von Funktionen ausführen kann. Einige CPUs weisen jedoch eine weitaus höhere Granularität auf. Um VMs sicher zu verwenden, sind mindestens 1 oder 2 zusätzliche Ringe erforderlich (häufig mit negativen Zahlen gekennzeichnet). Dies würde jedoch den Rahmen dieser Antwort sprengen.
Woher kommt das Betriebssystem?
Frühe Single-Tasking-Betriebssysteme
In sehr frühen DOS- und anderen frühen Single-Tasking-basierten Systemen wurde der gesamte Code im inneren Ring ausgeführt. Jedes Programm, das Sie jemals ausgeführt haben, hatte die volle Leistung über den gesamten Computer und konnte buchstäblich alles tun, wenn es sich schlecht verhielt, einschließlich des Löschens aller Ihrer Daten oder sogar des Verursachens von Hardwareschäden In einigen extremen Fällen, wie dem Einstellen ungültiger Anzeigemodi auf sehr alten Bildschirmen, kann dies schlimmer sein, wenn der Code einfach fehlerfrei ist.
Dieser Code war in der Tat weitgehend betriebssystemunabhängig, solange Sie einen Loader hatten, der das Programm in den Speicher laden konnte (ziemlich einfach für frühe Binärformate), und der Code war nicht auf Treiber angewiesen und implementierte den gesamten Hardware-Zugriff selbst, unter dem er ausgeführt werden sollte Jedes Betriebssystem, solange es in Ring 0 ausgeführt wird. Beachten Sie, dass ein sehr einfaches Betriebssystem wie dieses normalerweise als Monitor bezeichnet wird, wenn es nur zum Ausführen anderer Programme verwendet wird und keine zusätzlichen Funktionen bietet.
Moderne Multi-Tasking-Betriebssysteme
Modernere Betriebssysteme, einschließlich UNIX , Windows- Versionen ab NT und verschiedene andere mittlerweile unklare Betriebssysteme, haben beschlossen, diese Situation zu verbessern. Benutzer wollten zusätzliche Funktionen wie Multitasking, damit sie mehr als eine Anwendung gleichzeitig ausführen können, und Schutz, also einen Fehler ( oder böswilliger Code) in einer Anwendung können nicht mehr unbegrenzt Schaden an Maschine und Daten anrichten.
Dies geschah mit den oben erwähnten Ringen, das Betriebssystem würde den einzigen Platz in Ring 0 einnehmen und Anwendungen würden in den äußeren nicht vertrauenswürdigen Ringen ausgeführt, nur in der Lage, einen eingeschränkten Satz von Operationen auszuführen, die das Betriebssystem erlaubte.
Diese Erhöhung der Nützlichkeit und des Schutzes war jedoch mit Kosten verbunden. Programme mussten nun mit dem Betriebssystem zusammenarbeiten, um Aufgaben auszuführen, die sie nicht selbst ausführen durften. Sie konnten beispielsweise nicht mehr die direkte Kontrolle über die Festplatte übernehmen, indem sie auf ihren Speicher zugegriffen und willkürlich geändert haben Stattdessen mussten sie das Betriebssystem auffordern, diese Aufgaben für sie auszuführen, damit überprüft werden konnte, ob sie die Operation ausführen durften. Dabei wurden keine Dateien geändert, die nicht zu ihnen gehörten. Außerdem wurde überprüft, ob die Operation tatsächlich gültig und gültig war würde die Hardware nicht in einem undefinierten Zustand belassen.
Jedes Betriebssystem entschied sich für eine andere Implementierung dieser Schutzfunktionen, teilweise basierend auf der Architektur, für die das Betriebssystem entwickelt wurde, und teilweise basierend auf dem Design und den Prinzipien des betreffenden Betriebssystems. UNIX legte beispielsweise den Schwerpunkt auf Maschinen, die für die Verwendung durch mehrere Benutzer geeignet und fokussiert sind Die dafür verfügbaren Funktionen wurden zwar so konzipiert, dass Windows einfacher ist und auf langsamerer Hardware mit einem einzelnen Benutzer ausgeführt werden kann. Die Art und Weise, in der User-Space-Programme auch mit dem Betriebssystem kommunizieren, ist auf X86 völlig anders als beispielsweise auf ARM oder MIPS, sodass ein Betriebssystem mit mehreren Plattformen Entscheidungen treffen muss, die auf der Notwendigkeit basieren, an der Hardware zu arbeiten, für die es gedacht ist.
Diese betriebssystemspezifischen Interaktionen werden in der Regel als "Systemaufrufe" bezeichnet und umfassen die vollständige Interaktion eines User Space-Programms mit der Hardware über das Betriebssystem. Sie unterscheiden sich grundlegend je nach der Funktion des Betriebssystems, und daher muss ein Programm, das seine Arbeit über Systemaufrufe ausführt, dies tun OS-spezifisch sein.
Der Program Loader
Zusätzlich zu den Systemaufrufen bietet jedes Betriebssystem eine andere Methode zum Laden eines Programms vom sekundären Speichermedium und in den Speicher . Um von einem bestimmten Betriebssystem geladen werden zu können, muss das Programm einen speziellen Header enthalten, der dem Betriebssystem beschreibt, wie es sein kann geladen und laufen.
Früher war dieser Header so einfach, dass das Schreiben eines Loaders für ein anderes Format fast trivial war. Mit modernen Formaten wie elf, die erweiterte Funktionen wie dynamische Verknüpfungen und schwache Deklarationen unterstützen, ist es für ein Betriebssystem jedoch nahezu unmöglich, Binärdateien zu laden was nicht dafür gedacht war, bedeutet, dass es auch ohne Systemaufruf-Inkompatibilitäten immens schwierig ist, ein Programm so in den RAM zu legen, dass es ausgeführt werden kann.
Bibliotheken
Programme verwenden selten Systemaufrufe direkt, sie erhalten ihre Funktionalität jedoch fast ausschließlich durch Bibliotheken, die die Systemaufrufe in ein etwas freundlicheres Format für die Programmiersprache umschließen, z. B. C hat die C-Standardbibliothek und glibc unter Linux und ähnlichen und win32-Bibliotheken unter Windows NT und höher, die meisten anderen Programmiersprachen haben ähnliche Bibliotheken, die die Systemfunktionalität in geeigneter Weise umschließen.
Diese Bibliotheken kann bis zu einem gewissen Grad auch die Cross - Plattform - Probleme überwinden , wie oben beschrieben, gibt es eine Reihe von Bibliotheken , die sich um die Bereitstellung eine einheitliche Plattform für Anwendungen ausgelegt sind , während intern Anrufe an eine breite Palette von OSes Verwaltung wie SDL , bedeutet dies , dass , obwohl Programme können nicht binärkompatibel sein. Programme, die diese Bibliotheken verwenden, können eine gemeinsame Quelle für die Plattformen haben, was die Portierung so einfach wie das erneute Kompilieren macht.
Ausnahmen nach oben
Trotz allem, was ich hier gesagt habe, gab es Versuche, die Einschränkungen zu überwinden, dass Programme nicht auf mehr als einem Betriebssystem ausgeführt werden können. Einige gute Beispiele sind das Wine-Projekt, bei dem sowohl der Win32-Programmlader als auch das Binärformat und die Systembibliotheken erfolgreich emuliert wurden, sodass Windows-Programme unter verschiedenen UNIX-Betriebssystemen ausgeführt werden können. Es gibt auch eine Kompatibilitätsschicht, mit der mehrere BSD UNIX-Betriebssysteme Linux-Software ausführen können, und natürlich Apples eigenes Shim, mit dem man alte MacOS-Software unter MacOS X ausführen kann.
Diese Projekte erfordern jedoch einen enormen manuellen Entwicklungsaufwand. Abhängig davon, wie unterschiedlich die beiden Betriebssysteme sind, reicht der Schwierigkeitsgrad von einer relativ kleinen Abweichung bis zur nahezu vollständigen Emulation des anderen Betriebssystems, was häufig komplexer ist als das Schreiben eines gesamten Betriebssystems an sich. Dies ist also die Ausnahme und nicht die Regel.