{{Navigation
|Zurück = [[Teil 6 - Multitasking]]
|Vor = [[Teil 8 - Ein erstes Programm]]
}}
==Ziel==
Wir haben mittlerweile zwei Kernelfunktionen, die als Tasks abwechselnd laufen und langweilige Dinge machen wie einen Buchstaben auszugeben. Jetzt wollen wir etwas weitergehen und beliebig viele Tasks langweilige Sachen machen lassen (solange uns der Speicher nicht ausgeht, jedenfalls).
Ich möchte hier auch gleich im Voraus erwähnen, dass wir langsam die Stellen hinter uns gebracht haben, wo wir entweder keine Wahl haben, wie es zu implementieren ist, oder wo es eine eindeutige Empfehlung gibt. Ab hier gibt es für die meisten Probleme mehrere gute Wege zum Ziel. Welche Wege man wählt, entscheidet am Ende, was für ein Kernel entsteht. Ich werde jeweils nur einen Weg beispielhaft ausführen, aber gelegentlich kurz auf andere Möglichkeiten hinweisen.
==Physische Speicherverwaltung==
Fangen wir zunächst damit an, unseren Kernel von der Einschränkung zu befreien, dass er nur genau zwei Tasks ausführen kann. Eigentlich haben wir mit init_task schon eine ganz nette Schnittstelle, um neue Tasks zu erstellen - das einzige, was nicht so schön ist, ist dass wir den Task von Hand ins Array eintragen und ihm seine fest deklarierten Stacks geben müssen.
Für jeden Task im Voraus Speicher zu reservieren, ist zu unflexibel, also müssen die Variablen ''stack_a'', ''stack_b'', ''user_stack_a'' und ''user_stack_b'' weg. Die Funktion ''init_task'' braucht die entsprechenden Parameter jetzt auch nicht mehr, denn sie soll sich den benötigten Speicher jetzt selbst (und zwar zur Laufzeit) holen. Wir schreiben also ungefähr so etwas:
Angenommen x ist ein int und wir interessieren uns für ein Flag in Bit 5.
Definieren wir also zunächst:
#define FLAG (1 << 5)
Wir können jetzt...
...prüfen, ob das Bit gesetzt ist: (x & FLAG)
...das Bit setzen: x |= FLAG
...das Bit löschen: x &= ~FLAG
===Speicherverwaltung initialisieren===
Eigentlich fehlt jetzt nur noch der Anfangszustand für die Speicherbitmap. Wir müssen dazu wissen, welche Speicherbereiche frei sind und wo wir besser nicht hinschreiben. Zum Glück weiß das BIOS das und GRUB gibt uns die entsprechende Information auch weiter. Wir ignorieren sie bisher nur.
Wenn unser Kernel gestartet ist, liegt im Register ''ebx'' ein Pointer auf die [[Multiboot#Multiboot-Structure|Multiboot Information Structure]]. Der verlinkte Artikel enthält eine Tabelle über den Aufbau der Struktur, die wir am geschicktesten als C-struct deklarieren (zumindest bis zu den mmap-Feldern). Das Feld mbs_mmap_addr ist ein Pointer auf die gesuchten Daten und der Abschnitt [[Multiboot#mbs_mmap_addr|mbs_mmap_addr]] enthält den Aufbau eines Eintrags der Memory Map.
Wir müssen also zunächst einmal zurück in die start.S und den Pointer weitergeben:
...
. = 0x100000;
/* Symbol fuer den Kernelanfang definieren */
kernel_start = .;
/*
* Der Multiboot-Header muss zuerst kommen (in den ersten 8 kB).
...
*(.bss)
}
/* Symbole fuer das Kernelende definieren (auf 4k aufrunden) */
. = ALIGN(4096);
kernel_end = .;
}
In C können wir diese Symbole anschließend als extern-Variablen deklarieren. Da die Variable keinen Datentyp hat (es zeigt ja nur ein Symbol an diese Stelle, ohne dass dort Platz für irgendwelche Daten reserviert wäre), ist void die richtige Wahl (da dieses Nichts zudem so ziemlich unveränderlich ist, kann man gleich ein const void verwenden (sonst kann es passieren, dass der Compiler eine Warnung ausgibt)). Wir sind hinterher sowieso nur an der Adresse der Variablen interessiert: