| Zeile 6: | Zeile 6: | ||
Wie auch immer. Ich freue mich, dass an dieser Ausgabe sehr viele Leute (außer mir und Phil) mitgearbeitet haben und ich auch sonst viel Feedback erhalte. Da wäre zum Beispiel Lizer, der drei hervorragende Tutorials geschrieben hat (in dieser Ausgabe sind zwei davon abgedruckt), aber auch Icy Wolf, der jetzt parallel zu Phil seine Kommentare abgibt und Neo3, der nun für die News zuständig ist. | Wie auch immer. Ich freue mich, dass an dieser Ausgabe sehr viele Leute (außer mir und Phil) mitgearbeitet haben und ich auch sonst viel Feedback erhalte. Da wäre zum Beispiel Lizer, der drei hervorragende Tutorials geschrieben hat (in dieser Ausgabe sind zwei davon abgedruckt), aber auch Icy Wolf, der jetzt parallel zu Phil seine Kommentare abgibt und Neo3, der nun für die News zuständig ist. | ||
| − | Achja, bevor ich es vergesse: ein riesen Dankeschön an jaboe, der Tim Robinsons Artikel über Kernel-Entwicklung in C übersetzt hat! | + | Achja, bevor ich es vergesse: ein riesen Dankeschön an jaboe, der Tim Robinsons Artikel über [[Kernel]]-Entwicklung in [[C]] übersetzt hat! |
== News == | == News == | ||
=== Über mich === | === Über mich === | ||
| − | Ich bin 19 Jahre alt und habe schon meine erste Anlehre als Elektroausrüster bestanden, neben dem habe ich ein großes Interesse an Elektronik und PC-Programmierung. Ich habe mit 13 Jahren angefangen, HTML-Web-Seiten zu basteln. Nach zwei Jahren suchte ich mir aber schon eine neue Herausforderung und begann mit VB 6. Als MS dann auf VB.net umzog, ging ich natürlich mit, zwischendurch hab ich mich aber auch mit anderen Programmiersprachen beschäftigt, wie Pascal und C++, blieb dann doch bei VB.net. | + | Ich bin 19 Jahre alt und habe schon meine erste Anlehre als Elektroausrüster bestanden, neben dem habe ich ein großes Interesse an Elektronik und PC-Programmierung. Ich habe mit 13 Jahren angefangen, HTML-Web-Seiten zu basteln. Nach zwei Jahren suchte ich mir aber schon eine neue Herausforderung und begann mit VB 6. Als MS dann auf VB.net umzog, ging ich natürlich mit, zwischendurch hab ich mich aber auch mit anderen Programmiersprachen beschäftigt, wie [[Pascal]] und [[C++]], blieb dann doch bei VB.net. |
| − | Mich hat das OS-Programmieren eigentlich erst so richtig gepackt vor etwa einem Monat und da ich von einem Kollegen (der eine Elektroniker-Lehre macht) etwas von Assembler gehört habe und auch erfahren habe, das man damit CPUs programmiert, hab ich mich auf die suche im Internet gemacht nach OS-Programmierung mit ASM, fand dann auch einige Themen in Foren darüber und erfuhr, dass ich zuerst einen Bootloader in ASM proggen muss, damit ich da drauf aufbauen kann mit dem Kernel. Und bin schlussendlich hier bei Lowlevel gelandet. | + | Mich hat das OS-Programmieren eigentlich erst so richtig gepackt vor etwa einem Monat und da ich von einem Kollegen (der eine Elektroniker-Lehre macht) etwas von [[Assembler]] gehört habe und auch erfahren habe, das man damit CPUs programmiert, hab ich mich auf die suche im Internet gemacht nach OS-Programmierung mit ASM, fand dann auch einige Themen in Foren darüber und erfuhr, dass ich zuerst einen Bootloader in ASM proggen muss, damit ich da drauf aufbauen kann mit dem Kernel. Und bin schlussendlich hier bei Lowlevel gelandet. |
=== Windows Longhorn === | === Windows Longhorn === | ||
| Zeile 24: | Zeile 24: | ||
Informiert euch selbst darüber, ist alles auf Deutsch ;-) | Informiert euch selbst darüber, ist alles auf Deutsch ;-) | ||
| − | == Kernel-Entwicklung in C == | + | == [[Kernel]]-Entwicklung in [[C]] == |
| − | Nun gut - eure einzige Erfahrung mit dem Programmieren von Betriebssystemen dürfte das Schreiben eines | + | Nun gut - eure einzige Erfahrung mit dem Programmieren von Betriebssystemen dürfte das Schreiben eines [[Bootloader]]s in [[Assembler]] sein. Wenn ihr ihn von Grund auf geschrieben habt, dann dürfte es einige Wochen gedauert haben (mindestens), und ihr wünschtet, es gäbe einen einfacheren Weg. Nun: hier ist er, besonders wenn ihr bereits mit der [[C]]-Programmiersprache vertraut seit. Wenn ihr noch nicht mit [[C]] vertraut sein solltet (und ihr aber eine andere Highlevel-Sprache beherrscht), lernt es, denn es ist es wert, weil es einfach ist, euren [[Kernel]] in [[C]] zu programmieren. Wichtig ist vor allem, die richtigen Informationen zu bekommen und die richtigen Tools zu haben. |
| − | === Die Programmiersprache C === | + | === Die Programmiersprache [[C]] === |
| − | C wurde eigentlich als Low-Level-HLL konzipiert (Unix- | + | [[C]] wurde eigentlich als Low-Level-HLL konzipiert (Unix-[[Kernel]]s wurden traditionell in [[C]] geschrieben) und seine Hauptvorteile waren seine Portabilität und seine Nähe zur Maschine. Es ist einfach, nicht-portable Programme in [[C]] zu schreiben, aber mit etwas Wissen ausgestattet, ist es möglich, mit Portabilität im Hintergedanken zu programmieren. Es gibt nichts, was kompiliertem [[C]]-Code von [[Assembler]] unterscheidet - in Wahrheit ist euer Assembler nicht mehr als ein Low-Level-Compiler, einer, der die Angaben direkt in maschinenlesbare Opcodes übersetzt. Wie jede andere High-Level-Sprache auch, abstrahiert [[C]] den Code mehr und trennt euch von der Maschine; dennoch beschränkt euch [[C]] nicht zu sehr bei dem Code, den ihr programmiert. |
=== Tools === | === Tools === | ||
| − | Es gibt zwei Hauptkomponenten, die in die Erzeugung von maschinenlesbarem Code aus C-Quelltext eingebunden sind: ein Compiler und ein Linker. Der Compiler macht die meiste Arbeit und ist verantwortlich für die Verwandlung von C-Quellcode in Objektdateien - das sind die Dateien, die Maschinencode und Daten enthalten, aber für sich selbst kein vollständiges Programm darstellen. Viele C-Compiler besitzen die Möglichkeit, direkt aus dem Quelltext eine ausführbare Datei zu erzeugen, in diesem Fall ruft der Compiler den Linker intern auf. Der Linker bündelt alle Objektdateien, setzt die Referenzen, die sie sich gegenseitig zuweisen, leitet sie um (relocation) und erzeugt eine ausführbare Datei. | + | Es gibt zwei Hauptkomponenten, die in die Erzeugung von maschinenlesbarem Code aus [[C]]-Quelltext eingebunden sind: ein Compiler und ein Linker. Der Compiler macht die meiste Arbeit und ist verantwortlich für die Verwandlung von [[C]]-Quellcode in Objektdateien - das sind die Dateien, die Maschinencode und Daten enthalten, aber für sich selbst kein vollständiges Programm darstellen. Viele [[C]]-Compiler besitzen die Möglichkeit, direkt aus dem Quelltext eine ausführbare Datei zu erzeugen, in diesem Fall ruft der Compiler den Linker intern auf. Der Linker bündelt alle Objektdateien, setzt die Referenzen, die sie sich gegenseitig zuweisen, leitet sie um (relocation) und erzeugt eine ausführbare Datei. |
| − | Die meisten Linker arbeiten mit den Quelldateien von jedem Compiler, solange dieser kompatible Objektdateien erzeugt. Das erlaubt euch zum Beispiel, das meiste von eurem Kernel in C, aber einige maschinenabhängige Teile in Assembler zu schreiben. | + | Die meisten Linker arbeiten mit den Quelldateien von jedem Compiler, solange dieser kompatible Objektdateien erzeugt. Das erlaubt euch zum Beispiel, das meiste von eurem [[Kernel]] in [[C]], aber einige maschinenabhängige Teile in [[Assembler]] zu schreiben. |
Zum Entwickeln würde ich irgendein Paket empfehlen, welches den GNU GCC-Compiler und den ld-Linker enthält, denn: | Zum Entwickeln würde ich irgendein Paket empfehlen, welches den GNU GCC-Compiler und den ld-Linker enthält, denn: | ||
| Zeile 48: | Zeile 48: | ||
Unix ELF und Windows PE neigen dazu, am populärsten unter den Betriebssystementwicklern zu werden, hauptsächlich wegen der guten Unterstützung der beiden durch die verfügbaren Linker. ELF scheint mehr in Amateur-Kerneln verwendet, hauptsächlich wegen seiner Einfachheit, obwohl PE leistungsfähiger (und infolgedessen komplexer) ist. Beide sind sehr gut dokumentiert. COFF wird ebenfalls in einigen Projekten verwandt, obwohl ihm die grundlegende Unterstützung von Eigenschaften wie dynamisches Linken fehlt. Eine Alternative für einfache Kernels ist das flache Binärformat (das heißt kein wirkliches Format). In diesem Fall schreibt der Linker rohen Code und Daten in die Ausgabedatei, und das Ergebnis ist ähnlich dem Format der COM-Dateien in MS-DOS, obwohl die resultierende Datei größer sein kann als 64kB und 32-Bit Opcodes verwenden kann (vorausgesetzt, der Loader aktiviert vorher den Protected-Mode). | Unix ELF und Windows PE neigen dazu, am populärsten unter den Betriebssystementwicklern zu werden, hauptsächlich wegen der guten Unterstützung der beiden durch die verfügbaren Linker. ELF scheint mehr in Amateur-Kerneln verwendet, hauptsächlich wegen seiner Einfachheit, obwohl PE leistungsfähiger (und infolgedessen komplexer) ist. Beide sind sehr gut dokumentiert. COFF wird ebenfalls in einigen Projekten verwandt, obwohl ihm die grundlegende Unterstützung von Eigenschaften wie dynamisches Linken fehlt. Eine Alternative für einfache Kernels ist das flache Binärformat (das heißt kein wirkliches Format). In diesem Fall schreibt der Linker rohen Code und Daten in die Ausgabedatei, und das Ergebnis ist ähnlich dem Format der COM-Dateien in MS-DOS, obwohl die resultierende Datei größer sein kann als 64kB und 32-Bit Opcodes verwenden kann (vorausgesetzt, der Loader aktiviert vorher den Protected-Mode). | ||
| − | Ein Nachteil sämtlicher etablierter IA-32-Compiler ist, dass sie von einem flachen 32-Bit-Adressraum ausgehen, in dem CS, DS, ES und SS eine gemeinsame Basisadresse haben. Da sie keine far-Pointer (48-Bit seg16:ofs32) benutzen, erschweren sie das Schreiben von Code für ein segmentiertes Betriebssystem. Programme in einem segmentierten Betriebssystem, welches in C geschrieben wurde, sind auf sehr viel Assemblercode angewiesen und wären sehr viel weniger effizient als Programme, die für ein "flaches" Betriebssystem geschrieben worden sind. Nun, während des Schreibens, stellte Watcom in Aussicht, ihren Watcom-Compiler zu veröffentlichen, der angeblich far-Pointer im 32-Bit Protected Mode unterstützen würde. | + | Ein Nachteil sämtlicher etablierter IA-32-Compiler ist, dass sie von einem flachen 32-Bit-Adressraum ausgehen, in dem CS, DS, ES und SS eine gemeinsame Basisadresse haben. Da sie keine far-Pointer (48-Bit seg16:ofs32) benutzen, erschweren sie das Schreiben von Code für ein segmentiertes Betriebssystem. Programme in einem segmentierten Betriebssystem, welches in [[C]] geschrieben wurde, sind auf sehr viel Assemblercode angewiesen und wären sehr viel weniger effizient als Programme, die für ein "flaches" Betriebssystem geschrieben worden sind. Nun, während des Schreibens, stellte Watcom in Aussicht, ihren Watcom-Compiler zu veröffentlichen, der angeblich far-Pointer im 32-Bit Protected Mode unterstützen würde. |
Hier sind einige weitere Warnungen zum Gebrauch von gcc und ld (obwohl das potentiell für jeden anderen Compiler / Linker zutrifft). Erstens setzt gcc gern die Stringliterale (Konstanten vom Stringtyp), die von Funktionen genutzt werden, einfach vor den Funktionscode. Normalerweise ist das kein Problem, aber ein paar Leute verheddern sich bei dem Versuch ihre Kernel "Hello, world" ausgeben zu lassen. Beachtet dieses Beispiel: | Hier sind einige weitere Warnungen zum Gebrauch von gcc und ld (obwohl das potentiell für jeden anderen Compiler / Linker zutrifft). Erstens setzt gcc gern die Stringliterale (Konstanten vom Stringtyp), die von Funktionen genutzt werden, einfach vor den Funktionscode. Normalerweise ist das kein Problem, aber ein paar Leute verheddern sich bei dem Versuch ihre Kernel "Hello, world" ausgeben zu lassen. Beachtet dieses Beispiel: | ||
| Zeile 73: | Zeile 73: | ||
=== Die Laufzeitbibliothek === | === Die Laufzeitbibliothek === | ||
| − | Ein Hauptbestandteil beim Programmieren eines Betriebssystems ist das Neuschreiben der Laufzeitbibliothek, auch bekannt als libc. Das liegt darin begründet, dass die Laufzeitbibliothek der am meisten betriebsystemabhängige Teil des Compilerpaketes ist: die C-Laufzeitbibliothek stellt genug Funktionalität bereit, die es euch ermöglicht, portierbare Programme zu schreiben, die inneren Abläufe jedoch sind abhängig von dem eingesetzten Betriebssystem. Tatsächlich benutzen Compileranbieter oft unterschiedliche Laufzeitbibliotheken für das selbe Betriebssystem: Microsoft Visual C++ stellt verschiedene Bibliotheken für die verschiedenen Kombinationen aus debug/multi-threated/DLL bereit, und die älteren MS-DOS-Compiler boten Laufzeitbibliotheken für bis zu sechs unterschiedliche Speichermodelle an. | + | Ein Hauptbestandteil beim Programmieren eines Betriebssystems ist das Neuschreiben der Laufzeitbibliothek, auch bekannt als libc. Das liegt darin begründet, dass die Laufzeitbibliothek der am meisten betriebsystemabhängige Teil des Compilerpaketes ist: die [[C]]-Laufzeitbibliothek stellt genug Funktionalität bereit, die es euch ermöglicht, portierbare Programme zu schreiben, die inneren Abläufe jedoch sind abhängig von dem eingesetzten Betriebssystem. Tatsächlich benutzen Compileranbieter oft unterschiedliche Laufzeitbibliotheken für das selbe Betriebssystem: Microsoft Visual C++ stellt verschiedene Bibliotheken für die verschiedenen Kombinationen aus debug/multi-threated/DLL bereit, und die älteren MS-DOS-Compiler boten Laufzeitbibliotheken für bis zu sechs unterschiedliche Speichermodelle an. |
Bis auf weiteres sollte es ausreichend sein, nur eine Laufzeitbibliothek zu schreiben (obwohl das Schreiben einer Makefile, die eine Auswahl an dynamischem oder statischem Linken anbietet oft nützlich ist). Ihr solltet euch zum Ziel setzen, die Bibliothek so zu replizieren, wie es nach dem ISO-C-Standart vorgegeben ist, denn es wird die Portierung von Programmen auf euer Betriebssystem einfacher machen. | Bis auf weiteres sollte es ausreichend sein, nur eine Laufzeitbibliothek zu schreiben (obwohl das Schreiben einer Makefile, die eine Auswahl an dynamischem oder statischem Linken anbietet oft nützlich ist). Ihr solltet euch zum Ziel setzen, die Bibliothek so zu replizieren, wie es nach dem ISO-C-Standart vorgegeben ist, denn es wird die Portierung von Programmen auf euer Betriebssystem einfacher machen. | ||
| − | Es macht es wesentlich einfacher, Code für Bibliotheken zu schreiben, wenn ihr Quelltext von bestehenden Implementierungen in die Hände bekommt, besonders wenn die Umgebung der Bibliothek vergleichbar mit der ist, in der ihr entwickelt. Es gibt viele C-Funktionen, welche betriebssystem- und plattformabhängig sind: zum Beispiel kann das meiste der string.h und wchar.h direkt kopiert werden. Umgekehrt gibt es viele Funktionen, die euer Compiler anbieten kann, die aber keinen Sinn für euer Betriebssystem haben: viele DOS-Compiler bieten eine bios.h-Headerdatei an, die den Zugriff auf das PC-BIOS ermöglicht. Solange ihr keinen VM86-Monitor in euren Kernel implementiert, werdet ihr nicht in der Lage sein, BIOS-Funktionen direkt aufzurufen. Allerdings sind solche Funktionen Erweiterungen und haben als solche Namen mit einem Unterstrich (z.B. _biosdisk()) am Anfang. Ihr habt nicht die Verpflichtung, diese Erweiterungen zu implementieren, und ihr könnt eure eigenen Erweiterungen festlegen, wenn ihr wollt, solange ihr ihnen einen Namen mit Unterstrich am Anfang gebt. | + | Es macht es wesentlich einfacher, Code für Bibliotheken zu schreiben, wenn ihr Quelltext von bestehenden Implementierungen in die Hände bekommt, besonders wenn die Umgebung der Bibliothek vergleichbar mit der ist, in der ihr entwickelt. Es gibt viele [[C]]-Funktionen, welche betriebssystem- und plattformabhängig sind: zum Beispiel kann das meiste der string.h und wchar.h direkt kopiert werden. Umgekehrt gibt es viele Funktionen, die euer Compiler anbieten kann, die aber keinen Sinn für euer Betriebssystem haben: viele DOS-Compiler bieten eine bios.h-Headerdatei an, die den Zugriff auf das PC-BIOS ermöglicht. Solange ihr keinen VM86-Monitor in euren Kernel implementiert, werdet ihr nicht in der Lage sein, BIOS-Funktionen direkt aufzurufen. Allerdings sind solche Funktionen Erweiterungen und haben als solche Namen mit einem Unterstrich (z.B. _biosdisk()) am Anfang. Ihr habt nicht die Verpflichtung, diese Erweiterungen zu implementieren, und ihr könnt eure eigenen Erweiterungen festlegen, wenn ihr wollt, solange ihr ihnen einen Namen mit Unterstrich am Anfang gebt. |
Andere Funktionen der C-Bibliothek sind abhängig von der Unterstützung durch den Kernel: das meiste der stdio.h ist auf das Vorhandensein mehrer Arten von Dateisystemen angewiesen, selbst wenn printf() eine Zieladresse für die Ausgabe benötigt. Da wir gerade bei printf() sind: die meisten Implementierungen von OpenSource-C-Bibliotheken definieren eine generische printf()-Engine, da die selbe Funktionalität an vielen Stellen benötigt wird (printf(),fprintf(), sprintf() und den v-,w-, und vw-Versionen dieser Funktionen). Ihr solltet in der Lage sein, diese allgemeine Engine zu herauszufiltern und sie für eure Eigene zu verwenden, oder zumindest versuchen, sie zu emulieren: schreibt eine Funktion, die einen Formatstring akzeptiert und eine Liste mit Argumenten, die ihre Ausgabe an ein abstraktes Interface weitergibt (entweder eine Funktion oder eine gepufferten Stream). | Andere Funktionen der C-Bibliothek sind abhängig von der Unterstützung durch den Kernel: das meiste der stdio.h ist auf das Vorhandensein mehrer Arten von Dateisystemen angewiesen, selbst wenn printf() eine Zieladresse für die Ausgabe benötigt. Da wir gerade bei printf() sind: die meisten Implementierungen von OpenSource-C-Bibliotheken definieren eine generische printf()-Engine, da die selbe Funktionalität an vielen Stellen benötigt wird (printf(),fprintf(), sprintf() und den v-,w-, und vw-Versionen dieser Funktionen). Ihr solltet in der Lage sein, diese allgemeine Engine zu herauszufiltern und sie für eure Eigene zu verwenden, oder zumindest versuchen, sie zu emulieren: schreibt eine Funktion, die einen Formatstring akzeptiert und eine Liste mit Argumenten, die ihre Ausgabe an ein abstraktes Interface weitergibt (entweder eine Funktion oder eine gepufferten Stream). | ||
| Zeile 84: | Zeile 84: | ||
=== OS-spezifische Unterstützung === | === OS-spezifische Unterstützung === | ||
| − | C ist als Kernelsprache nicht perfekt: offensichtlich gibt es keine standardisierte Möglichkeit, den Zugriff auf die Funktionen einer Maschine zu erlauben. Das bedeutet, dass es oft notwendig ist zu Inline-Assembler zurückzugreifen, oder Teile in Assembler zu programmieren und sie mit der Compilerausgabe zur Linkzeit zu linken. Inline-Assembler ermöglicht es euch, Teile von C-Funktionen in Assembler zu schreiben und wie gewohnt auf die Variablen von C-Funktionen zuzugreifen: der Code, den ihr schreibt, wird in den Code, den der Compiler generiert, eingefügt. Die Unterstützung von Inlineassembler variiert unter verschiedenen Compilern, wenn ihr die AT+T-Syntax durchhaltet, ist gcc der Beste unter den PC-Compilern. Obwohl Visual C++ und Borland C++ beide eine vertrautere Intel-Syntax verwenden, sind ihre Inlineassembler nicht genauso stark integriert mit dem Rest der Compiler. Gcc zwingt euch zu einer esoterischen Syntax, aber erlaubt es euch, jegliche C-Ausdrücke als eine Eingabe an einen Assemblerblock zu verwenden und die Ausgaben überall zu platzieren. | + | [[C]] ist als Kernelsprache nicht perfekt: offensichtlich gibt es keine standardisierte Möglichkeit, den Zugriff auf die Funktionen einer Maschine zu erlauben. Das bedeutet, dass es oft notwendig ist zu Inline-Assembler zurückzugreifen, oder Teile in Assembler zu programmieren und sie mit der Compilerausgabe zur Linkzeit zu linken. Inline-Assembler ermöglicht es euch, Teile von [[C]]-Funktionen in [[Assembler]] zu schreiben und wie gewohnt auf die Variablen von [[C-Funktionen]] zuzugreifen: der Code, den ihr schreibt, wird in den Code, den der Compiler generiert, eingefügt. Die Unterstützung von Inlineassembler variiert unter verschiedenen Compilern, wenn ihr die AT+T-Syntax durchhaltet, ist gcc der Beste unter den PC-Compilern. Obwohl Visual C++ und Borland C++ beide eine vertrautere Intel-Syntax verwenden, sind ihre Inlineassembler nicht genauso stark integriert mit dem Rest der Compiler. Gcc zwingt euch zu einer esoterischen Syntax, aber erlaubt es euch, jegliche C-Ausdrücke als eine Eingabe an einen Assemblerblock zu verwenden und die Ausgaben überall zu platzieren. |
Der resultierende Assembler ist also besser mit dem Optimierer integriert als in den Compilern von Borland und Microsoft: zum Beispiel erlaubt gcc es euch genau anzugeben, welche Register als Resultat eines Assemblerblockes verändert werden. Einiger Assemblercode wird auch im Usermode benötigt, wenn ihr Softwareinterrupts zum Aufrufen von Kernel-Syscall verwendet. Ihr könntet die Interruptaufrufe direkt im Code der C-Bibliothek platzieren (so wie es MS-DOS-Bibliotheken tun),oder eine seperate Bibliothek mit einer Funktion für jeden Aufruf erzeugen und normal darauf verlinken (so wie es Windows NT mit der ntdll.dll macht). Es dürfte auch Sinn machen die betriebssystemabhängige Schnittstelle in eine sprachunabhängige Bibliothek einzufügen, so dass Programme, die in verschiedene Sprachen geschrieben wurden die gleiche Betriebssystembibliothek benutzen können. Das ermöglicht es Leuten, Programme in anderen Sprachen als in C zu schreiben, oder in Sprachen, die keine C-Bindungen erlauben. | Der resultierende Assembler ist also besser mit dem Optimierer integriert als in den Compilern von Borland und Microsoft: zum Beispiel erlaubt gcc es euch genau anzugeben, welche Register als Resultat eines Assemblerblockes verändert werden. Einiger Assemblercode wird auch im Usermode benötigt, wenn ihr Softwareinterrupts zum Aufrufen von Kernel-Syscall verwendet. Ihr könntet die Interruptaufrufe direkt im Code der C-Bibliothek platzieren (so wie es MS-DOS-Bibliotheken tun),oder eine seperate Bibliothek mit einer Funktion für jeden Aufruf erzeugen und normal darauf verlinken (so wie es Windows NT mit der ntdll.dll macht). Es dürfte auch Sinn machen die betriebssystemabhängige Schnittstelle in eine sprachunabhängige Bibliothek einzufügen, so dass Programme, die in verschiedene Sprachen geschrieben wurden die gleiche Betriebssystembibliothek benutzen können. Das ermöglicht es Leuten, Programme in anderen Sprachen als in C zu schreiben, oder in Sprachen, die keine C-Bindungen erlauben. | ||
| − | === C++ im Kernel === | + | === [[C++]] im [[Kernel]] === |
| − | Über die Nutzung von C++ im Kernel variieren die Meinungen: die meisten Linuxkernelprogrammier halten sich davon fern, während einige Leute ganze Betriebssysteme in C++ entwickelt haben. Ich halte mich an C, obwohl ich keinen Grund sehe, warum ihr C++ nicht benutzen solltet, solange ihr wisst, was ihr tut. Eine Sache, über die ihr euch klar sein solltet ist, das ihr zum Schreiben eines C++- | + | Über die Nutzung von [[C++]] im [[Kernel]] variieren die Meinungen: die meisten Linuxkernelprogrammier halten sich davon fern, während einige Leute ganze Betriebssysteme in [[C++]] entwickelt haben. Ich halte mich an [[C]], obwohl ich keinen Grund sehe, warum ihr [[C++]] nicht benutzen solltet, solange ihr wisst, was ihr tut. Eine Sache, über die ihr euch klar sein solltet ist, das ihr zum Schreiben eines [[C++]]-[[Kernel]]s ein bisschen mehr Code für das Gerüst benötigt, und einige [[C++]]-Funktionen sind tabu für euch (solange ihr nicht den benötigten Code für die Unterstüung schreiben könnt). |
Als allererstes werdet ihr die new- und delete-Operatoren programmieren müssen. Das ist einfach, wenn ihr bereits malloc() und free() geschrieben habt: new und delete können Ein-Zeilen-Funktionen sein, die jede von ihnen aufruft. Wenn ihr globale Instanzen von Klassen haben wollt, müsst ihr einigen Code in die Startup-Routine einfügen, um jeden ihrer Konstruktoren aufzurufen. Die Art wie das getan wird, unterscheidet zwischen den Compilern, so dass es das Beste ist, die Laufzeitbibliothek eures Compilers durchzusuchen um zu sehen, wie der Hersteller es gemacht hat; versucht in Dateien mit Namen wie crt0.c zu suchen. Ihr werdet vermutlich atexit() implementieren müssen, da der Compiler wahrscheinlich Code ausgeben wird, der atexit() benutzt, um globale Destruktoren von Objekten aufzurufen, wenn das Programm beendet wird. Wiedereinmal wird es von dem Compiler, den ihr verwendet, abhängen und für welches Betriebssystem auch immer der Compiler ausgelegt ist. | Als allererstes werdet ihr die new- und delete-Operatoren programmieren müssen. Das ist einfach, wenn ihr bereits malloc() und free() geschrieben habt: new und delete können Ein-Zeilen-Funktionen sein, die jede von ihnen aufruft. Wenn ihr globale Instanzen von Klassen haben wollt, müsst ihr einigen Code in die Startup-Routine einfügen, um jeden ihrer Konstruktoren aufzurufen. Die Art wie das getan wird, unterscheidet zwischen den Compilern, so dass es das Beste ist, die Laufzeitbibliothek eures Compilers durchzusuchen um zu sehen, wie der Hersteller es gemacht hat; versucht in Dateien mit Namen wie crt0.c zu suchen. Ihr werdet vermutlich atexit() implementieren müssen, da der Compiler wahrscheinlich Code ausgeben wird, der atexit() benutzt, um globale Destruktoren von Objekten aufzurufen, wenn das Programm beendet wird. Wiedereinmal wird es von dem Compiler, den ihr verwendet, abhängen und für welches Betriebssystem auch immer der Compiler ausgelegt ist. | ||
| Zeile 95: | Zeile 95: | ||
Normalerweise würde ich das Aufräumen von beidem, Exceptionhandling und großen Virtuellen Klassen, im Kernel steuern. Exceptionhandling führt gewöhnlich zu unnötigem Aufblähen, und das Aufrufen von massenhaft virtuellen Funktionen führt zu unnötigen Umleitungen und behindert den Optimierer. Errinert euch, euer Kernel sollte so effizient wie möglich sein, auch zum Nachteil eines schönen Designs, wenn notwendig. | Normalerweise würde ich das Aufräumen von beidem, Exceptionhandling und großen Virtuellen Klassen, im Kernel steuern. Exceptionhandling führt gewöhnlich zu unnötigem Aufblähen, und das Aufrufen von massenhaft virtuellen Funktionen führt zu unnötigen Umleitungen und behindert den Optimierer. Errinert euch, euer Kernel sollte so effizient wie möglich sein, auch zum Nachteil eines schönen Designs, wenn notwendig. | ||
| − | Ein Betriebssystem in einer Sprache wie C zu schreiben, kann wesentlich produktiver sein, als es komplett in Assembler zu programmieren, insbesondere wenn ihr bereit seit auf die geringfügigen Geschwindigkeitsvorteile, die Assembler ermöglicht, zu verzichten. Es sind freie Compiler verfügbar, so wie gcc, die das Schreiben von | + | Ein Betriebssystem in einer Sprache wie [[C]] zu schreiben, kann wesentlich produktiver sein, als es komplett in [[Assembler]] zu programmieren, insbesondere wenn ihr bereit seit auf die geringfügigen Geschwindigkeitsvorteile, die [[Assembler]] ermöglicht, zu verzichten. Es sind freie Compiler verfügbar, so wie gcc, die das Schreiben von [[Kernel]]code verhältnismäßig einfach machen, und die Nutzung von C wird sich auf lange Sicht auszahlen. |
| − | == Programmierung eines GRUB-kompatiblen | + | == Programmierung eines [[GRUB]]-kompatiblen [[Kernel]]s == |
=== Vorwort === | === Vorwort === | ||
Nun gut, erstmal möchte ich den Leser recht herzlich begrüßen. Ich hoffe, dass alle Sonderzeichen (Umlaute, etc.) korrekt dargestellt werden und dieser Text auch ansonsten in Ordnung ist. Nun zum Rechtlichen. Dieser Text darf gerne von jedermann verbreitet werden, so lange damit kein Geld verdient wird. Außerdem werde ich stinksauer, wenn jemand im Text rumpfuscht. Wenn also jemand Fehler entdeckt oder sonst was auszusetzen hat, bitte einfach ein Mail schreiben (lizer@gmx.net). Ansonsten ist noch zu sagen, dass sich dieser Text hauptsächlich an OS-Developer richtet. Ich gehe also davon aus, dass ich hier keine Begriffe erklären muss. Gut, das wars auch schon. Viel Spaß! | Nun gut, erstmal möchte ich den Leser recht herzlich begrüßen. Ich hoffe, dass alle Sonderzeichen (Umlaute, etc.) korrekt dargestellt werden und dieser Text auch ansonsten in Ordnung ist. Nun zum Rechtlichen. Dieser Text darf gerne von jedermann verbreitet werden, so lange damit kein Geld verdient wird. Außerdem werde ich stinksauer, wenn jemand im Text rumpfuscht. Wenn also jemand Fehler entdeckt oder sonst was auszusetzen hat, bitte einfach ein Mail schreiben (lizer@gmx.net). Ansonsten ist noch zu sagen, dass sich dieser Text hauptsächlich an OS-Developer richtet. Ich gehe also davon aus, dass ich hier keine Begriffe erklären muss. Gut, das wars auch schon. Viel Spaß! | ||
| − | Dann mal los. Um einen Kernel zu schreiben, der von GRUB problemlos als solcher erkannt und geladen werden kann, muss man im Grunde nur eins berücksichtigen, nämlich dass der Mutliboot Header vorhanden ist, und zwar an der richtigen Stelle. Zunächst einmal eine Auflistung aller Komponenten des Headers. | + | Dann mal los. Um einen [[Kernel]] zu schreiben, der von [[GRUB]] problemlos als solcher erkannt und geladen werden kann, muss man im Grunde nur eins berücksichtigen, nämlich dass der Mutliboot Header vorhanden ist, und zwar an der richtigen Stelle. Zunächst einmal eine Auflistung aller Komponenten des Headers. |
# '''Die "magische Zahl"''' Sie leitet den Header ein und teilt so dem Loader mit, was er zu erwarten hat. Die Zahl: 0x1BADB002. | # '''Die "magische Zahl"''' Sie leitet den Header ein und teilt so dem Loader mit, was er zu erwarten hat. Die Zahl: 0x1BADB002. | ||
# '''Die Flags''' Sie sagen dem Loader, was von ihm erwartet wird. Die ersten 16 Bits [0..15] stellen die "Bedingungen" dar. Wenn also ein Bit gesetzt ist, der Bootloader aber die entsprechende Erwartung des Kernels nicht erfüllen kann, bricht der Loader den Bootvorgang ab. Die hinteren 16 Bits [16..31] nenne ich jetzt mal die "Vorlieben" des Kernels. Sie stellen Erwartungen an den Bootloader, wie auch die ersten 16 Bits; wenn der Bootloader diesen hier aber nicht genügen kann, ist das nur halb so schlimm - es wird trotzdem weiter geladen. | # '''Die Flags''' Sie sagen dem Loader, was von ihm erwartet wird. Die ersten 16 Bits [0..15] stellen die "Bedingungen" dar. Wenn also ein Bit gesetzt ist, der Bootloader aber die entsprechende Erwartung des Kernels nicht erfüllen kann, bricht der Loader den Bootvorgang ab. Die hinteren 16 Bits [16..31] nenne ich jetzt mal die "Vorlieben" des Kernels. Sie stellen Erwartungen an den Bootloader, wie auch die ersten 16 Bits; wenn der Bootloader diesen hier aber nicht genügen kann, ist das nur halb so schlimm - es wird trotzdem weiter geladen. | ||