Wie laufen Betriebssysteme… ohne Betriebssystem?

167

Ich bin gerade sehr neugierig. Ich bin ein Python-Programmierer, und diese Frage hat mich nur verwirrt: Sie schreiben ein Betriebssystem. Wie machst du das? Es muss irgendwie laufen, und so ist in einem anderen Betriebssystem?

Wie kann eine Anwendung ohne Betriebssystem ausgeführt werden? Wie können Sie den Computer anweisen, C auszuführen und diese Befehle auf dem Bildschirm auszuführen, wenn kein Betriebssystem zum Ausführen vorhanden ist?

Hat es mit einem UNIX-Kernel zu tun? Wenn ja, was ist ein Unix-Kernel oder ein Kernel im Allgemeinen?

Ich bin mir sicher, dass Betriebssysteme komplizierter sind, aber wie funktioniert das?

Thor Correia
quelle
14
Ich bin mir ziemlich sicher, dass ein BIOS dafür gedacht ist - es ist ein wirklich kleines Betriebssystem, das das Ausführen des größeren Betriebssystems bootet.
Sevenseacat
64
Ein Betriebssystem ist praktisch , aber Sie benötigen keines, um Programme auf einem Computer auszuführen.
Andres F.
10
Es ist durchaus möglich, auch Software ohne Betriebssystem zu schreiben. Viele Forth-Interpreten liefen traditionell ohne Betriebssystem (oder man könnte sagen, sie waren Betriebssysteme). Es ist nicht einmal so schwer. Wenn Sie C kennen, schreiben Sie vielleicht gerne ein solches Programm (vielleicht ein kleines Spiel) als Lernübung.
Max
44
Diese Verwirrung ist einer der Kosten für die wundervollen, sicheren und stark abstrahierten Computersysteme, die wir heutzutage verwenden: Die Leute können sehr gute und kompetente Programmierer sein und kennen nicht einmal die Grundlagen der Funktionsweise des Computers. Wie tief willst du gehen? Für sehr niedrige, aber immer noch über der Physik liegende Werte siehe Wie wurden die ersten Mikroprozessoren programmiert? auf Electronics.SE.
DMCKEE
2
Die Programmierung erfolgte vor der Erfindung des aktuellen Betriebssystemkonzepts. Offensichtlich ist es etwas auf dieser Ebene, das das Betriebssystem auslöst. Betriebssysteme werden gebootet. Dies wird in der Regel zumindest in einem 4-jährigen CS-Programm irgendwann erwähnt, da die meisten einen Computer-Theorie-Kurs für Betriebssysteme erfordern.
Rig

Antworten:

263

Es gibt viele Websites, die den Startvorgang durchlaufen (z. B. Wie Computer hochfahren ). Kurz gesagt, es handelt sich um einen mehrstufigen Prozess, der das System immer wieder ein wenig aufbaut, bis es schließlich die Betriebssystemprozesse starten kann.

Es beginnt mit der Firmware auf dem Motherboard, die versucht, die CPU zum Laufen zu bringen. Anschließend wird das BIOS geladen, das wie ein Mini-Betriebssystem aussieht, das die andere Hardware zum Laufen bringt. Sobald dies erledigt ist, sucht es nach einem Startgerät (Festplatte, CD usw.) und findet, sobald es gefunden ist, den MBR (Master Boot Record), lädt ihn in den Speicher und führt ihn aus. Es ist dieses kleine Stück Code, das dann weiß, wie man das Betriebssystem initialisiert und startet (oder andere Bootloader, da die Dinge komplizierter geworden sind). An diesem Punkt würden Dinge wie der Kernel geladen und laufen.

Es ist ziemlich unglaublich, dass es überhaupt funktioniert!

Rechnung
quelle
108
+1 für den letzten Satz.
ein Lebenslauf
39
Es gibt einen Grund, warum es "Booten" heißt. Der Begriff ist die Abkürzung für "Bootstrapping", wie in "Ziehen Sie sich an Ihren Bootstraps hoch".
KeithS
5
Es gab eine Zeit, in der jemand den Bootstrap-Code eingeben oder umschalten musste. Manchmal war es ein einfacher Sprung zur ersten Anweisung eines Programms im ROM. Ein anderes Mal war es Code, von einem Gerät zu lesen und zu der ersten Programmanweisung in den gelesenen Daten zu springen. Die Dinge sind jetzt viel einfacher.
BillThor
15
@BillThor: Wo mit "viel einfacher" Sie natürlich "viel komplizierter" meinen. Sie sind einfach zu bedienen .
Raphael Schweikert
173

Ein "Bare-Metal" -Betriebssystem läuft in nichts. Es führt den vollständigen Befehlssatz auf der physischen Maschine aus und hat Zugriff auf den gesamten physischen Speicher, alle Geräteregister und alle privilegierten Befehle, einschließlich derer, die die Hardware zur Unterstützung des virtuellen Speichers steuern.

(Wenn das Betriebssystem auf einer virtuellen Maschine ausgeführt wird , kann sie denken . Es in der gleichen Situation wie oben Der Unterschied besteht darin , dass bestimmte Dinge emuliert werden oder auf andere Weise durch den Hypervisor behandelt, dh dem Niveau , das die virtuellen Maschinen läuft .)

Obwohl das Betriebssystem möglicherweise in (zum Beispiel) C implementiert ist, stehen ihm nicht alle normalen C-Bibliotheken zur Verfügung. Insbesondere wird es nicht die normalen "stdio" -Bibliotheken geben. Stattdessen wird beispielsweise ein Plattengerätetreiber implementiert, der das Lesen und Schreiben von Plattenblöcken ermöglicht. Es implementiert ein Dateisystem über der Plattenblockschicht und darüber hinaus die Systemaufrufe, die die Laufzeitbibliotheken einer Benutzeranwendung zum Erstellen, Lesen und Schreiben von Dateien usw. ausführen.

Wie kann eine Anwendung ohne Betriebssystem ausgeführt werden?

Es muss sich um eine spezielle Art von Anwendung handeln (z. B. ein Betriebssystem), die weiß, wie man direkt mit der E / A-Hardware usw. interagiert.

Wie können Sie den Computer anweisen, C auszuführen und diese Befehle auf dem Bildschirm auszuführen, wenn kein Betriebssystem zum Ausführen vorhanden ist?

Das tust du nicht.

Die Anwendung (die aus Gründen der Argumentation in C geschrieben wurde) wird kompiliert und auf einem anderen Computer verknüpft, um ein systemeigenes Codebild zu erhalten. Dann wird das Image auf die Festplatte an einer Stelle geschrieben, an der es vom BIOS gefunden werden kann. Das BIOS lädt das Image in den Speicher und führt eine Anweisung aus, um zum Einstiegspunkt der Anwendung zu springen.

Es gibt (normalerweise) kein "Ausführen von C und Ausführen von Befehlen" in der Anwendung, es sei denn, es handelt sich um ein vollständiges Betriebssystem. In diesem Fall liegt es in der Verantwortung des Betriebssystems, die gesamte dafür erforderliche Infrastruktur zu implementieren. Keine Magie. Einfach viel Code.

Die Antwort von Bill bezieht sich auf das Bootstrapping. Hierbei handelt es sich um den Vorgang, bei dem Sie von einem ausgeschalteten Computer zu einem Computer wechseln, auf dem das normale Betriebssystem ausgeführt wird. Es ist jedoch zu beachten, dass das BIOS nach Abschluss seiner Aufgaben (normalerweise) die vollständige Kontrolle über die Hardware an das Hauptbetriebssystem übergibt und keine weitere Rolle spielt - bis zum nächsten Neustart des Systems. Das Hauptbetriebssystem läuft sicherlich nicht "innerhalb" des BIOS im herkömmlichen Sinne.

Hat es mit einem UNIX-Kernel zu tun? Wenn ja, was ist ein Unix-Kernel oder ein Kernel im Allgemeinen?

Ja tut es.

Der UNIX-Kernel ist der Kern des UNIX-Betriebssystems. Es ist der Teil von UNIX, der alle oben beschriebenen "Bare-Metal" -Stoffe ausführt.

Die Idee eines "Kernels" ist, dass Sie versuchen, die Systemsoftware in Kernmaterial (das physischen Gerätezugriff, den gesamten Speicher usw. erfordert) und Nicht-Kernmaterial zu unterteilen. Der Kernel besteht aus dem Kernmaterial.

In Wirklichkeit ist die Unterscheidung zwischen Kernel / Core und Nicht-Kernel / Nicht-Core komplizierter. Und es gab eine Menge Debatten darüber, was wirklich in einen Kernel gehört und was nicht. (Schlagen Sie zum Beispiel den Micro-Kernel nach.)

Stephen C
quelle
6
Phänomenale Antwort. Ich würde dir, wenn möglich, noch einige Upvotes geben.
weberc2
7
Viele gute Antworten hier, daher werde ich dies als Kommentar hinzufügen, da dies bisher noch niemand erwähnt hat: Eine der Schlüsselfunktionen in einem Betriebssystem besteht darin, dass mehrere Anwendungen aus Benutzersicht "gleichzeitig" ausgeführt werden können. Die Möglichkeit, Prozesse zu planen und Prozesse vor gegenseitigen Verhaltensänderungen zu schützen, ist normalerweise nur in einem Betriebssystem und nicht in Firmware oder BIOS zu finden.
Sean Barbeau
2
The idea of a "kernel" is that you try to separate the system software into core stuffEinfach zu merken, wenn man feststellt, dass der Begriff kernelaus dem Deutschen stammt Kern, was Kern / Kern bedeutet.
deed02392
1
Ich
3
"Weg damit zu kommen, machte PC-Benutzer weniger schlau." - Nicht weniger schlau ... weniger Computerkenntnisse. Man könnte auch sagen, dass es die Anzahl der PC-Benutzer erhöht hat.
Stephen C
62

Am Anfang war kein Strom in der CPU.

Und der Mann sagte "lass es Strom geben", und die CPU fing an, von einer gegebenen Adresse im Speicher zu lesen und den Befehl auszuführen, der dort vorhanden war. Dann die nächste und so weiter bis zum Ende der Macht.

Das war der Stiefel. Ihre Aufgabe bestand darin, eine weitere Software zu laden, um auf die Umgebung zuzugreifen, in der sich die Hauptsoftware befand, und sie zu laden.

Schließlich wurden Sie auf einem freundlichen Bildschirm aufgefordert, sich anzumelden.

mouviciel
quelle
58
Diese Antwort sollte nach christianity.stackexchange.com verschoben werden
Coomie
6
Was ist "eine gegebene Adresse"? Woher kommt sie? Sorry, dass du hier Charles Darwin spielst.
Midhat
7
@Midhat - Die erste Adresse, die von der CPU abgerufen wird, ist fest verdrahtet. Normalerweise ist es 0.
mouviciel
17
... und am 7. Tag ruhte sich der Mann mit seinen Spielen aus
Kanadier Luke
9
@mouviciel Die Adresse im Speicher 0x7C00für jede x86kompatible Architektur und hat zunächst vom BIOS gefüllt werden , die in der Regel lädt den ersten Sektor , was auch immer startfähiges Gerät es vorzieht ... Nizza Antwort aber: -7
Tobias Kienzler
29

Es tut mir leid, dass ich zu spät komme, aber ich werde es so beschreiben:

  • Das Motherboard wird mit Strom versorgt.

  • Zeitschaltkreise starten und stabilisieren sich bei Bedarf allein aufgrund ihrer elektrischen Eigenschaften. Einige neuere Geräte verwenden möglicherweise einen sehr eingeschränkten Mikroprozessor oder Sequenzer.

    Es sollte beachtet werden, dass viele Dinge wie "Timing-Schaltkreise starten und stabilisieren, wenn nötig" in Hardware nicht mehr wirklich vorkommen. Ein enormer Teil dieser Arbeit besteht aus extrem spezialisierter Software, die auf sehr begrenzten Subprozessoren / Sequenzern ausgeführt wird.

    - jkerian am 25. Oktober um 5:20 Uhr

  • Die CPU und der RAM werden mit Strom versorgt.

  • Die CPU lädt (basierend auf ihrer internen Verdrahtung) Daten aus dem BIOS. Auf einigen Computern wird das BIOS möglicherweise in den Arbeitsspeicher gespiegelt und von dort ausgeführt, aber dies ist selten IIRC.

    Beim Einschalten beginnen x86-kompatible CPUs an der Adresse 0xFFFFFFF0 im Adressraum ...

    -Micheal Steil, 17 Fehler, die Microsoft im Xbox-Sicherheitssystem gemacht hat ( Archiv )

  • Das BIOS ruft Hardware-Ports und -Adressen auf, die vom Motherboard für Festplatten- und andere Hardware-E / A-Vorgänge verwendet werden, und dreht Festplatten hoch, damit unter anderem der Rest des Arbeitsspeichers funktioniert.

  • Der BIOS-Code (über die in der Hardware gespeicherten CMOS-Einstellungen) verwendet IDE- oder SATA-Befehle auf niedriger Ebene, um den Startsektor jeder Festplatte in einer vom CMOS festgelegten Reihenfolge zu lesen, oder ein Benutzer überschreibt diese mit einem Menü.

  • Die erste Platte mit einem Bootsektor erhält ihren Bootsektor ausgeführt. Dieser Bootsektor ist eine Assembly, die Anweisungen zum Laden weiterer Daten von der Festplatte, zum Laden größerer NTLDR, späterer Phasen GRUBusw. enthält.

  • Schließlich wird der OS-Maschinencode vom Bootloader direkt oder indirekt über Chainloading ausgeführt, indem ein Bootsektor von einem alternativen oder versetzten Ort geladen wird.

Sie bekommen dann eine freundliche Kernel-Panik, einen erstickten Pinguin oder Ihre Festplatte kommt aufgrund eines Kopfcrashs zum Stillstand. =) Im alternativen Szenario richtet Ihr Kernel Prozesstabellen, speicherinterne Strukturen und Bereitstellungsdatenträger ein, lädt Treiber, Module und eine GUI oder eine Reihe von Diensten (falls auf einem Server). Dann werden Programme ausgeführt, wenn ihre Header gelesen werden, und ihre Assemblierung wird in den Speicher gebracht und entsprechend zugeordnet.

ζ--
quelle
2
Es sollte beachtet werden, dass viele Dinge wie "Timing-Schaltkreise starten und stabilisieren, wenn nötig" in der Hardware nicht mehr wirklich vorkommen. Ein enormer Teil dieser Arbeit besteht aus extrem spezialisierter Software, die auf sehr begrenzten Subprozessoren / Sequenzern ausgeführt wird. - Ein freundlicher Nachbarschaft Firmware-Ingenieur
Jkerian
@jkerian Stört es dich, dass ich deinen Kommentar in meinem Post zitiert habe?
ζ--
überhaupt nicht.
Jkerian
BIOS ist kein Betriebssystem. BIOS ist eine Abkürzung für Basic Input / Output System, und das ist, was BIOS tut. Es ermöglicht Programmierern, Ressourcen auf niedriger Ebene mit vom Hersteller bereitgestellten Treibern zu verwenden. Wenn das Betriebssystem in den geschützten (32 Bit) oder langen (64 Bit) Modus wechselt, ist das BIOS nicht mehr verfügbar, und das Betriebssystem verwendet eigene Treiber, die im Wesentlichen die Funktionen des BIOS auf "niedrigeren" Ebenen ersetzen. Moderne Betriebssysteme, zum Beispiel Linux und Windows, verwenden das BIOS nur zum Erkennen verwendbarer RAM-Abschnitte und zum Laden ihres eigenen erweiterten Loaders, der die erforderlichen Treiber laden kann.
Hannes Karppila
1
@HannesKarppila Aktualisiert; das ist jetzt ungefähr vier Jahre alt und ich bin nicht mehr auf dieser Seite aktiv.
ζ--
15

Es gibt viele gute Antworten, aber ich wollte Folgendes hinzufügen: Sie haben erwähnt, dass Sie aus einem Python-Hintergrund stammen. Python ist eine nicht interpretierte (oder "interpiled" oder was auch immer, zumindest in typischen CPython-Anwendungsfällen) Sprache. Das bedeutet, dass Sie eine andere Software (den Python-Interpreter) haben, die sich den Quellcode ansieht und ihn auf irgendeine Weise ausführt. Dies ist ein gutes Modell und ermöglicht sehr schöne Hochsprachen, die von der eigentlichen Hardware gut abstrahiert sind. Nachteil ist, dass Sie immer zuerst diese Interpreter-Software benötigen.

Eine solche Interpretersoftware ist typischerweise in einer Sprache geschrieben, die zu Maschinencode kompiliert wird, beispielsweise C oder C ++. Maschinencode ist das, was die CPU verarbeiten kann. Was eine CPU tun kann, ist, einige Bytes aus dem Speicher zu lesen und abhängig von den Bytewerten eine bestimmte Operation zu starten. Eine Bytesequenz ist also ein Befehl zum Laden einiger Daten aus dem Speicher in ein Register, eine andere Sequenz zum Hinzufügen von zwei Werten, eine andere zum Speichern des Werts aus einem Register zurück in den Hauptspeicher und bald (ein Register ist ein spezieller Speicherbereich, der Teil ist) von der CPU, wo es am besten funktionieren kann), sind die meisten dieser Befehle auf dieser Ebene ziemlich niedrig. Der für diese Maschinencode-Anweisungen lesbare Code ist Assembler-Code. Dieser Maschinencode ist im Grunde das, was in .exe- oder.com-Dateien unter Windows oder in Linux / Unix-Binärdateien gespeichert ist.

Wenn ein Computer jetzt hochgefahren wird, ist er stumm, hat aber einige Kabel, die solche Maschinencodeanweisungen lesen. Auf einem PC ist dies normalerweise (derzeit) ein EEPROM-Chip auf dem Mainboard, der das BIOS (Basic Input Output System) enthält. Dieses System kann nicht viel, es kann den Zugriff auf Hardware usw. vereinfachen und dann eine Tastenoperation ausführen: Gehen Sie zu Booten und kopieren Sie die ersten Bytes (auch bekannt als Master Boot Record, MBR) in den Speicher und teilen Sie der CPU mit, dass "hier ist Ihr Programm". Die CPU behandelt diese Bytes dann als Maschinencode und führt sie aus. Typischerweise ist dies ein Betriebssystem-Lader, der den Kernel mit einigen Parametern lädt und dann die Steuerung an diesen Kernel übergibt, der dann alle seine Treiber lädt, um auf die gesamte Hardware zuzugreifen, ein Desktop- oder Shell-Programm oder was auch immer zu laden und dem Benutzer zu erlauben, sich anzumelden und benutze das System.

johannes
quelle
6
"interpiled"? Ich habe diesen Begriff noch nie gehört.
Bryan Oakley
3
Dieser Begriff wurde vor ungefähr fünf Jahren häufig verwendet, um "moderne" Dolmetscher zu beschreiben, die eine eigene Kompilierungsphase haben, die von der Ausführung getrennt ist. Keine Ahnung, ob dieser Begriff irgendwo überlebt hat ;-)
johannes
1
"interpretiert"? Ich habe diesen Begriff noch nie gehört.
Cole Johnson
12

Sie fragen "Wie kann eine Anwendung ohne Betriebssystem ausgeführt werden". Die einfache Antwort lautet "Ein Betriebssystem ist keine Anwendung". Ein Betriebssystem kann zwar mit denselben Tools wie eine Anwendung erstellt werden und besteht aus demselben Rohmaterial, es ist jedoch nicht dasselbe. Ein Betriebssystem muss nicht die gleichen Regeln wie eine Anwendung einhalten.

OTOH, Sie können sich die tatsächliche Hardware und Firmware als das "Betriebssystem" vorstellen, in dem die "Anwendung" des Betriebssystems ausgeführt wird. Die Hardware ist ein sehr einfaches Betriebssystem - sie kann Anweisungen ausführen, die in Maschinencode geschrieben sind, und sie weiß, dass sie beim Start eine ganz bestimmte Speicheradresse für ihre erste Anweisung prüfen sollte. Es startet also und führt sofort die erste Anweisung aus, gefolgt von der zweiten und so weiter.

Das Betriebssystem ist also einfach Maschinencode, der an einem bekannten Ort vorhanden ist und der direkt mit der Hardware interagieren kann.

Bryan Oakley
quelle
1
+1 Ich denke das ist die beste Antwort. In Bezug auf Abstraktionen denke ich, dass Sie es auf die richtigen Ebenen nageln.
Preet Sangha
6

Die Beantwortung Ihrer Frage setzt voraus, dass Sie wissen, wie der native Code (für die CPU) aussieht und wie er von der CPU interpretiert wird.

Normalerweise basiert der gesamte Kompilierungsprozess auf der Übersetzung von Dingen, die Sie in C, Pascal oder sogar Python (unter Verwendung von pypy) und C # schreiben, in Dinge, die die CPU versteht, dh einfache Anweisungen wie "etwas unter [Speicheradresse] speichern", "Zahlen hinzufügen, die unter eax-Registern gespeichert sind und ebx "," call function foo "," vergleiche eax mit 10 ". Diese Anweisungen werden nacheinander ausgeführt und bewirken Dinge, die Sie mit Ihrem Code tun wollten.

Denken Sie jetzt darüber nach: Sie brauchen kein Betriebssystem, um diesen nativen Code auszuführen! Sie müssen diesen Code nur in den Speicher laden und der CPU mitteilen, dass er vorhanden ist und ausgeführt werden soll. Seien Sie jedoch nicht zu sehr besorgt. Das ist der Job, um den sich das BIOS kümmern sollte - es lädt Ihren Code (nur einen und einen Sektor) direkt nach dem Start der CPU unter der physischen Adresse 0x7C00. Dann beginnt die CPU, diesen einen Sektor (512 B) Ihres Codes auszuführen. Und Sie können alles tun, was Sie sich vorstellen! Natürlich ohne jegliche Unterstützung durch das Betriebssystem. Das liegt daran, dass SIE das Betriebssystem sind. Cool was? Keine Standardbibliothek, kein Boost, kein Python, keine Programme, keine Treiber! Du musst alles selbst schreiben.

Und wie kommuniziert man mit Hardware? Nun, Sie haben zwei Möglichkeiten:

  1. Sie bleiben im "Real-Modus" - CPU-Ausführungsmodus mit nur 1 MB Speicher (und noch weniger), keinen erweiterten CPU-Funktionen wie CPU-Erweiterungen, Speicherschutz und Multitasking. 16-Bit ausführbarer Code, alter Adressierungsmodus ... Aber mit einigen vom BIOS bereitgestellten Routinen, einschließlich einfacher Bildschirmausgabe, Tastaturunterstützung, Festplatten-E / A und Energieverwaltung. Mit einem Wort, Sie sind zurück in Zeiten von MS-DOS und 16-Bit-CPUs.
  2. Sie wechseln in den "Geschützten Modus" mit allen Funktionen Ihrer CPU, dem gesamten installierten Speicher und so weiter. Im geschützten Modus sind Sie jedoch völlig alleine und müssen ALLES selbst erledigen (und Sie kommunizieren mit der Hardware mithilfe von "In" - und "Out" -Anweisungen zur Eingabe / Ausgabe von Daten an E / A-Ports und mithilfe von Interrupts.) /O). Muss ich sagen, dass jedes Betriebssystem seit Windows 95 und dem allerersten Linux diese Option wählt?

Jetzt fragst du, was Kernel ist. Kurz gesagt, Kernel ist alles, was Sie nicht direkt sehen und erleben. Es verwaltet zusammen mit den Treibern alles, angefangen von Ihrer Tastatur bis hin zu fast jeder Hardware in Ihrem PC. Sie kommunizieren damit über eine grafische Shell oder ein Terminal. Oder durch Funktionen in Ihrem Code, die jetzt zum Glück mit Unterstützung des Betriebssystems ausgeführt werden.

Zum besseren Verständnis kann ich Ihnen einen Rat geben: Versuchen Sie, Ihr eigenes Betriebssystem zu schreiben. Auch wenn es "Hallo Welt" auf den Bildschirm schreiben wird.

Mateusz K.
quelle
3

Es gibt einige Unterschiede für die Funktionsweise eines Betriebssystems, die extrem systemabhängig sind. Um nützlich zu sein, muss ein System beim Start ein vorhersehbares Verhalten aufweisen, z. B. "Ausführung an Adresse X starten". Bei Systemen, bei denen nichtflüchtiger Speicher (z. B. Flash-Speicher) im Programmbereich zugeordnet ist, ist dies relativ einfach, da Sie nur sicherstellen, dass Sie den Startcode an der richtigen Stelle im Programmbereich des Prozessors platzieren. Dies ist bei Mikrocontrollern sehr häufig. Einige Systeme müssen ihre Startprogramme von einem anderen Speicherort abrufen, bevor sie ausgeführt werden können. In diesen Systemen sind einige Operationen fest (oder fast fest) verkabelt. Es gibt einige Prozessoren, die ihren Startcode über i2c von einem anderen Chip abrufen.

Systeme, die die x86-Prozessorfamilie verwenden, verwenden normalerweise einen mehrstufigen Startprozess, der aufgrund seiner Evolution und Abwärtskompatibilität recht komplex ist. Das System führt eine Firmware (BIOS - Basic Input / Output System oder ähnliches) aus, die sich in einem nichtflüchtigen Speicher auf der Hauptplatine befindet. Manchmal wird ein Teil oder die gesamte Firmware in den Arbeitsspeicher kopiert (verlagert), um die Ausführung zu beschleunigen. Dieser Code wurde mit dem Wissen geschrieben, welche Hardware vorhanden und zum Booten verwendbar sein würde.

Die Start-Firmware wird normalerweise mit Annahmen darüber geschrieben, welche Hardware auf dem System vorhanden sein wird. Vor Jahren gab es auf einer 286-Maschine wahrscheinlich die Annahme, dass ein Diskettenlaufwerk-Controller an der E / A-Adresse X vorhanden ist und Sektor 0 an einen bestimmten Speicherort geladen wird, wenn ein bestimmter Befehlssatz (und der Code an Sektor 0) gegeben wird weiß, wie man die BIOS-eigenen Funktionen verwendet, um mehr Code zu laden, und schließlich wird genug Code geladen, um ein Betriebssystem zu sein. Bei einem Mikrocontroller kann davon ausgegangen werden, dass ein serieller Anschluss mit bestimmten Einstellungen vorhanden ist, bei denen darauf gewartet werden muss, dass Befehle (um eine komplexere Firmware zu aktualisieren) X-mal ausgeführt werden, bevor der Startvorgang fortgesetzt wird.

Der genaue Startvorgang eines bestimmten Systems ist für Sie nicht so wichtig wie das Wissen, dass er sich auf verschiedenen Systemen unterscheidet, aber auch, dass alle Dinge gemeinsam haben. Oft werden die E / A-Geräte innerhalb des Startcodes (Bootstrapping) abgefragt, wenn E / A-Vorgänge ausgeführt werden müssen, anstatt sich auf Interrupts zu verlassen. Dies liegt daran, dass Interrupts komplex sind, Stack-RAM verwenden (das möglicherweise noch nicht vollständig eingerichtet ist) und Sie sich keine Gedanken darüber machen müssen, andere Vorgänge zu blockieren, wenn Sie der einzige Vorgang sind.

Nach dem ersten Laden verhält sich der Betriebssystemkern (der Kernel ist der Hauptteil der meisten Betriebssysteme) zunächst ähnlich wie die Firmware. Es muss entweder mit Kenntnissen der vorhandenen Hardware programmiert werden oder vorhandene Hardware ermitteln, RAM als Stack-Space einrichten, verschiedene Tests durchführen, verschiedene Datenstrukturen einrichten, möglicherweise ein Dateisystem ermitteln und bereitstellen und dann möglicherweise ein Programm starten, das mehr ist wie die Programme, die Sie zum Schreiben verwenden (ein Programm, das auf einem vorhandenen Betriebssystem beruht).

Der OS-Code wird normalerweise in einer Mischung aus C und Assembly geschrieben. Der allererste Code für den OS-Kernel befindet sich wahrscheinlich immer in der Assembly und richtet beispielsweise den Stack ein, auf den sich der C-Code stützt, und ruft dann eine C-Funktion auf. Andere handgeschriebene Assemblierungen sind ebenfalls enthalten, da einige Operationen, die ein Betriebssystem ausführen muss, in C häufig nicht ausdrückbar sind (wie das Umschalten des Kontexts / das Austauschen von Stapeln). Oft müssen spezielle Flags an den C-Compiler übergeben werden, damit dieser sich nicht auf die von den meisten C-Programmen verwendeten Standardbibliotheken verlässt und nicht erwartet, dass eine vorhanden istint main(int argc, char *argv[])im Programm. Außerdem müssen spezielle Linker-Optionen verwendet werden, die die meisten Anwendungsprogrammierer niemals verwenden. Dies kann dazu führen, dass das Kernel-Programm erwartet, an einer bestimmten Adresse geladen zu werden, oder es kann so eingerichtet werden, dass es an bestimmten Stellen externe Variablen gibt, obwohl diese Variablen in keinem C-Code deklariert wurden (dies ist nützlich für speicherabgebildete E / A oder andere spezielle Speicherplätze).

Die gesamte Operation scheint zunächst magisch zu sein, aber nachdem Sie sie untersucht und Teile davon verstanden haben, wird die Magie nur zu einer Reihe von Programmen, für deren Implementierung viel mehr Planungs- und Systemkenntnisse erforderlich sind. Das Debuggen erfordert jedoch Magie.

nategoose
quelle
3

Um zu verstehen, wie Betriebssysteme funktionieren, kann es hilfreich sein, sie in zwei Kategorien zu unterteilen: diejenigen, die auf Anfrage lediglich Dienste für Anwendungen bereitstellen, und diejenigen, die Hardwarefunktionen in der CPU verwenden, um zu verhindern, dass Anwendungen Dinge tun, die sie nicht tun sollten. MS-DOS war vom ehemaligen Stil; Alle Windows-Versionen seit 3.0 haben den letzteren Stil (zumindest wenn etwas leistungsfähigeres als ein 8086 ausgeführt wird).

Der ursprüngliche IBM PC, auf dem PC-DOS oder MS-DOS ausgeführt wird, wäre ein Beispiel für den früheren Stil von "OS" gewesen. Wenn eine Anwendung ein Zeichen auf dem Bildschirm anzeigen wollte, hätte es einige Möglichkeiten gegeben, dies zu tun. Es könnte die Routine aufrufen, die MS-DOS auffordert, sie an die "Standardausgabe" zu senden. Wenn dies der Fall wäre, würde MS-DOS prüfen, ob die Ausgabe umgeleitet wurde, und wenn dies nicht der Fall wäre, würde es eine Routine aufrufen, die im ROM gespeichert ist (in einer Sammlung von Routinen, die IBM als Basic Input / Output System bezeichnet), die ein Zeichen am anzeigt Cursor positionieren und Cursor bewegen ("Teletyp schreiben"). Diese BIOS-Routine würde dann ein Bytepaar irgendwo im Bereich von 0xB800: 0 bis 0xB800: 3999 speichern; Hardware auf dem Farbgrafikadapter ruft wiederholt Paare von Bytes in diesem Bereich ab. Verwenden Sie das erste Byte jedes Paares, um eine Zeichenform auszuwählen, und das zweite Byte, um Vordergrund- und Hintergrundfarben auszuwählen. Die Bytes werden abgerufen und in einer Reihenfolge zu roten, grünen und blauen Signalen verarbeitet, die eine lesbare Textanzeige ergibt.

Programme auf dem IBM PC können Text anzeigen, indem sie die DOS-Routine "Standardausgabe" oder die BIOS-Routine "Teletyp schreiben" verwenden oder ihn direkt im Anzeigespeicher speichern. Viele Programme, die viel Text anzeigen mussten, entschieden sich schnell für letzteres, da es buchstäblich hunderte Male so schnell sein konnte wie die DOS-Routinen. Dies lag nicht daran, dass die DOS- und BIOS-Routinen außergewöhnlich ineffizient waren. Sofern die Anzeige nicht ausgeblendet war, konnte sie nur zu bestimmten Zeiten beschrieben werden. Die BIOS-Routine zur Ausgabe eines Zeichens wurde so konzipiert, dass sie jederzeit aufgerufen werden kann. Jede Anforderung musste daher erneut gestartet werden, um auf den richtigen Zeitpunkt für die Ausführung einer Schreiboperation zu warten. Im Gegensatz dazu könnte sich Anwendungscode, der wusste, was zu tun ist, nach den verfügbaren Möglichkeiten zum Schreiben des Displays organisieren.

Ein wesentlicher Punkt hierbei ist, dass DOS und BIOS zwar die Ausgabe von Text auf dem Bildschirm ermöglichten, diese Fähigkeiten jedoch nicht besonders "magisch" waren. Eine Anwendung, die Text auf das Display schreiben wollte, konnte dies genauso effektiv tun, zumindest wenn die Display-Hardware wie erwartet funktionierte (wenn jemand einen Monochrome Display Adapter installiert hatte, der dem CGA ähnelte, aber über einen Zeichenspeicher verfügte) befindet sich bei 0xB000: 0000-0xB000: 3999), würde das BIOS dort automatisch Zeichen ausgeben; Eine Anwendung, die für die Arbeit mit dem MDA oder CGA programmiert wurde, könnte dies ebenfalls tun, aber eine Anwendung, die nur für den CGA programmiert wurde, wäre auf dem MDA völlig nutzlos.

Bei neueren Systemen sieht das etwas anders aus. Prozessoren haben verschiedene "Privileg" -Modi. Sie beginnen im privilegiertesten Modus, in dem Code alles tun darf, was er will. Sie können dann in einen eingeschränkten Modus wechseln, in dem nur ausgewählte Speicherbereiche oder E / A-Funktionen verfügbar sind. Code kann nicht direkt von einem eingeschränkten Modus zurück in den Berechtigungsmodus wechseln, aber der Prozessor hat Einstiegspunkte für den privilegierten Modus definiert, und Code für den eingeschränkten Modus kann den Prozessor auffordern, Code an einem dieser Einstiegspunkte im privilegierten Modus auszuführen. Darüber hinaus gibt es Einstiegspunkte im privilegierten Modus, die mit einer Reihe von Vorgängen verknüpft sind, die im eingeschränkten Modus verboten wären. Angenommen, jemand wollte mehrere MS-DOS-Anwendungen gleichzeitig ausführen, wobei jede einen eigenen Bildschirm hat. Wenn Anwendungen mit 0xB800: 0 direkt auf den Anzeigecontroller schreiben könnten, gäbe es keine Möglichkeit zu verhindern, dass eine Anwendung den Bildschirm einer anderen Anwendung überschreibt. Andererseits könnte ein Betriebssystem die Anwendung im eingeschränkten Modus ausführen und alle Zugriffe auf den Anzeigespeicher abfangen. Wenn sich herausstellte, dass eine Anwendung, die sich im "Hintergrund" befinden sollte, versuchte, 0xB800: 160 zu schreiben, konnte sie die Daten in einem Speicher ablegen, den sie als Hintergrundanwendungs-Bildschirmpuffer reserviert hatte. Wenn diese Anwendung später in den Vordergrund geschaltet wird, kann der Puffer auf den realen Bildschirm kopiert werden. Ein Betriebssystem könnte die Anwendung im eingeschränkten Modus ausführen und Zugriffe auf den Anzeigespeicher abfangen. Wenn sich herausstellte, dass eine Anwendung, die sich im "Hintergrund" befinden sollte, versuchte, 0xB800: 160 zu schreiben, konnte sie die Daten in einem Speicher ablegen, den sie als Hintergrundanwendungs-Bildschirmpuffer reserviert hatte. Wenn diese Anwendung später in den Vordergrund geschaltet wird, kann der Puffer auf den realen Bildschirm kopiert werden. Ein Betriebssystem könnte die Anwendung im eingeschränkten Modus ausführen und Zugriffe auf den Anzeigespeicher abfangen. Wenn sich herausstellte, dass eine Anwendung, die sich im "Hintergrund" befinden sollte, versuchte, 0xB800: 160 zu schreiben, konnte sie die Daten in einem Speicher ablegen, den sie als Hintergrundanwendungs-Bildschirmpuffer reserviert hatte. Wenn diese Anwendung später in den Vordergrund geschaltet wird, kann der Puffer auf den realen Bildschirm kopiert werden.

Die wichtigsten Dinge, die zu beachten sind, sind: (1) Obwohl es oft praktisch ist, über einen Standardsatz von Routinen zu verfügen, um verschiedene Standarddienste wie das Anzeigen von Text auszuführen, tun sie nichts, was eine Anwendung, die im "privilegierten Modus" ausgeführt wurde, nicht kann wenn es richtig programmiert wurde, um mit der Hardware umzugehen, die installiert wurde; (2) Obwohl die meisten Anwendungen, die heute ausgeführt werden, von ihrem Betriebssystem daran gehindert würden, solche E / A-Vorgänge direkt auszuführen, kann ein Programm, das im privilegierten Modus gestartet wird, alles tun, was es möchte, und beliebige Regeln für den eingeschränkten Modus einrichten Programme.

Superkatze
quelle
2

Wie Stephen C. sagte, geht es nicht nur um das Starten des Betriebssystems, sondern auch darum, wie es ausgeführt wird, mit der Hardware und der Software darüber interagiert.

Ich werde nur zu seiner Antwort hinzufügen, dass Sie vielleicht einen Blick auf "Die Elemente von Computersystemen" werfen möchten . Es ist ein Buch und einige Tools, die erklären, wie ein Computer, ein Betriebssystem und Compiler interagieren. Das Einzigartige daran ist, dass Sie mit diesen Tools Ihr eigenes Betriebssystem in einer simulierten Umgebung sehr schnell entwickeln können. Dabei werden die vielen Details ignoriert, die für eine echte Umgebung erforderlich sind, damit Sie die Konzepte verstehen können . Es macht einen tollen Job, wenn Sie den Wald anstelle der Bäume sehen.

Wenn Sie mehr über die Interaktion des Betriebssystems mit der Hardware erfahren möchten, lesen Sie Minix .

Gilad Naor
quelle
1

Sie schreiben ein Betriebssystem. Es muss irgendwie laufen, und so ist in einem anderen Betriebssystem?

Ihre Anwendung wird in einem Betriebssystem ausgeführt. Dieses Betriebssystem bietet Dienste für Ihre Anwendung, z. B. das Öffnen einer Datei und das Schreiben von Bytes. Diese Dienste werden normalerweise über Systemaufrufe bereitgestellt.

Das Betriebssystem läuft in der Hardware. Die Hardware stellt Dienste für das Betriebssystem bereit, z. B. das Einstellen der Baudrate eines seriellen Anschlusses und das Schreiben von Bytes. Diese Dienste werden normalerweise über Speicherregister oder E / A-Ports bereitgestellt.


Um ein sehr vereinfachtes Beispiel zu geben, wie dies funktioniert:

Ihre Anwendung weist das Betriebssystem an, etwas in eine Datei zu schreiben. Das Betriebssystem bietet Ihrer Anwendung Konzepte wie Dateien und Verzeichnisse.

Auf der Hardware existieren diese Konzepte nicht. Die Hardware bietet Konzepte wie Festplatten, die in feste Blöcke von 512 Bytes unterteilt sind. Das Betriebssystem entscheidet, welche Blöcke für Ihre Datei verwendet werden sollen, und einige andere Blöcke für Metadaten wie Dateiname, Größe und Speicherort auf der Festplatte. Dann teilt es der Hardware mit: Schreiben Sie diese 512 Bytes in den Sektor mit dieser Nummer auf der Platte mit dieser Nummer; Schreiben Sie diese anderen 512 Bytes in den Sektor mit dieser anderen Nummer auf die Platte mit derselben Nummer. und so weiter.

Die Art und Weise, wie das Betriebssystem die Hardware dazu auffordert, ist sehr unterschiedlich. Eine der Funktionen eines Betriebssystems besteht darin, die Anwendungen vor diesen Unterschieden zu schützen. Für das Festplatten-Beispiel müsste das Betriebssystem auf einer Hardware-Art die Festplatten- und Sektornummer in einen E / A-Port schreiben und dann die Bytes nacheinander in einen separaten E / A-Port schreiben. Auf einer anderen Art von Hardware müsste das Betriebssystem die gesamten 512 Bytes eines Sektors in einen Speicherbereich kopieren, die Position dieses Speicherbereichs in einen speziellen Speicherbereich schreiben und die Platten- und Sektornummer in einen weiteren schreiben spezieller Speicherort.


Die heutige High-End-Hardware ist äußerst kompliziert. Die Handbücher mit allen Programmierdetails sind Türstopper mit Tausenden von Seiten. Das neueste Handbuch für Intel-CPUs umfasst beispielsweise sieben Bände mit insgesamt über 4000 Seiten - und das nur für die CPU. Die meisten anderen Komponenten stellen Speicherblöcke oder E / A-Ports zur Verfügung, die das Betriebssystem der CPU anweisen kann, Adressen in ihrem Adressraum zuzuordnen. Einige dieser Komponenten stellen noch mehr Dinge hinter ein paar E / A-Ports oder Speicheradressen zur Verfügung. Beispiel: Die Echtzeituhr (Real Time Clock, die Komponente, die die Zeit des Computers im ausgeschalteten Zustand festhält) stellt einige hundert Byte Speicher hinter zwei E / A-Anschlüssen zur Verfügung der ursprüngliche PC / AT. Dinge wie Festplatten haben ganze separate Prozessoren, mit denen das Betriebssystem über standardisierte Befehle spricht. GPUs sind noch komplizierter.

Mehrere Personen in den obigen Kommentaren schlugen den Arduino vor. Ich stimme ihnen zu, es ist viel einfacher zu verstehen - der ATmega328, der alles auf dem Arduino Uno macht, außer den USB-Anschluss als seriellen Anschluss freizulegen, hat ein Handbuch mit nur wenigen hundert Seiten. Auf dem Arduino laufen Sie direkt auf der Hardware, ohne dazwischen befindliches Betriebssystem. nur ein paar kleine Bibliotheksroutinen, die Sie nicht verwenden müssen, wenn Sie nicht möchten.

CesarB
quelle
1

Lauffähige Beispiele

Technisch gesehen ist ein Programm, das ohne Betriebssystem läuft, ein Betriebssystem. Schauen wir uns also an, wie man ein paar winzige Hallo-Welt-Betriebssysteme erstellt und ausführt.

Der Code aller folgenden Beispiele ist auf diesem GitHub-Repo vorhanden .

Bootsektor

Auf x86 können Sie am einfachsten und niedrigsten einen Master- Bootsektor (MBR) erstellen , der eine Art Bootsektor darstellt , und diesen dann auf einer Festplatte installieren.

Hier erstellen wir eine mit einem einzigen printfAufruf:

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Ergebnis:

Bildbeschreibung hier eingeben

Getestet unter Ubuntu 18.04, QEMU 2.11.1.

main.img enthält folgendes:

  • \364in oktal == 0xf4in hex: die Kodierung für einen hltBefehl, der die CPU auffordert, nicht mehr zu arbeiten.

    Deshalb wird unser Programm nichts tun: nur starten und stoppen.

    Wir verwenden Oktal, da \xHexadezimalzahlen von POSIX nicht angegeben werden.

    Wir könnten diese Kodierung leicht erhalten mit:

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    Die 0xf4Kodierung ist aber natürlich auch im Intel-Handbuch dokumentiert.

  • %509sproduzieren 509 Plätze. Muss die Datei bis zum Byte 510 ausfüllen.

  • \125\252in oktal == 0x55gefolgt von 0xaa: magischen Bytes, die von der Hardware benötigt werden. Sie müssen die Bytes 511 und 512 sein.

    Ist dies nicht der Fall, wird dies von der Hardware nicht als startfähige Festplatte behandelt.

Beachten Sie, dass auch ohne etwas zu tun, einige Zeichen bereits auf dem Bildschirm gedruckt sind. Diese werden von der Firmware ausgedruckt und dienen zur Identifikation des Systems.

Laufen auf echter Hardware

Emulatoren machen Spaß, aber Hardware ist das einzig Wahre.

Beachten Sie jedoch, dass dies gefährlich ist und Sie Ihre Festplatte versehentlich löschen können: Tun Sie dies nur auf alten Computern, die keine kritischen Daten enthalten! Oder noch besser, Devboards wie der Raspberry Pi, siehe das ARM-Beispiel unten.

Für einen typischen Laptop müssen Sie Folgendes tun:

  • Brennen Sie das Image auf einen USB-Stick (zerstört Ihre Daten!):

    sudo dd if=main.img of=/dev/sdX
    
  • Stecken Sie den USB an einen Computer

  • Mach es an

  • Sagen Sie ihm, er soll von USB booten.

    Dies bedeutet, dass die Firmware USB vor der Festplatte auswählt.

    Wenn dies nicht das Standardverhalten Ihres Computers ist, drücken Sie nach dem Einschalten die Eingabetaste, F12, ESC oder andere ungewöhnliche Tasten, bis Sie ein Startmenü erhalten, in dem Sie auswählen können, ob Sie vom USB-Stick starten möchten.

    In diesen Menüs kann häufig die Suchreihenfolge konfiguriert werden.

Auf meinem alten Lenovo Thinkpad T430, UEFI BIOS 1.16, sehe ich beispielsweise Folgendes:

Hallo Welt

Nachdem wir ein minimales Programm erstellt haben, begeben wir uns in eine hallo Welt.

Die offensichtliche Frage ist: Wie mache ich IO? Ein paar Möglichkeiten:

  • Bitten Sie die Firmware, z. B. BIOS oder UEFI, dies für uns zu tun
  • VGA: Spezieller Speicherbereich, der beim Schreiben auf den Bildschirm gedruckt wird. Kann im geschützten Modus verwendet werden.
  • Schreiben Sie einen Treiber und sprechen Sie direkt mit der Display-Hardware. Dies ist der "richtige" Weg, dies zu tun: leistungsstärker, aber komplexer.
  • serielle Schnittstelle . Dies ist ein sehr einfaches standardisiertes Protokoll, das Zeichen von einem Host-Terminal sendet und abruft.

    Quelle .

    Es ist leider nicht auf den meisten modernen Laptops ausgesetzt, aber es ist der übliche Weg für Entwicklungsboards, siehe die ARM-Beispiele unten.

    Das ist wirklich schade, da solche Schnittstellen zum Beispiel für das Debuggen des Linux-Kernels sehr nützlich sind .

  • Verwenden Sie Debug-Funktionen von Chips. ARM nennt sie zum Beispiel Semihosting . Bei echter Hardware sind einige zusätzliche Hardware- und Softwareunterstützungen erforderlich, bei Emulatoren kann dies jedoch eine kostenlose, praktische Option sein. Beispiel .

Hier machen wir ein BIOS-Beispiel, da es auf x86 einfacher ist. Beachten Sie jedoch, dass dies nicht die robusteste Methode ist.

Netz

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Zusammenstellen und verknüpfen mit:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

Ergebnis:

Bildbeschreibung hier eingeben

Getestet auf: Lenovo Thinkpad T430, UEFI BIOS 1.16. Die Festplatte wurde auf einem Ubuntu 18.04-Host erstellt.

Neben den Standardanweisungen für die Userland-Montage haben wir:

  • .code16: Weist GAS an, 16-Bit-Code auszugeben

  • cli: Software-Interrupts deaktivieren. Diese könnten dazu führen, dass der Prozessor nach dem erneut gestartet wirdhlt

  • int $0x10: führt einen BIOS-Aufruf durch. Dies ist, was die Zeichen eins nach dem anderen druckt.

Die wichtigen Link-Flags sind:

  • --oformat binary: Gebe rohen binären Assembler-Code aus. Verwerfe ihn nicht in einer ELF-Datei, wie dies bei normalen Userland-Programmen der Fall ist.

Verwenden Sie C anstelle von Assembly

Da C zu Assembly kompiliert wird und die Verwendung von C ohne die Standardbibliothek ziemlich einfach ist, benötigen Sie im Grunde nur Folgendes:

  • Ein Linker-Skript, um Dinge an der richtigen Stelle in Erinnerung zu rufen
  • Flags, die GCC anweisen, die Standardbibliothek nicht zu verwenden
  • Ein winziger Assembly-Einstiegspunkt, der den erforderlichen C-Status für mainFolgendes festlegt :
    • der Stapel
    • Null aus BSS

TODO: so ein x86 Beispiel auf GitHub verlinken. Hier ist ein ARM, den ich erstellt habe .

Es macht jedoch mehr Spaß, wenn Sie die Standardbibliothek verwenden möchten, da wir keinen Linux-Kernel haben, der einen Großteil der Funktionen der C-Standardbibliothek über POSIX implementiert .

Einige Möglichkeiten, ohne auf ein vollwertiges Betriebssystem wie Linux umzusteigen, sind:

  • Newlib

    Ausführliches Beispiel unter: https://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931

    In Newlib müssen Sie die Syscalls selbst implementieren, aber Sie erhalten ein sehr minimales System, und es ist sehr einfach, sie zu implementieren.

    Beispielsweise könnten Sie printfzu den UART- oder ARM-Systemen umleiten oder exit()mit Semihosting implementieren .

  • Embedded-Betriebssysteme wie FreeRTOS und Zephyr .

    Mit solchen Betriebssystemen können Sie in der Regel die präventive Zeitplanung deaktivieren und so die vollständige Kontrolle über die Laufzeit des Programms behalten.

    Sie können als eine Art vorimplementierte Newlib angesehen werden.

ARM

In ARM sind die allgemeinen Vorstellungen dieselben. Ich habe hochgeladen:

Für den Raspberry Pi scheint https://github.com/dwelch67/raspberrypi das beliebteste Tutorial zu sein, das derzeit erhältlich ist.

Einige Unterschiede zu x86 sind:

  • Die Eingabe erfolgt durch direktes Schreiben an magische Adressen, es gibt keine inund outAnweisungen.

    Dies wird als speicherabgebildete E / A bezeichnet .

  • Für echte Hardware wie den Raspberry Pi können Sie die Firmware (BIOS) selbst zum Festplatten-Image hinzufügen.

    Das ist eine gute Sache, da es die Aktualisierung dieser Firmware transparenter macht.

Firmware

In Wahrheit ist Ihr Bootsektor nicht die erste Software, die auf der CPU des Systems ausgeführt wird.

Was tatsächlich zuerst ausgeführt wird, ist die sogenannte Firmware , bei der es sich um eine Software handelt:

  • von den Hardware-Herstellern gemacht
  • Typischerweise geschlossene Quelle, aber wahrscheinlich C-basiert
  • im Nur-Lese-Speicher gespeichert und daher ohne Zustimmung des Anbieters schwerer / unmöglich zu ändern.

Bekannte Firmwares sind:

  • BIOS : alte allgegenwärtige x86-Firmware. SeaBIOS ist die von QEMU standardmäßig verwendete Open Source-Implementierung.
  • UEFI : BIOS-Nachfolger, besser standardisiert, aber leistungsfähiger und unglaublich aufgebläht.
  • Coreboot : der edle Cross Arch Open Source Versuch

Die Firmware macht Dinge wie:

  • Durchlaufen Sie jede Festplatte, jedes USB-Gerät, jedes Netzwerk usw., bis Sie etwas finden, das gebootet werden kann.

    Wenn wir QEMU ausführen, -hdaheißt das, dass main.imgeine Festplatte an die Hardware angeschlossen ist, und

    hda ist der erste, der ausprobiert wird, und er wird verwendet.

  • Laden Sie die ersten 512 Bytes in die RAM-Speicheradresse 0x7c00, platzieren Sie den RIP der CPU dort und lassen Sie ihn laufen

  • Zeigen Sie Dinge wie das Boot-Menü oder BIOS-Druckaufrufe auf dem Display an

Firmware bietet betriebssystemähnliche Funktionen, von denen die meisten Betriebssysteme abhängen. Beispielsweise wurde eine Python-Teilmenge für die Ausführung unter BIOS / UEFI portiert: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Es kann argumentiert werden, dass Firmwares nicht von Betriebssystemen zu unterscheiden sind und dass Firmware die einzige "echte" Bare-Metal-Programmierung ist, die man durchführen kann.

Wie dieser CoreOS-Entwickler es ausdrückt :

Der schwierige Teil

Wenn Sie einen PC einschalten, werden die Chips, aus denen der Chipsatz besteht (Northbridge, Southbridge und SuperIO), noch nicht ordnungsgemäß initialisiert. Obwohl das BIOS-ROM so weit wie möglich von der CPU entfernt ist, kann die CPU darauf zugreifen, da dies erforderlich ist, da die CPU sonst keine Anweisungen zur Ausführung hätte. Dies bedeutet nicht, dass das BIOS-ROM vollständig zugeordnet ist, normalerweise nicht. Es ist jedoch gerade genug zugeordnet, um den Startvorgang in Gang zu setzen. Alle anderen Geräte, vergiss es einfach.

Wenn Sie Coreboot unter QEMU ausführen, können Sie mit den höheren Schichten von Coreboot und mit den Nutzdaten experimentieren, QEMU bietet jedoch kaum Gelegenheit, mit dem Startcode auf niedriger Ebene zu experimentieren. Zum einen funktioniert RAM von Anfang an.

Post-BIOS-Ausgangszustand

Wie viele Dinge in der Hardware ist die Standardisierung schwach, und eines der Dinge, auf die Sie sich nicht verlassen sollten, ist der Anfangszustand der Register, wenn Ihr Code nach dem BIOS ausgeführt wird.

Tun Sie sich selbst einen Gefallen und verwenden Sie einen Initialisierungscode wie den folgenden: https://stackoverflow.com/a/32509555/895245

Register mögen %dsund %eshaben wichtige Nebenwirkungen, daher sollten Sie sie auf Null setzen, auch wenn Sie sie nicht explizit verwenden.

Beachten Sie, dass einige Emulatoren besser sind als echte Hardware und Ihnen einen schönen Anfangszustand geben. Wenn Sie dann auf echter Hardware laufen, geht alles kaputt.

GNU GRUB Multiboot

Bootsektoren sind einfach, aber nicht sehr praktisch:

  • Sie können nur ein Betriebssystem pro Festplatte haben
  • Der Ladecode muss wirklich klein sein und in 512 Bytes passen. Dies könnte mit dem int 0x13 BIOS-Aufruf behoben werden .
  • Sie müssen viel selbst starten, beispielsweise in den geschützten Modus

Aus diesen Gründen hat GNU GRUB ein praktischeres Dateiformat namens Multiboot erstellt.

Minimales Arbeitsbeispiel: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Ich benutze es auch in meinem GitHub-Beispielrepo , um alle Beispiele problemlos auf echter Hardware ausführen zu können, ohne den USB-Stick millionenfach zu brennen. Auf QEMU sieht es so aus:

Bildbeschreibung hier eingeben

Wenn Sie Ihr Betriebssystem als Multiboot-Datei vorbereiten, kann GRUB es in einem regulären Dateisystem finden.

Dies ist, was die meisten Distributionen tun, indem sie OS-Images unterbringen /boot.

Multiboot-Dateien sind im Grunde eine ELF-Datei mit einem speziellen Header. Sie werden von GRUB unter https://www.gnu.org/software/grub/manual/multiboot/multiboot.html angegeben

Sie können eine Multiboot-Datei mit in eine bootfähige Disk verwandeln grub-mkrescue.

El Torito

Format, das auf CDs gebrannt werden kann: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Es ist auch möglich, ein Hybrid-Image zu erstellen, das entweder auf ISO oder USB funktioniert. Dies kann mit grub-mkrescue( Beispiel ) erfolgen und wird auch vom Linux-Kernel bei der make isoimageVerwendung durchgeführt isohybrid.

Ressourcen

Ciro Santilli ist ein Schauspieler
quelle