| Zeile 1: | Zeile 1: | ||
| + | {{Tutorial | ||
| + | |Titel=Software Multitasking | ||
| + | |Schwierigkeit=2 | ||
| + | |Vorwissen=[[IRQ]], [[Scheduler]] | ||
| + | |Sprache=Pascal | ||
| + | }} | ||
Dieses Tutorial ist für alle gedacht, die in ihrem [[Betriebssystem]] [[Multitasking]] auf Softwarebasis implementieren wollen. Ich bin am Anfang an diesem Thema fast verzweifelt, daher schreibe ich dieses Tutorial, damit andere einen leichten Einstieg in diese Thema haben. Ich werde hier nicht "alles" erklären, aber danach sollte einfache Tests mit zwei Tasks möglich sein. | Dieses Tutorial ist für alle gedacht, die in ihrem [[Betriebssystem]] [[Multitasking]] auf Softwarebasis implementieren wollen. Ich bin am Anfang an diesem Thema fast verzweifelt, daher schreibe ich dieses Tutorial, damit andere einen leichten Einstieg in diese Thema haben. Ich werde hier nicht "alles" erklären, aber danach sollte einfache Tests mit zwei Tasks möglich sein. | ||
| Zeile 25: | Zeile 31: | ||
Zuerst kommen ein paar Konstanten: | Zuerst kommen ein paar Konstanten: | ||
| + | <pascal> | ||
const TASK_EMPTY as BYTE = 0 | const TASK_EMPTY as BYTE = 0 | ||
const TASK_READY as BYTE = 1 | const TASK_READY as BYTE = 1 | ||
| + | </pascal> | ||
Die zeigen einfach nur an, ob ein Tabelleneintrag der Task-Tabelle mit einem Task belegt ist. | Die zeigen einfach nur an, ob ein Tabelleneintrag der Task-Tabelle mit einem Task belegt ist. | ||
| − | + | <pascal> | |
type TaskType | type TaskType | ||
PID as USHORT | PID as USHORT | ||
| Zeile 39: | Zeile 47: | ||
dim shared AllTasks as USHORT | dim shared AllTasks as USHORT | ||
dim shared currentPID as USHORT | dim shared currentPID as USHORT | ||
| − | + | </pascal> | |
Nicht wirklich schwer. Wir machen eine Typendefinition für die Task-Tabelle. Enthalten sind die ProzessID (positive 16bit Zahl), die Stack-Adresse (positive 32bit Zahl) und der Status des Tasks (8bit Zahl). Danach legen wir ein Array an, das wir jetzt mit den Daten füttern können. | Nicht wirklich schwer. Wir machen eine Typendefinition für die Task-Tabelle. Enthalten sind die ProzessID (positive 16bit Zahl), die Stack-Adresse (positive 32bit Zahl) und der Status des Tasks (8bit Zahl). Danach legen wir ein Array an, das wir jetzt mit den Daten füttern können. | ||
Dann legen wir noch zwei Variablen an. "AllTasks" gibt an, wieviele Einträge der Tabelle belegt sind. "currentPID" beinhaltet die ProzessID des aktuell laufenden Tasks. | Dann legen wir noch zwei Variablen an. "AllTasks" gibt an, wieviele Einträge der Tabelle belegt sind. "currentPID" beinhaltet die ProzessID des aktuell laufenden Tasks. | ||
| − | + | <pascal> | |
function dec (byref tPTR as UINTEGER PTR) as UINTEGER PTR | function dec (byref tPTR as UINTEGER PTR) as UINTEGER PTR | ||
tPTR -= 1 | tPTR -= 1 | ||
return tPTR | return tPTR | ||
end function | end function | ||
| + | </pascal> | ||
Das hat jetzt nichts direkt mit Multitasking zu tun, ist aber nützlich. Damit können wir so etwas wie "*--stack = 0" in FreeBASIC folgendermaßen schreiben: "*(dec(stack)) = 0". Kurz gesagt, nimmt sie einen UINTEGER-pointer und setzt ihn um 4bytes (also ein UINTEGER) runter. Das macht später einiges einfacher. | Das hat jetzt nichts direkt mit Multitasking zu tun, ist aber nützlich. Damit können wir so etwas wie "*--stack = 0" in FreeBASIC folgendermaßen schreiben: "*(dec(stack)) = 0". Kurz gesagt, nimmt sie einen UINTEGER-pointer und setzt ihn um 4bytes (also ein UINTEGER) runter. Das macht später einiges einfacher. | ||
| − | + | <pascal> | |
sub Multitasking_Init | sub Multitasking_Init | ||
PIT_Timer_init (100) | PIT_Timer_init (100) | ||
IDT_CreateEntry (Interrupt_IRQ0, @Multitasking_Switch, &h08, &h8E) | IDT_CreateEntry (Interrupt_IRQ0, @Multitasking_Switch, &h08, &h8E) | ||
end sub | end sub | ||
| + | </pascal> | ||
Auch einfach. Wir programmieren den PIT so, dass er 100 mal in der Sekunde (vermutlich kein guter Wert für später) den IRQ 0 auslöst. Dann legen wir in der IDT einen Eintrag an, an der Stelle für IRQ 0. Da tragen wir die Adresse der Task-Switch Routine ein, und geben als Selector den Hex-Wert 0x08 an. Die Flags setzen wir auf 0x8E. | Auch einfach. Wir programmieren den PIT so, dass er 100 mal in der Sekunde (vermutlich kein guter Wert für später) den IRQ 0 auslöst. Dann legen wir in der IDT einen Eintrag an, an der Stelle für IRQ 0. Da tragen wir die Adresse der Task-Switch Routine ein, und geben als Selector den Hex-Wert 0x08 an. Die Flags setzen wir auf 0x8E. | ||
| − | + | <pascal> | |
sub Multitasking_Switch naked | sub Multitasking_Switch naked | ||
asm | asm | ||
| Zeile 89: | Zeile 99: | ||
end asm | end asm | ||
end sub | end sub | ||
| + | </pascal> | ||
Kurz, oder? Als erstes fällt wohl das "naked" auf. Damit sagen wir FreeBASIC (Vorsicht, funktioniert erst aber Version 0.21!), dass er am Anfang und Ende nichts am Stack macht und kein "ret" anfügt. Sonst könnten wir die Routine nicht direkt in die IDT eintragen. | Kurz, oder? Als erstes fällt wohl das "naked" auf. Damit sagen wir FreeBASIC (Vorsicht, funktioniert erst aber Version 0.21!), dass er am Anfang und Ende nichts am Stack macht und kein "ret" anfügt. Sonst könnten wir die Routine nicht direkt in die IDT eintragen. | ||
| Zeile 97: | Zeile 108: | ||
Dann laden wir nur noch die Register wieder vom Stack und gehen mit "iret" zum neuen Task. | Dann laden wir nur noch die Register wieder vom Stack und gehen mit "iret" zum neuen Task. | ||
| + | <pascal> | ||
function Multitasking_scheduler (stack as UINTEGER) as UINTEGER | function Multitasking_scheduler (stack as UINTEGER) as UINTEGER | ||
Tasks(currentPID).stack = stack | Tasks(currentPID).stack = stack | ||
| Zeile 107: | Zeile 119: | ||
return Tasks(currentPID).stack | return Tasks(currentPID).stack | ||
end function | end function | ||
| + | </pascal> | ||
Auch recht einfach. Wir sichern die Stackadresse im Tabelleneintrag des aktuellen Tasks. Dann erhöhen wir currentPID bis wir einen Task finden, der auf seine Aktivierung wartet. Sollte der Wert in currentPID zu hoch werden, setzen wir ihn wieder auf den Tabellenanfang. Haben wir als einen Task gefunden, geben wir dessen Stackadresse zurück. | Auch recht einfach. Wir sichern die Stackadresse im Tabelleneintrag des aktuellen Tasks. Dann erhöhen wir currentPID bis wir einen Task finden, der auf seine Aktivierung wartet. Sollte der Wert in currentPID zu hoch werden, setzen wir ihn wieder auf den Tabellenanfang. Haben wir als einen Task gefunden, geben wir dessen Stackadresse zurück. | ||
| + | <pascal> | ||
sub Task_Create (ID as USHORT, priority as UBYTE, address as any ptr) | sub Task_Create (ID as USHORT, priority as UBYTE, address as any ptr) | ||
if Tasks(ID).status <> TASK_EMPTY then return | if Tasks(ID).status <> TASK_EMPTY then return | ||
| Zeile 145: | Zeile 159: | ||
asm sti '// allow Interrupts again | asm sti '// allow Interrupts again | ||
end sub | end sub | ||
| + | </pascal> | ||
Puh. Auf zur Erklärung: Wir prüfen zuerst, ob die gewünschte ProzessID schon belegt ist. Wenn ja, kerhen wir sofort zurück (besser wäre es, wenn wir einen Fehler-Wert zurückgeben würden). Dann erstellen wir eine Variable, in der wir die Stack-Adresse des Tasks zwischenspeichern. | Puh. Auf zur Erklärung: Wir prüfen zuerst, ob die gewünschte ProzessID schon belegt ist. Wenn ja, kerhen wir sofort zurück (besser wäre es, wenn wir einen Fehler-Wert zurückgeben würden). Dann erstellen wir eine Variable, in der wir die Stack-Adresse des Tasks zwischenspeichern. | ||
| Zeile 174: | Zeile 189: | ||
== Links == | == Links == | ||
[http://www.fh-zwickau.de/doc/prmo/pmtutor/text/ Protected-Mode Tutorial der FH-Zwickau] | [http://www.fh-zwickau.de/doc/prmo/pmtutor/text/ Protected-Mode Tutorial der FH-Zwickau] | ||
| − | |||
[http://code.google.com/p/onyxkernel/wiki/SoftwareMultitasking Software-Multitasking Tutorial auf google code] | [http://code.google.com/p/onyxkernel/wiki/SoftwareMultitasking Software-Multitasking Tutorial auf google code] | ||
[[Category:Tutorials]] | [[Category:Tutorials]] | ||