Hardware-Multitasking — Änderungen 6864 → 6882

Zeile 5: Zeile 5:
 
=== Das Task State Segment ===
 
=== Das Task State Segment ===
  
−
Hardware-Multitasking beruht auf dem sogenannten Task State Segment, welches jedem Task zugeordnet wird und unter anderem dessen Registerwerte speichert. Der Aufbau des TSS wird [[TSS|hier exakt]] beschrieben. Man kann die Felder dabei in zwei Kategorien einteilen:
+
Hardware-Multitasking beruht auf dem sogenannten Task State Segment, welches jedem Task zugeordnet wird und unter anderem dessen Registerwerte speichert. Der Aufbau des TSS wird [[TSS|hier exakt]] beschrieben.
−
#'''statische Felder''' werden einmal mit Werten belegt und dann jeweils beim Taskwechsel von der CPU in die entsprechenden Register geladen, aber niemals automatisch verändert. Zu den statischen Feldern zählen IOMap Base Address, LDT Segment Selektor, CR3, ESP0-2 und SS0-2. Das CR3-Feld muss nicht initialisiert werden, wenn Paging nicht aktiv ist, ebenso der LDT Selektor, falls man keine LDTs verwendet. Mit der IOMap kann man bestimmte Ports maskieren, auf die der Task kein Zugriff haben soll. IOMap Base Address kann Null gesetzt werden, wenn man diese Funktion der CPU nicht nutzen möchte.
 
−
#'''dynamische Felder''' müssen auch initalisiert werden, werden dann aber beim Taskwechsel von der CPU immer mit den entsprechenden geänderten Registerwerten belegt. Zu den dynamischen Feldern gehören EAX-EDX, CS-GS/SS, EFLAGS, ESI/EDI/EBP/ESP und Previous Task Link.
 
  
−
Der Speicherort des TSS wird als Eintrag in der GDT gespeichert. Der Selektor dieses Eintrags wird im Task Register der CPU gespeichert, wodurch sie bei einem Taskwechsel weiß, wo die (dynamischen) Registerwerte gespeichert werden sollen, damit diese beim Wiederaufruf des Tasks unverändert sind. Folgender Code legt ein TSS an und erzeugt dann den Descriptor in der GDT, der darauf zeigt.
+
Der Speicherort des TSS wird als Eintrag in der GDT gespeichert. Der Selektor dieses Eintrags wird im Task Register der CPU gespeichert, wodurch sie bei einem Taskwechsel weiß, wo die (dynamischen) Registerwerte gespeichert werden sollen, damit diese beim Wiederaufruf des Tasks unverändert sind.
 +
 
 +
Folgender Code legt ein TSS an und erzeugt dann den Descriptor in der GDT, der darauf zeigt.
  
 
<c>
 
<c>
Zeile 135: Zeile 135:
  
 
Dieses doch recht kleine Codestück wird das eigentliche Multitasking übernehmen.
 
Dieses doch recht kleine Codestück wird das eigentliche Multitasking übernehmen.
−
Ich gehe dabei davon aus, dass in deinem OS bereits PIT und PIC initalisiert worden sind, denn denn immer wenn der PIT „feuert“, dann wechselt der Scheduler zum nächsten Task.
+
Ich gehe dabei davon aus, dass in deinem OS bereits PIT und PIC initalisiert worden sind, denn in der [[ISR]] des [[PIT|PITs]] wird der Schedulercode ausgeführt.
 +
 
 
Dafür muss er ersteinmal den nächsten Eintrag in der Task-Table finden, der aktiv ist (present-Feld ist nicht null)
 
Dafür muss er ersteinmal den nächsten Eintrag in der Task-Table finden, der aktiv ist (present-Feld ist nicht null)
  
 
<c>
 
<c>
 
DWORD current_task_id=0;
 
DWORD current_task_id=0;
−
#define interrupt_return  asm("leave; iret")
+
WORD  tss_sel;
−
void isr_pit()
+
void scheduler()
 
{
 
{
 
   current_task_id++;
 
   current_task_id++;
Zeile 149: Zeile 150:
 
       if (current_task_id = NUM_TASKS]) current_task_id=0;
 
       if (current_task_id = NUM_TASKS]) current_task_id=0;
 
   }
 
   }
−
</c>
+
  tss_sel = current_task_id + TSS_SEL_BASE;
−
Jetzt muss zu diesem Task geswechselt werden, indem zu dessen TSS-Selektor gesprungen wird:
 
−
<c>
 
−
  asm("mov %%ax, *$lf+5;"
 
−
      "1:"
 
−
      "ltr $1234:$1234;" :
 
−
: "a" (8*(current_task_id + TSS_SEL_BASE));
 
−
  interrupt_return;
 
 
}
 
}
 
</c>
 
</c>
−
Dieser Code kann natürlich noch erweitert werden, zum Beispiel, wenn man jedem Task eine unterschiedliche Priorität zuweist.
+
Jetzt muss zu diesem Task geswechselt werden, indem zu dessen TSS-Selektor gesprungen wird. Es empfiehlt sich diesen Teil in Assembler zu schreiben, da die ISR auch aus einem Assemblerstub heraus aufgerufen werden sollte. Dieser sieht dann so aus:
−
''Eventuell ist hier die AT&T-Syntax falsch, da ich mit dieser nicht ganz vertraut bin. Außerdem ist dieses als normale C-Prozedur getarnte Version einer ISR etwas unschön.''
+
 
 +
<asm>
 +
extern _tss_sel
 +
extern _scheduler
 +
global _isr_pit
 +
_isr_pit:
 +
  pusha
 +
  call _scheduler
 +
  mov ax, [_tss_sel]
 +
  mov [opcode_hack+5], ax
 +
  popa
 +
  opcode_hack:
 +
  jmp 1234 : 1234
 +
iret
 +
</asm>
 +
 
 +
Jetzt musst du nur noch "isr_pit" in deine IDT als PIT Interrupthandler eintragen, damit der Scheduler auch bei jedem Tick aufgerufen wird und zum nächsten Task wechselt.
  
 
=== Schritt 5: Multitasking initalisieren ===
 
=== Schritt 5: Multitasking initalisieren ===