Multitasking im x86 Real Mode
-
Hallo,
um ein bisschen in die Materie der Betriebssysteme einzusteigen, habe ich versucht ein extrem primitives Multitasking im x86 Real Mode zu realisieren.
Konkret wechsle ich im Timer Interrupt zwischen dem Task1 (=main) und Task2 (=thread). Der Stack, der vom Interrupt aufgebaut wird, enthält Infos zu IP und SP. Genau diese Werte passe ich dem entsprechenden Task an.
Den iret Befehl nutze ich, um dann wirklich den Task zu wechseln, indem von nun an ein anderer IP und SP gesetzt ist.Das ganze habe ich maximal einfach gehalten, ich speichere mir keine Register mit ab, kann nur zwischen 2 Tasks hin und her wechseln und mir ist klar, dass gewisse Dinge im PM einfacher gingen. Allerdings hat es mir geholfen, Multitasking mal praktisch umzusetzen, um die Dinge möglichst im Detail zu verstehen.
Der Code funktioniert wie gewünscht (getestet mit bochs), Task1 schreibt M auf die Konsole, Task2 T, und der Interrupt Handler schließlich H, so kann man das Multitasking schön verfolgen.
Ich stell den Code hier mal rein, denn ich hab ich Gegenzug von Euren tollen Tutorials auch schon einiges lernen dürfen, so hilft vielleicht mein code auch dem einen oder anderen Einsteiger wie mir.
Falls euch Fehler auffallen, sagt Bescheid. Auch sonstiges Feedback ist willkommen!; Simples Multitasking im x86 Real Mode ;====================================== ; ; 1. Installieren eines Interrupt Handlers ; für den PIT ; 2. Der Task "main" führt eine Endlosschleife aus ; und gibt am Bildschirm "M" aus ; 3. Der Task "thread" führt eine Endlosschleife aus ; und gibt am Bildschirm "T" aus ; 4. Der Interrupt Handler hat einen Zähler, und wechselt ; beim Zählerstand 0 und 128 den Task ; Bei jedem Aufruf des Handlers wir "H" ausgegeben ; 5. switch_taskx speichert tasky und fügt taskx ein, ; schliesslich erhält durch iret der andere ; Task die CPU ; 6. Wichtige Adressen des 0. Segments ; 0x0000: Segmentbeginn ; 0x7C00: main, Kernel wird von BIOS hier gestartet ; 0x9c00: main stack ; 0x8c00: thread1 stack ; 7. Stack beim Eintritt in Interrupt Handler ; IP: SP+0 ; CS: SP+2 ; FLAGS: SP+4 ; SP: SP+6 ; SS: SP+8 %define SEGMENT_NR 0 %define START_OFFSET 0x7C00 %define TASK1_STACK 0x9c00 %define TASK2_STACK 0x8c00 %define TASK2_FLAGS 0x200 %define PIT_IRV_NR 0x8 %define BIOS_VIDEO 0x10 %define BIOS_OUTPUT 0x0E %define BOOT_SIG 0xAA55 %define EOI_BYTE 0x20 %define EOI_PORT 0x20 org START_OFFSET ;------------------------------------- main: ; ax enthält vorerst 0 Wert mov ax,SEGMENT_NR ; stack vorbereiten mov ss,ax mov sp,TASK1_STACK ; IVT, Eintrag 8 (PIT) setzen auf den Handler pit_handler cli mov gs,ax mov bx,PIT_IRV_NR shl bx,2 ;bx=bx*4 mov word [gs:bx], pit_handler add bx,2 mov word [gs:bx], ax sti ; Endlosschleife in main ; gibt sich am Bildschirm mit "M" aus loop_inf: mov bl,'M' call showAction jmp loop_inf end_loop_inf: end_main: ;------------------------------------- ; Endlosschleife in thread ; gibt sich am Bildschirm mit "T" aus thread: mov bl,'T' call showAction jmp thread end_thread: ;------------------------------------- ; Funktion zum Ausgeben eines Zeichens ; das Zeichen wird im Register bl erwartet showAction: mov ah, BIOS_OUTPUT mov al, bl int BIOS_VIDEO ret end_showAction: ;------------------------------------- ; Interrupt Handler für IRQ=0 (PIT) ; wird verwendet um einen Context Switch zwischen ; den Tasks main und thread durchzuführen pit_handler: ; am Bildschrim ausgeben, dass Handler ; aufgerufen wurde mov bl,'H' call showAction ; end of interrupt dem PIC mitteilen mov al, EOI_BYTE out EOI_PORT, al ; ctrB++, läuft nach 256 Werten über add byte [ctrB],1 mov byte al,[ctrB] ; ctr==128, Task2 (thread) starten cmp al,128 je switch_task2 ; ctr==0, für Task1 (loop_inf) starten cmp al,0 je switch_task1 ; derzeit kein Task Switch nötig, raus hier! iret ;Aufbau des Stacks siehe oben, Punkt 6. ; Task2 speichern, Task1 einfügen switch_task1: ;task2 speichern ;ip mov bx,sp mov ax,[bx+0] mov bx,task2+0 mov [bx],ax ;flags mov bx,sp mov ax,[bx+4] mov bx,task2+2 mov [bx],ax ;sp mov bx,sp mov ax,[bx+6] mov bx,task2+4 mov [bx],ax ;task1 einfügen ;ip mov ax,[task1+0] mov bx,sp mov [bx],ax ;flags mov ax,[task1+2] mov bx,sp mov [bx+4],ax ;sp mov ax,[task1+4] mov bx,sp mov [bx+6],ax ;interrupt verlassen iret end_switch_task1: ; Task1 speichern, Task2 einfügen switch_task2: ;task1 speichern ;ip mov bx,sp mov ax,[bx+0] mov bx,task1+0 mov [bx],ax ;flags mov bx,sp mov ax,[bx+4] mov bx,task1+2 mov [bx],ax ;sp mov bx,sp mov ax,[bx+6] mov bx,task1+4 mov [bx],ax ;task2 einfügen ;ip mov ax,[task2+0] mov bx,sp mov [bx],ax ;flags mov ax,[task2+2] mov bx,sp mov [bx+4],ax ;sp mov ax,[task2+4] mov bx,sp mov [bx+6],ax ;interrupt verlassen iret end_switch_task2: end_pit_handler: ;------------------------------------- ; zähler für aktuellen task ctrB db 0 ; speichern des task zustandes: IP, Flags, SP task1 dw loop_inf,0,0 task2 dw thread,TASK2_FLAGS,TASK2_STACK ;------------------------------------- ; erster Sektor bis auf 512 byte auffüllen ; Boot Signatur 55AA ganz am Ende times 510-($-$$) hlt dw BOOT_SIG
-
MT in RM, das ist mal was anderes. Danke für deinen Beitrag!