Erste Hilfe — Änderungen 7963 → 7969

Zeile 2: Zeile 2:
  
 
'''Anfänger''':
 
'''Anfänger''':
−
Du hast es gerade geschafft deinen Kernel zu laden und möchtest einfach nur Text auf dem Bildschirm ausgeben. Der Code scheint einwandfrei, der Kernel wird auch geladen und... - der Emulator stürzt ab. Wahrscheinlich ist dir nur irgendwo ein kleiner Fehler unterlaufen, den schon tausende vor dir gemacht haben. Deswegen quäl dich nicht und lies einfach diesen Artikel, am besten behälst du die kleinen Tücken der Betriebssystem-Entwicklung, die hier aufgeführt werden, immer im Hinterkopf. Dann sparst du dir viel Zeit und Ärger.
+
Du hast es gerade geschafft deinen Kernel zu laden und möchtest einfach nur Text auf dem Bildschirm ausgeben. Der Code scheint einwandfrei, der Kernel wird auch geladen und… – der Emulator stürzt ab. Wahrscheinlich ist dir nur irgendwo ein kleiner Fehler unterlaufen, den schon tausende vor dir gemacht haben. Deswegen quäl dich nicht und lies einfach diesen Artikel, am besten behälst du die kleinen Tücken der Betriebssystem-Entwicklung, die hier aufgeführt werden, immer im Hinterkopf. Dann sparst du dir viel Zeit und Ärger.
  
 
'''Frustrierte''':
 
'''Frustrierte''':
Zeile 10: Zeile 10:
 
Du bist häufig im Forum unterwegs und hilfst gerne? Und da ist dieser eine Fehler, den alle immer wieder machen. Damit du ihn nicht tausendmal wieder korrigieren musst, schreib ihn einfach in diesen Artikel.
 
Du bist häufig im Forum unterwegs und hilfst gerne? Und da ist dieser eine Fehler, den alle immer wieder machen. Damit du ihn nicht tausendmal wieder korrigieren musst, schreib ihn einfach in diesen Artikel.
  
−
== Allgeime Fehler ==
+
== Allgemeine Fehler ==
−
=== "ich habe die fehlerhafte Stelle genau gefunden" ===
+
=== „Ich habe die fehlerhafte Stelle genau gefunden“ ===
 
NEIN! Es kann sein, dass die Stelle fehlerhaft ist, sie muss es aber nicht sein. Dafür ist die Betriebssystementwicklung einfach viel zu komplex. Vielmehr kann die Codestelle nur der Auslöser für einen Fehler sein, der sich schon viel früher eingeschlichen hat. Beispielsweise kann ein Fehler bei einem bestimmten Funktionsaufruf auftreten, aber nicht, weil die Funktion fehlerhaft ist, sondern weil der Stack zu groß wird.
 
NEIN! Es kann sein, dass die Stelle fehlerhaft ist, sie muss es aber nicht sein. Dafür ist die Betriebssystementwicklung einfach viel zu komplex. Vielmehr kann die Codestelle nur der Auslöser für einen Fehler sein, der sich schon viel früher eingeschlichen hat. Beispielsweise kann ein Fehler bei einem bestimmten Funktionsaufruf auftreten, aber nicht, weil die Funktion fehlerhaft ist, sondern weil der Stack zu groß wird.
  
 
=== Schon im Bootloader ===
 
=== Schon im Bootloader ===
−
Viele schreiben ihren Bootloader zwar nicht selbst, aber die Unerscchrockenen, die es tun, begeben sich gleich am Anfang in Gefahr:
+
Viele schreiben ihren Bootloader zwar nicht selbst, aber die Unerschrockenen, die es tun, begeben sich gleich am Anfang in Gefahr:
 
<asm>
 
<asm>
 
[org 0x7c00]
 
[org 0x7c00]
Zeile 23: Zeile 23:
 
jmp $
 
jmp $
 
</asm>
 
</asm>
−
Feiner Code, aber wer weiß, welche Registerzustände das BIOS uns überlässt. Wenn es zum Beispiel einen Farjump nach 0x00:0x7C00 macht, dann ist alles in Ordnung. Ist es aber einer nach 0x7C0:0x00, dann ist bei "call write " Schluss, weil die Org-Directive erwartet, dass in IP ein 0x00 steht. Außerdem wissen wir auch nicht, wohin das BIOS den Stack gesetzt hat, also sollte man den auch noch verschieben. Deswegen hat am Anfang jedes Bootloaders folgender (oder ähnlicher) Code zu stehen:
+
Feiner Code, aber wer weiß, welche Registerzustände das BIOS uns überlässt. Wenn es zum Beispiel einen Farjump nach 0x0000:0x7C00 macht, dann ist alles in Ordnung. Ist es aber einer nach 0x07C0:0x0000, dann ist bei „call write“ Schluss, weil die Org-Directive erwartet, dass in IP ein 0x7C00 steht. Außerdem wissen wir auch nicht, wohin das BIOS den Stack gesetzt hat, also sollte man den auch noch verschieben. Deswegen hat am Anfang jedes Bootloaders folgender (oder ähnlicher) Code zu stehen:
 
<asm>
 
<asm>
 
%define STACK_SEGMENT  0x1000
 
%define STACK_SEGMENT  0x1000
Zeile 47: Zeile 47:
  
 
=== Der Stack ===
 
=== Der Stack ===
−
Kommen wir jetzt zum einem der größten Biester in der Betriebssystemsentwicklung: der Stack. Das erste, was viele vergessen, ist, dass der Stack nach UNTEN hin wächst. Ein ''push'' vermindert also den Stackpointer! Und der Stack die unangenehme Eigenschaft, alles zu überschreiben, was sich ihm in den Weg stellt, also auch Code und Daten. Deswegen ist folgender Code zum Scheitern verurteilt:
+
Kommen wir jetzt zum einem der größten Biester in der Betriebssystemsentwicklung: dem Stack. Das erste, was viele vergessen, ist, dass der Stack nach '''unten''' hin wächst. Ein ''push'' vermindert also den Stackpointer! Und der Stack hat die unangenehme Eigenschaft, alles zu überschreiben, was sich ihm in den Weg stellt, also auch Code und Daten. Deswegen ist folgender Code zum Scheitern verurteilt:
 
<asm>
 
<asm>
 
#define GDT_BASE  0x20000
 
#define GDT_BASE  0x20000
Zeile 63: Zeile 63:
 
</asm>
 
</asm>
  
−
Und irgendwo in ''ClearScreen'' schmiert dann der Code ab, weil der Stack eben nach UNTEN wächst und die GDT überschreibt. Also immer vorsichtig sein mit dem Stack, insbesondere wenn irgendwann man für jeden Prozess einen eigenen hat.
+
Und irgendwo in ''ClearScreen'' schmiert dann der Code ab, weil der Stack eben nach '''unten''' wächst und die GDT überschreibt. Also immer vorsichtig sein mit dem Stack, insbesondere wenn man irgendwann für jeden Prozess einen eigenen hat.
  
−
=== invalid TSS ===
+
=== Invalid TSS ===
 
Das [[TSS]] ist ein sehr speicherhungriges Konstrukt, wesegen viele Programmierer auf Software-Multitasking umsteigen. Aber ganz ohne TSS geht es dann doch nicht. Dann nimmt man eben nur ein TSS und ändert die einzelnen Felder je nach Prozess. Damit spart man viel Speicherplatz. Dann hat man im [[Scheduler]] folgendes:
 
Das [[TSS]] ist ein sehr speicherhungriges Konstrukt, wesegen viele Programmierer auf Software-Multitasking umsteigen. Aber ganz ohne TSS geht es dann doch nicht. Dann nimmt man eben nur ein TSS und ändert die einzelnen Felder je nach Prozess. Damit spart man viel Speicherplatz. Dann hat man im [[Scheduler]] folgendes:
 
<asm>
 
<asm>
Zeile 78: Zeile 78:
 
iret
 
iret
 
</asm>
 
</asm>
−
Ja, gut gedacht, funktioniert aber nicht. Bochs beschwert sich zum Beispiel mit einem gut gemeinten ''LTR: doesn't point to an available TSS descriptor!''. Das Problem: die CPU setzt im TSS das sogenannte busy-Flag in der GDT beim Laden des TSS. Wenn man Hardware-Multitasking verwendet, wofür daa TSS eigentlich gedacht ist, dann ist das kein Problem, da man ja immer wieder ein neues TSS lädt und im alten das busy-Flag automatisch wieder gelöscht wird. Hier wird aber nur EIN TSS verwendet. Desshalb muss das busy-Flag von Hand gelöscht werden:
+
Ja, gut gedacht, funktioniert aber nicht. Bochs beschwert sich zum Beispiel mit einem gut gemeinten ''LTR: doesn't point to an available TSS descriptor!''. Das Problem: die CPU setzt im TSS das sogenannte busy-Flag in der GDT beim Laden des TSS. Wenn man Hardware-Multitasking verwendet, wofür daa TSS eigentlich gedacht ist, dann ist das kein Problem, da man ja immer wieder ein neues TSS lädt und im alten das busy-Flag automatisch wieder gelöscht wird. Hier wird aber nur '''ein''' TSS verwendet. Deshalb muss das busy-Flag von Hand gelöscht werden:
  
 
<asm>
 
<asm>
Zeile 89: Zeile 89:
  
 
=== Defekter Sektor ===
 
=== Defekter Sektor ===
−
Es kann auch durchaus vorkommen das ein Sektor der Diskette, die zu zum Testen verwendest, defekt ist. Dieser Fehler ist besonders dann schwer zu finden wenn man sein System primär auf realer Hardware testet.
+
Es kann auch durchaus vorkommen das ein Sektor der Diskette, die zu zum Testen verwendest, defekt ist. Dieser Fehler ist besonders dann schwer zu finden, wenn man sein System primär auf realer Hardware testet.
  
 
== C-spezifische Fehler ==
 
== C-spezifische Fehler ==
Zeile 95: Zeile 95:
 
TODO
 
TODO
 
=== volatile ===
 
=== volatile ===
−
Der Compiler hat die angenehme Eigenschaft den Code um sinnlose Passagen zu erleichtern. Aber manchmal ist das eben nicht hilfreich, vor allem wenn man inline-Assembler verwendet wird dieser oft wegoptimiert. Dagegen hilft das ''volatile'' Schlüsselwort. Es fordert den Compiler dazu auf den entsprechenden Code nicht zu optimieren:
+
Der Compiler hat die angenehme Eigenschaft den Code um sinnlose Passagen zu erleichtern. Aber manchmal ist das eben nicht hilfreich, vor allem wenn man inline-Assembler verwendet, wird dieser oft wegoptimiert. Dagegen hilft das ''volatile'' Schlüsselwort. Es fordert den Compiler dazu auf, den entsprechenden Code nicht zu optimieren:
 
<c>
 
<c>
 
asm volatile("mov %0, %%eax;" : : "a" (value) );
 
asm volatile("mov %0, %%eax;" : : "a" (value) );
Zeile 108: Zeile 108:
 
}
 
}
 
</c>
 
</c>
−
Aus der Schleife könnte der Compiler eine Endlosschleife machen, da er der Meinung ist, dass sich key_pressed nicht ändern kann in der Schleife. Aber ''volatile'' löst das Problem:
+
Aus der Schleife könnte der Compiler eine Endlosschleife machen, da er der Meinung ist, dass sich key_pressed in der Schleife nicht ändern kann. Aber ''volatile'' löst das Problem:
 
<c>
 
<c>
 
volatile bool key_pressed=false;  //wird vom IRQ-handler gesetzt
 
volatile bool key_pressed=false;  //wird vom IRQ-handler gesetzt
Zeile 121: Zeile 121:
 
=== Nur ein kleines Zeichen... ===
 
=== Nur ein kleines Zeichen... ===
 
'''Soll diese Passage im Artikel bleiben?'''
 
'''Soll diese Passage im Artikel bleiben?'''
−
Die folgenden Fehler sind eigentlich zu banal für diesen Artikel. Leider passieren sie aber doch zu häufig. Außerdem überließt man sie bei längeren Codepassagen schneller als einem lieb ist:
 
  
−
==== "==" ist ungleich "=" ====
+
Die folgenden Fehler sind eigentlich zu banal für diesen Artikel. Leider passieren sie aber doch zu häufig. Außerdem überliest man sie bei längeren Codepassagen schneller, als einem lieb ist:
 +
 
 +
==== „==“ ist ungleich „=“ ====
 
==== Zahlensysteme ====
 
==== Zahlensysteme ====
 
Die Page Size Extension ist eine tolle Sache, also aktivieren wir sie doch mal. Dafür einfach in cr4 das 4. Bit setzen. Ganz simpel:
 
Die Page Size Extension ist eine tolle Sache, also aktivieren wir sie doch mal. Dafür einfach in cr4 das 4. Bit setzen. Ganz simpel:
Zeile 146: Zeile 147:
 
Nein, wieder falsch. Das Minuszeichen negiert den Wert doch nur, bildet also das Zweierkomplement. Richtig gewsen wäre:
 
Nein, wieder falsch. Das Minuszeichen negiert den Wert doch nur, bildet also das Zweierkomplement. Richtig gewsen wäre:
 
   int gekippt = ~0xFA;
 
   int gekippt = ~0xFA;
−
Diese Operation liefert das gewünschte 0x05
+
Diese Operation liefert das gewünschte 0x05.
  
−
== C++ spezifische Fehler ==
+
== C++-spezifische Fehler ==
 
=== designated initializers ===
 
=== designated initializers ===
 
Das Problem hier ist, dass C++ im Gegensatz zu C keine ''designated initializers'' (siehe [http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html GCC Manual] für eine Erklärung) unterstützt, beispielsweise wird der Code
 
Das Problem hier ist, dass C++ im Gegensatz zu C keine ''designated initializers'' (siehe [http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html GCC Manual] für eine Erklärung) unterstützt, beispielsweise wird der Code
Zeile 176: Zeile 177:
 
};
 
};
 
</cpp>
 
</cpp>
−
Die andere Möglichkeit ist einen Konstruktor für die Struktur zu definieren - dafür muss aber die Struktur benannt werden:
+
Die andere Möglichkeit ist einen Konstruktor für die Struktur zu definieren – dafür muss aber die Struktur benannt werden:
 
<cpp>
 
<cpp>
 
struct gdt_register
 
struct gdt_register
Zeile 191: Zeile 192:
 
</cpp>
 
</cpp>
  
−
== Linker-spezifische Fehler ==
+
== Linkerspezifische Fehler ==
−
=== undefined reference to `funktionsname' ===
+
=== Undefined reference to `funktionsname' ===
 
Diese Fehlermeldung wird vom Linker ausgegeben, wenn er kein Symbol mit dem Namen ''funktionsname'' findet. Dies kann vorkommen, wenn
 
Diese Fehlermeldung wird vom Linker ausgegeben, wenn er kein Symbol mit dem Namen ''funktionsname'' findet. Dies kann vorkommen, wenn
 
* du vergessen hast die Objektdatei mit der Definition der Funktion ''funktionsname'' zu kompilieren und mit zu linken
 
* du vergessen hast die Objektdatei mit der Definition der Funktion ''funktionsname'' zu kompilieren und mit zu linken
−
* du vergessen hast in der Sourcecodedatei diese Funktion zu definieren
+
* du vergessen hast diese Funktion in der Sourcecodedatei zu definieren
 
* du aus Versehen die Funktion als ''static'' deklariert hast
 
* du aus Versehen die Funktion als ''static'' deklariert hast
 
* du [[C++]] verwendest und ''funktionsname'' in einer C++-Quellcodedatei definiert und von einer C/Assembler-Quellcodedatei verwendet wird, eine Lösung findet sich [[C++#C++ Funktionen aus C/Assembler nutzen | hier]]
 
* du [[C++]] verwendest und ''funktionsname'' in einer C++-Quellcodedatei definiert und von einer C/Assembler-Quellcodedatei verwendet wird, eine Lösung findet sich [[C++#C++ Funktionen aus C/Assembler nutzen | hier]]