Starten des Multitaskings
Du erstellst wie unten beschrieben einen neuen Prozess für den Kernel. Dann brauchst du eine TSS. Da trägst du dann Ring0 SS und Ring0 ESP des Kernelstack vom Prozess, der grad läuft ein. Beachte, das in das TSS nicht der ESP eingetragen wird, sondern der Pointer auf das unterste Stackelement. In der GDT brauchst du einen TSS-Descriptor, der auf diese TSS zeigt. Du lädst nun mit LTR das TSS.
Einen neuen Prozess erstellen
Als erstes erstellst du einen neuen Kernelstack für den Prozess, und pushest auf ihn die Werte der Register, die der Prozess haben soll, wenn er gestartet wird. Beim Kernelprozess ist das nicht notwendig, da er ja grade läuft.
Jetzt erstellst du für jeden Prozess eine struct, in der du Informationen zum Prozess speicherst. Dort trägst du das Pagedirectory des Prozesses ein, sowie den Stack den du eben erstellt hat.
Tasks per Interupt wechseln
Du trägst in der IDT einen Handler für den Interupt ein, den du benutzen willst. (z.B. Timer Interrupt (IRQ 0)) Der Handler pusht nun alle Register, ruft den scheduler auf, wechselt die Stacks und popt die Register wieder.
C Beispielcode zum Stackerstellen:
unsigned long *stackbase = malloc (STACK_SIZE);
unsigned long *stackptr = stackbase;
// die werde die vom interrupt gate gepushed werden, werden später mit iret gepopt.
*stackptr = ss; // automatisch vom interrupt gate gepushed
stackptr--;
*stackptr = esp; // automatisch vom interrupt gate gepushed
stackptr--;
*stackptr = eflags; // automatisch vom interrupt gate gepushed
stackptr--;
*stackptr = cs; // automatisch vom interrupt gate gepushed
stackptr--;
*stackptr = eip; // startaddresse der anwendung, wird automatisch vom interrupt gate gepushed
stackptr--;
*stackptr = eax;
stackptr--;
*stackptr = ebx;
stackptr--;
*stackptr = ecx;
stackptr--;
*stackptr = edx;
stackptr--;
*stackptr = esi;
stackptr--;
*stackptr = edi;
stackptr--;
*stackptr = ds;
stackptr--;
*stackptr = es;
stackptr--;
*stackptr = fs;
stackptr--;
*stackptr = gs;
stackptr--;
Assembler-Beispielcode
taskswitch_interupt:
push eax
push ebx
push ecx
push edx
push esi
push edi
push ds
push es
push fs
push gs
mov [_thread_stack], esp
; die funktion _schedule hat folgende aufgaben:
; sie speichert die globale variable _thread_stack in die prozess-struct
; dann wählt sie den neuen prozess aus,
; wechselt das pagedirectory,
; speichert seinen esp in _thread_stack
; und speichert die basis des stacks in das tss
call _schedule
; eoi senden
; muss vor dem popen von eax gemacht werden
mov al, 0x20
out 0x20, al
mov esp, [_thread_stack]
pop gs
pop fs
pop es
pop ds
pop edi
pop esi
pop edx
pop ebx
pop eax
iret