Ausgabe 7 — Änderungen 1816 → 1817

Zeile 97: Zeile 97:
 
angegeben wurde, die Deskriptornummer des Auslösertasks. Zu diesem kehrt sie bei
 
angegeben wurde, die Deskriptornummer des Auslösertasks. Zu diesem kehrt sie bei
 
gesetztem NT-Flag im Flagregister mit einem IRET zurück. Die Bereiche, die die
 
gesetztem NT-Flag im Flagregister mit einem IRET zurück. Die Bereiche, die die
−
Namen der Register tragen dürften klar sein. Beim Eintrag „I/O Port Bitmap Base“ wird
+
Namen der Register tragen dürften klar sein. Beim Eintrag ''I/O Port Bitmap Base'' wird
 
das Offset einer Tabelle angegeben, die angibt, auf welche Portadressen der Task
 
das Offset einer Tabelle angegeben, die angibt, auf welche Portadressen der Task
 
Zugriff hat. Bei dem Wert 0 ist diese deaktiviert, sodass der Zugriff alleine durch das
 
Zugriff hat. Bei dem Wert 0 ist diese deaktiviert, sodass der Zugriff alleine durch das
Zeile 107: Zeile 107:
 
und nicht in der Task-Tabelle speichern will. Wie ich schon sagte braucht jeder Task ein
 
und nicht in der Task-Tabelle speichern will. Wie ich schon sagte braucht jeder Task ein
 
TSS und dies muss in der GDT eingetragen werden. Ein TSS hat als die 4 Typ-Bits den
 
TSS und dies muss in der GDT eingetragen werden. Ein TSS hat als die 4 Typ-Bits den
−
Wert „1001“ als Binärzahl. Dies ist der Aufbau eines TSS-Deskriptors:
+
Wert 1001 als Binärzahl. Dies ist der Aufbau eines TSS-Deskriptors:
−
</p><table class="ntable" border="1" bordercolor="#000000" cellspacing="0" cellpadding="2"><tr><td class="tableinfo"><img src="http://www.joachim-neu.de/public/lowlevel/7/3_2.png" alt="TSS-Deskriptor"><br></td></tr></table><br><p style="text-align:justify">
+
 
−
Jede Zeile der Tabelle gibt ein Word wieder, eine 16 Bit lange Zahl. Eigendlich gibt es
+
<img src="http://www.joachim-neu.de/public/lowlevel/7/3_2.png" alt="TSS-Deskriptor">
−
nichts, was unterschiedlich zu dem normalen Aufbau eines Deskriptors währe,
+
 
−
deswegen erkläre ich nur kurz alle Felder. Alle, die mit „<span style="background: #FFFF00">Limit</span>“ bezeichnet sind geben
+
Jede Zeile der Tabelle gibt ein Word wieder, eine 16 Bit lange Zahl. Eigentlich gibt es
−
die Größe des Segments an. Alle mit „<span style="background: #FF0000">Basis</span>“ bezeichneten geben die Basisadresse im
+
nichts, was unterschiedlich zu dem normalen Aufbau eines Deskriptors wäre,
−
Speicher an. Das Bit „<span style="background: #FF00FF">P</span>“ gibt an, ob sich das Segment im Speicher (present) befindet. Es
+
deswegen erkläre ich nur kurz alle Felder.  
−
sollte immer auf 1 gesetzt sein, da es meiner Meinung nach nicht zu den schlausten
+
* Alle, die mit ''<span style="background: #FFFF00">Limit</span>'' bezeichnet sind geben die Größe des Segments an. * Alle mit ''<span style="background: #FF0000">Basis</span>'' bezeichneten geben die Basisadresse im
 +
Speicher an. Das Bit ''<span style="background: #FF00FF">P</span>'' gibt an, ob sich das Segment im Speicher (present) befindet. Es sollte immer auf 1 gesetzt sein, da es meiner Meinung nach nicht zu den schlausten
 
Lösungen zählt, TSSs auszulagern, die man ständig braucht, zumal diese nicht sehr viel
 
Lösungen zählt, TSSs auszulagern, die man ständig braucht, zumal diese nicht sehr viel
−
Speicher sparen. Die 2 Bit lange Stelle „<span style="background: #80FFFF">DPL</span>“ gibt das DescriptorPrivilegeLevel an.<br>
+
Speicher sparen.  
 +
*Die 2 Bit lange Stelle ''<span style="background: #80FFFF">DPL</span>'' gibt das DescriptorPrivilegeLevel an.
 
Damit kann man kontrollieren, welche Privilegstufe Programme brauchen, die auf
 
Damit kann man kontrollieren, welche Privilegstufe Programme brauchen, die auf
−
dieses Segment zugreifen wollen. Das Bit „<span style="background: #0000FF">S</span>“ ist das System-Bit. Es unterscheidet
+
dieses Segment zugreifen wollen.  
 +
*Das Bit ''<span style="background: #0000FF">S</span>'' ist das System-Bit. Es unterscheidet
 
spezielle Segmente (wie TSS und alle Gates) von Standartsegmenten (Codesegment,
 
spezielle Segmente (wie TSS und alle Gates) von Standartsegmenten (Codesegment,
−
Datensegment). Bei speziellen Segmenten ist es nicht gesetzt, sprich 0. Dann gibt es
+
Datensegment). Bei speziellen Segmenten ist es nicht gesetzt, sprich 0.  
−
noch das „<span style="background: #800080">G</span>“(ranularity)-Bit, das angibt, ob die Größenangabe des Deskriptors in Bytes
+
* Dann gibt es noch das ''<span style="background: #800080">G</span>''(ranularity)-Bit, das angibt, ob die Größenangabe des Deskriptors in Bytes (=0) oder in Kbytes (=1) zu verstehen ist. Da ich noch kein TSS mit 1 Kbyte oder mehr Speicherverbrauch gesehen habe, sollte dies den Wert 0 haben.  
−
(=0) oder in Kbytes (=1) zu verstehen ist. Da ich noch kein TSS mit 1 Kbyte oder mehr
+
* Das ''<span style="background: #90C6FF">D</span>''-Bit gibt an, ob
−
Speicherverbrauch gesehen habe, sollte dies den Wert 0 haben. Das „<span style="background: #90C6FF">D</span>“-Bit gibt an, ob
 
 
der Deskriptor zu dem 286er kompatibel ist, oder nicht. Dort empfehle ich den Wert 1,
 
der Deskriptor zu dem 286er kompatibel ist, oder nicht. Dort empfehle ich den Wert 1,
 
also inkompatibel, dann kann man das Segment vergrößern, ohne sich irgendwann man
 
also inkompatibel, dann kann man das Segment vergrößern, ohne sich irgendwann man
−
damit wieder auseinander setzen zu müssen. Das „<span style="background: #808080">AVL</span>“-Flag steht zur freien
+
damit wieder auseinander setzen zu müssen.  
−
Verfügung. Das „<span style="background: #00FF00">Typ</span>“-Feld haben wir ja schon besprochen, es muss bei einem TSS den
+
* Das ''<span style="background: #808080">AVL</span>''-Flag steht zur freien Verfügung.  
−
Wert 1001b beinhalten.<br>
+
* Das ''<span style="background: #00FF00">Typ</span>''-Feld haben wir ja schon besprochen, es muss bei einem TSS den Wert 1001b beinhalten.
  
−
Wenn ein Task aktiviert wird, so wird sein „Typ“-Feld mit dem Wert „1011“ als
+
Wenn ein Task aktiviert wird, so wird sein ''Typ''-Feld mit dem Wert ''1011'' als
 
Binärzahl belegt. Das zeigt, dass das aktuelle Segment aktiv ist und man nicht zu ihm
 
Binärzahl belegt. Das zeigt, dass das aktuelle Segment aktiv ist und man nicht zu ihm
 
wechseln kann. Davon wissen wir schon, dass man mindestens zwei Tasks braucht,
 
wechseln kann. Davon wissen wir schon, dass man mindestens zwei Tasks braucht,
 
damit Multitasking mit TSS überhaupt richtig funktioniert. Ebenso wird beim Verlassen
 
damit Multitasking mit TSS überhaupt richtig funktioniert. Ebenso wird beim Verlassen
 
eines Tasks der Wert wieder zu 1001b.
 
eines Tasks der Wert wieder zu 1001b.
−
<br><b>4. Task-Tabelle</b><br>
+
 
 +
===Task-Tabelle===
 
Desweiteren braucht man noch, wie besprochen, eine Tabelle, die die
 
Desweiteren braucht man noch, wie besprochen, eine Tabelle, die die
 
Deskriptornummern und TaskIDs speichert. Auch der Status wird dort oft gespeichert,
 
Deskriptornummern und TaskIDs speichert. Auch der Status wird dort oft gespeichert,
Zeile 147: Zeile 150:
 
TaskID und TSS-Nummer. Als <span style="background: #0000FF">Status</span>bereich dürfte auch ein Word (16 Bits) reichen.
 
TaskID und TSS-Nummer. Als <span style="background: #0000FF">Status</span>bereich dürfte auch ein Word (16 Bits) reichen.
 
Auch in dieser Tabelle ist eine Zeile ein Word.
 
Auch in dieser Tabelle ist eine Zeile ein Word.
−
</p><table class="ntable" border="1" bordercolor="#000000" cellspacing="0" cellpadding="2"><tr><td class="tableinfo"><img src="http://www.joachim-neu.de/public/lowlevel/7/3_3.png" alt="Task-Tabelle"><br></td></tr></table><br><p style="text-align:justify">
+
 
 +
<img src="http://www.joachim-neu.de/public/lowlevel/7/3_3.png" alt="Task-Tabelle">
 +
 
 
Es gibt 2 Möglichkeiten, das Ende dieser Tabelle aus Task-Tabellen-Einträgen zu
 
Es gibt 2 Möglichkeiten, das Ende dieser Tabelle aus Task-Tabellen-Einträgen zu
 
finden.
 
finden.
 
1. Man speichert in einer Variable, wie viele Einträge man hat und überprüft das
 
1. Man speichert in einer Variable, wie viele Einträge man hat und überprüft das
 
aktuelle und das maximale Offset, um einen WrapArround (beim Anfang anfangen)
 
aktuelle und das maximale Offset, um einen WrapArround (beim Anfang anfangen)
−
zu vollziehen.<br>
+
zu vollziehen.
 
2. Man gibt am Ende der Tabelle dem nächsten leeren Eintrag die ID 0xFFFF oder ein
 
2. Man gibt am Ende der Tabelle dem nächsten leeren Eintrag die ID 0xFFFF oder ein
−
sonstiges „Endezeichen“ und führt einen WrapArround aus, sobald man als ID den
+
sonstiges "Endezeichen" und führt einen WrapAround aus, sobald man als ID den
−
Wert des Endezeichens gefunden hat.<br>
+
Wert des Endezeichens gefunden hat.
 +
 
 
Das war eigendlich schon das Wichtigste über die Task-Tabelle.
 
Das war eigendlich schon das Wichtigste über die Task-Tabelle.
  
−
<br><b>5. TaskGates</b><br>
+
===TaskGates===
 
Außerdem gehören zu TSS-Multitasking noch TaskGates. TaskGates kann man sowohl
 
Außerdem gehören zu TSS-Multitasking noch TaskGates. TaskGates kann man sowohl
 
in der GDT als auch in der IDT eintragen. Wenn man sie an erste Stelle stellt, so wird
 
in der GDT als auch in der IDT eintragen. Wenn man sie an erste Stelle stellt, so wird
Zeile 164: Zeile 170:
 
bei allen anderen Interrupts und/oder Exceptions. Ein TaskGate ist wie folgt aufgebaut:
 
bei allen anderen Interrupts und/oder Exceptions. Ein TaskGate ist wie folgt aufgebaut:
 
(wieder ist jede Zeile ein Word)
 
(wieder ist jede Zeile ein Word)
−
</p><table class="ntable" border="1" bordercolor="#000000" cellspacing="0" cellpadding="2"><tr><td class="tableinfo"><img src="http://www.joachim-neu.de/public/lowlevel/7/3_4.png" alt="Taskgate"><br></td></tr></table><br><p style="text-align:justify">
 
−
Alle Bereiche, die „<span style="background: #FF0000">Reserviert</span>“ sind, sollten den Wert „0“ beinhalten. Im Bereich
 
−
„<span style="background: #FFFF00">TSS-Deskriptor-Nummer</span>“ wird der Deskriptor festgehalten, auf den das TaskGate verweist.
 
−
Die Felder „<span style="background: #0000FF">P</span>“, „<span style="background: #80FFFF">DPL</span>“ und „<span style="background: #9966CC">S</span>“ habe ich vorher schon erklärt, sodass dies
 
−
hierüberflüssig ist. Der „<span style="background: #00FF00">Typ</span>“ muss bei einem TaskGate den Wert „0101“ als Binärzahl
 
−
beinhalten.<br>
 
  
−
TaskGates werden an sich eigendlich mein Multitasking selten verwendet, sondern
+
<img src="http://www.joachim-neu.de/public/lowlevel/7/3_4.png" alt="Taskgate">
 +
 
 +
Alle Bereiche, die ''<span style="background: #FF0000">Reserviert</span>'' sind, sollten den Wert 0 beinhalten. Im Bereich ''<span style="background: #FFFF00">TSS-Deskriptor-Nummer</span>'' wird der Deskriptor festgehalten, auf den das TaskGate verweist.
 +
 
 +
Die Felder ''<span style="background: #0000FF">P</span>'', ''<span style="background: #80FFFF">DPL</span>'' und ''<span style="background: #9966CC">S</span>'' habe ich vorher schon erklärt, sodass dies
 +
hier überflüssig ist. Der ''<span style="background: #00FF00">Typ</span>'' muss bei einem TaskGate den Wert 0101 als Binärzahl beinhalten.
 +
 
 +
TaskGates werden an sich eigentlich beim Multitasking selten verwendet, sondern
 
lediglich als Einträge in der IDT, damit bei einer Exception oder einem Interrupt ein
 
lediglich als Einträge in der IDT, damit bei einer Exception oder einem Interrupt ein
 
eigener Task aufgerufen wird. Dann wird das NT-Flag gesetzt und die CPU wechselt bei
 
eigener Task aufgerufen wird. Dann wird das NT-Flag gesetzt und die CPU wechselt bei
 
IRET zu dem Task, dessen TSS-Deskriptor im BackLink-Feld eingetragen ist.
 
IRET zu dem Task, dessen TSS-Deskriptor im BackLink-Feld eingetragen ist.
−
<br><b>6. Dispatcher</b><br>
+
 
−
So, nun aber nach dem ganzen Aufbauzeug zur Theorie. Ein Taskwechselalgorythmus
+
===Dispatcher===
 +
So, nun aber nach dem ganzen Aufbauzeug zur Theorie. Ein Taskwechselalgorithmus
 
sollte idealerweise aus zwei Teilen bestehen, dem Scheduler und dem Dispatcher, um
 
sollte idealerweise aus zwei Teilen bestehen, dem Scheduler und dem Dispatcher, um
−
den wir uns nun kümmern wollen. Der Dispatcher ist dafür verantwortlich, dass der
+
den wir uns nun kümmern wollen.  
 +
 
 +
Der Dispatcher ist dafür verantwortlich, dass der
 
Task die CPU zugeteilt und entzogen bekommt. Er muss alle Register laden bzw
 
Task die CPU zugeteilt und entzogen bekommt. Er muss alle Register laden bzw
−
speichern und den eigendlichen Taskwechsel veranlassen. Beim softwarebasierenden
+
speichern und den eigentlichen Taskwechsel veranlassen. Beim softwarebasierenden
 
Multitasking besteht diese Aufgabe des Dispatchers darin, zuerst die Werte des
 
Multitasking besteht diese Aufgabe des Dispatchers darin, zuerst die Werte des
 
aktuellen Tasks auf dessen Stack zu pushen, dann den neuen Stack zu laden und die
 
aktuellen Tasks auf dessen Stack zu pushen, dann den neuen Stack zu laden und die
 
Werte des Tasks zu popen. Bei hardwarebasierendem Multitasking besteht die Aufgabe
 
Werte des Tasks zu popen. Bei hardwarebasierendem Multitasking besteht die Aufgabe
 
lediglich darin, einen Task-Switch zu veranlassen, in dem die CPU dann eigenständig
 
lediglich darin, einen Task-Switch zu veranlassen, in dem die CPU dann eigenständig
−
die Register sichert und die neuen läd.
+
die Register sichert und die neuen lädt.
−
<br><b>7. Scheduler</b><br>
+
 
 +
===Scheduler===
 
Und hier setzt auch schon der Scheduler an. Während der Dispatcher nur die Taskwerte
 
Und hier setzt auch schon der Scheduler an. Während der Dispatcher nur die Taskwerte
 
sichert und die neuen setzt besteht die Aufgabe des Schedulers darin, den nächsten Task
 
sichert und die neuen setzt besteht die Aufgabe des Schedulers darin, den nächsten Task
−
auszuwählen. Dies tut er anhand der Scheduling Strategie. Es gibt verschiedene dieser
+
auszuwählen. Dies tut er anhand der Scheduling-Strategie. Es gibt verschiedene dieser
 
Strategien, welche wir später noch genauer besprechen werden. Bei der einfachsten
 
Strategien, welche wir später noch genauer besprechen werden. Bei der einfachsten
 
Strategie sucht der Scheduler einfach nur den nächsten Task in der Task-Tabelle raus
 
Strategie sucht der Scheduler einfach nur den nächsten Task in der Task-Tabelle raus
Zeile 195: Zeile 206:
 
ist dies nur die Nummer des TSS-Deskriptors, bei softwarebasierendem Multitasking ist
 
ist dies nur die Nummer des TSS-Deskriptors, bei softwarebasierendem Multitasking ist
 
dies SS und ESP des Tasks.
 
dies SS und ESP des Tasks.
−
<br><b>8. Scheduling Strategien</b><br>
+
 
 +
===Scheduling-Strategien===
 
Dies ist einer der umfangreichsten Punkte und der schwierigste, so meinen Experten.
 
Dies ist einer der umfangreichsten Punkte und der schwierigste, so meinen Experten.
 
Das Problem sei ihrer Meinung nach nicht das Wechseln der Tasks sondern das
 
Das Problem sei ihrer Meinung nach nicht das Wechseln der Tasks sondern das
Zeile 203: Zeile 215:
 
sich anfangs mit dem einfachsten Verfahren abgeben sollte, einfach den nächsten Task
 
sich anfangs mit dem einfachsten Verfahren abgeben sollte, einfach den nächsten Task
 
zu nehmen, der in der Tabelle kommt. Allgemein werden an eine Scheduling Strategie
 
zu nehmen, der in der Tabelle kommt. Allgemein werden an eine Scheduling Strategie
−
folgende Anforderungen gestellt:<br>
+
folgende Anforderungen gestellt:
 +
 
 
1. Sie muss fair sein, was heißt, dass alle Tasks von Grund auf gleich zu behandeln
 
1. Sie muss fair sein, was heißt, dass alle Tasks von Grund auf gleich zu behandeln
−
sind, sodass kein Task ständig dran kommt und kein Task nie dran kommt.<br>
+
sind, sodass kein Task ständig dran kommt und kein Task nie dran kommt.
 
2. Sie darf nicht zu komplex sein, sodass siche die CPU mehr mit dieser als mit den
 
2. Sie darf nicht zu komplex sein, sodass siche die CPU mehr mit dieser als mit den
−
Tasks beschäftigen würde.<br>
+
Tasks beschäftigen würde.
 
3. Sie muss unabhängig sein, das heißt, sie darf nicht mit der Anzahl der Tasks
 
3. Sie muss unabhängig sein, das heißt, sie darf nicht mit der Anzahl der Tasks
−
zusammenhängen, zum Beispiel nur bei 5 laufenden Tasks funktionieren.<br>
+
zusammenhängen, zum Beispiel nur bei 5 laufenden Tasks funktionieren.
 +
4. Sie sollte berechenbar sein, also nicht auf Zufallswerten basieren oder auf jedem
 +
System anderst funktionieren.
 +
5. Sie darf nicht beeinflussbar oder manipulierbar sein.
  
−
4. Sie sollte berechenbar sein, also nicht auf Zufallswerten basieren oder auf jedem
 
−
System anderst funktionieren.<br>
 
−
5. Sie darf nicht beeinflussbar oder manipulierbar sein.<br>
 
 
Es gibt viele verschiedene Strategien. Die einen benutzen Prioritätsstufen zum Einteilen
 
Es gibt viele verschiedene Strategien. Die einen benutzen Prioritätsstufen zum Einteilen
 
der Tasks, andere benutzen die Länge der Tasks. Bei Prioritätseinteilungen gibt es
 
der Tasks, andere benutzen die Länge der Tasks. Bei Prioritätseinteilungen gibt es
Zeile 220: Zeile 233:
 
zuzuteilen, oder aber das Privileglevel zu erhöhen, wenn der Task sich selber pausiert
 
zuzuteilen, oder aber das Privileglevel zu erhöhen, wenn der Task sich selber pausiert
 
und damit anderen den Vortritt lässt, wenn er gerade keine Rechenzeit benötigt. Sehr
 
und damit anderen den Vortritt lässt, wenn er gerade keine Rechenzeit benötigt. Sehr
−
häufige Scheduling Strategien sind „Round Robin“, „First Task First“ und „Shortest
+
häufige Scheduling Strategien sind ''Round Robin'', ''First Task First'' und ''Shortest
−
Time Left“. Diese und einige andere werde ich nun ein bisschen erleutern:<br>
+
Time Left''. Diese und einige andere werde ich nun ein bisschen erleutern:
−
– <u>Round Robin</u><br>
+
 
 +
====Round Robin===
 
Bei Round Robin werden alle Tasks in einer Kette hintereinander abgearbeitet und
 
Bei Round Robin werden alle Tasks in einer Kette hintereinander abgearbeitet und
 
jeder bekommt die gleiche Rechenzeit. Wenn man Prioritäten benutzt, so hat man
 
jeder bekommt die gleiche Rechenzeit. Wenn man Prioritäten benutzt, so hat man
Zeile 228: Zeile 242:
 
ist, dass es jedes Prioritätlevel nur einmal gibt und ein Task beim Erhöhen seines
 
ist, dass es jedes Prioritätlevel nur einmal gibt und ein Task beim Erhöhen seines
 
Levels einfach mit seinem direkten oberen Nachbarn tauscht, und dadruch nach oben
 
Levels einfach mit seinem direkten oberen Nachbarn tauscht, und dadruch nach oben
−
kommt.<br>
+
kommt.
−
– <u>First Task First</u><br>
+
 
 +
====First Task First====
 
Dieses Verfahren ist eigendlich mehr für Batchbetrieb, also dem Abarbeiten der
 
Dieses Verfahren ist eigendlich mehr für Batchbetrieb, also dem Abarbeiten der
−
Tasks nacheinander als gleichzeitig gedacht. Es folgt dem Sprichword „wer zuerst
+
Tasks nacheinander als gleichzeitig gedacht. Es folgt dem Sprichwort ''wer zuerst
−
kommt malt zuerst“, sodass ein Programm also den ersten Task ausführt und aber
+
kommt mahlt zuerst'', sodass ein Programm also den ersten Task ausführt und aber
 
derzeit auch noch andere annimmt. Und nach Beendigung dieses Tasks wird der
 
derzeit auch noch andere annimmt. Und nach Beendigung dieses Tasks wird der
 
nächste genommen. Das Verfahren ist unbrauchbar für Systeme mit Mikrokerneln,
 
nächste genommen. Das Verfahren ist unbrauchbar für Systeme mit Mikrokerneln,
 
bei denen Treiber und Module auch Tasks darstellen, da diese nicht aufgerufen
 
bei denen Treiber und Module auch Tasks darstellen, da diese nicht aufgerufen
 
werden können. Desweiteren scheiden sich die Geister, ob diese Methode überhaupt
 
werden können. Desweiteren scheiden sich die Geister, ob diese Methode überhaupt
−
den Namen „Multitasking“ verdient, da die Tasks nicht gleichzeitig ausgeführt
+
den Namen "Multitasking" verdient, da die Tasks nicht gleichzeitig ausgeführt
−
werden, jedoch aber hintereinander.<br>
+
werden, jedoch aber hintereinander.
−
– <u>Shortest Time Left</u><br>
 
  
 +
====Shortest Time Left====
 
Dieses Konzept besagt, dass der Task, der die kleinste Zeit in Anspruch nehmen wird
 
Dieses Konzept besagt, dass der Task, der die kleinste Zeit in Anspruch nehmen wird
 
zuerst an die Reihe kommt. Meist bemisst man die Zeit anhand der Größe des Tasks.
 
zuerst an die Reihe kommt. Meist bemisst man die Zeit anhand der Größe des Tasks.
 
Jedoch müssen diese Werte ständig aktualisiert werden und es ist schwer für das OS
 
Jedoch müssen diese Werte ständig aktualisiert werden und es ist schwer für das OS
 
zu sagen, was Code und was Daten sind. Ein Task könnte nur ein Ladeprogramm
 
zu sagen, was Code und was Daten sind. Ein Task könnte nur ein Ladeprogramm
−
beinhalten, dass den richtigen Task läd und würde somit als erstes ausgeführt und
+
beinhalten, dass den richtigen Task lädt und würde somit als erstes ausgeführt und
−
umgeht damit das System. Somit ist einer der Grundsätze nicht erfüllt.<br>
+
umgeht damit das System. Somit ist einer der Grundsätze nicht erfüllt.
−
– <u>Longest Time Left</u><br>
+
 
 +
====Longest Time Left====
 
Dieses Konzept ist exakt das Gegenteil des vorherigen. Es bevorzugt den längsten
 
Dieses Konzept ist exakt das Gegenteil des vorherigen. Es bevorzugt den längsten
 
Task und wieder gelten hier alle Einwände, welche bei „Shortest Time Left“ genannt
 
Task und wieder gelten hier alle Einwände, welche bei „Shortest Time Left“ genannt
−
wurden.<br>
+
wurden.
 +
 
 
Das waren auch schon die meiner Meinung nach wichtigsten. Es steht jedem frei,
 
Das waren auch schon die meiner Meinung nach wichtigsten. Es steht jedem frei,
 
weitere dazu zu entwickeln und bestehende zu verfeinern. Ich selber benutze Round
 
weitere dazu zu entwickeln und bestehende zu verfeinern. Ich selber benutze Round
 
Robin ohne jegliche Form von Prioritäten.
 
Robin ohne jegliche Form von Prioritäten.
−
<br><b>9. Taskwechsel</b><br>
+
 
 +
===Taskwechsel===
 
Nun aber zum meiner Meinung kompliziertesten Punkt der Praxis, dem Wechsel selber.
 
Nun aber zum meiner Meinung kompliziertesten Punkt der Praxis, dem Wechsel selber.
 
Das Implementieren eines Round-Robin-Schedulers sollte nicht alzu schwer gewesen
 
Das Implementieren eines Round-Robin-Schedulers sollte nicht alzu schwer gewesen
Zeile 260: Zeile 278:
 
hatte, als ich TSS für mein Betriebssystem implementierte.
 
hatte, als ich TSS für mein Betriebssystem implementierte.
 
Zuerst möchte ich schildern, wodurch ein Taskwechsel an sich ausgelöst wird. Es gibt
 
Zuerst möchte ich schildern, wodurch ein Taskwechsel an sich ausgelöst wird. Es gibt
−
mehrere Möglichkeiten:<br>
+
mehrere Möglichkeiten:
 +
 
 
1. Ein Far-Jump oder Far-Call bei dem als Selektor ein TSS angegeben wird. Der
 
1. Ein Far-Jump oder Far-Call bei dem als Selektor ein TSS angegeben wird. Der
−
Offsetteil wird hierbei ignoriert.<br>
+
Offsetteil wird hierbei ignoriert.
 
2. Ein IRET nachdem ein Task durch ein TaskGate in der IDT aufgerufen wird, also das
 
2. Ein IRET nachdem ein Task durch ein TaskGate in der IDT aufgerufen wird, also das
−
NT-Flag gesetzt ist.<br>
+
NT-Flag gesetzt ist.
 
3. Ein Far-Jump oder Far-Call auf ein Task-Gate. Auch hier wird der Offsetteil
 
3. Ein Far-Jump oder Far-Call auf ein Task-Gate. Auch hier wird der Offsetteil
−
ignoriert.<br>
+
ignoriert.
 +
 
 +
Es gibt also 2 Möglichkeiten:
 +
* Man macht einen Far-Jump auf ein TSS.
 +
* Man manipuliert das NT-Flag und simuliert den Aufruf als ein Interrupt durch ein
 +
TaskGate.
  
−
Es gibt also 2 Möglichkeiten:<br>
 
−
1. Man macht einen Far-Jump auf ein TSS.<br>
 
−
2. Man manipuliert das NT-Flag und simuliert den Aufruf als ein Interrupt durch ein
 
−
TaskGate.<br>
 
 
Die zweite Methode habe ich noch nie erfolgreich zum Laufen gebracht und sie ist
 
Die zweite Methode habe ich noch nie erfolgreich zum Laufen gebracht und sie ist
 
zweifellos die schwierigere, da man nicht nur die Flags sondern auch BackLink-Felder
 
zweifellos die schwierigere, da man nicht nur die Flags sondern auch BackLink-Felder
 
und Stacks manipulieren muss. Deswegen werde ich nun auch die erste Methode
 
und Stacks manipulieren muss. Deswegen werde ich nun auch die erste Methode
−
ausgiebig erleutern.<br>
+
ausgiebig erläutern.
−
Nun, man möchte sich nun denken „nichts leichter als das“ und schreibt munter in
+
 
−
seinen Code „JMP AX,0x00000000“, wobei man gedenkt, AX mit der
+
Nun, man möchte sich nun denken "nichts leichter als das" und schreibt munter in
 +
seinen Code <tt>JMP AX,0x00000000</tt>, wobei man gedenkt, AX mit der
 
Deskriptornummer zu beladen. Spätestens beim Versuch zu Assemblieren wird man
 
Deskriptornummer zu beladen. Spätestens beim Versuch zu Assemblieren wird man
 
feststellen: das geht nicht. Man muss sich also was anderes ausdenken. Erstmal möchte
 
feststellen: das geht nicht. Man muss sich also was anderes ausdenken. Erstmal möchte
−
ich sicherheitshalber das ganze zu diesem ausbauen: „JMP DWORD AX:0x12345678“.
+
ich sicherheitshalber das ganze zu diesem ausbauen: <tt>JMP DWORD AX:0x12345678</tt>.
−
So wird erzwingt, dass der Assembler auch den kompletten Offsetteil mit übersetzt und
+
So wird erzwungen, dass der Assembler auch den kompletten Offsetteil mit übersetzt und
 
nicht Teile durch Abkürzen unter den Tisch kehrt. Das Offset habe ich zu dieser
 
nicht Teile durch Abkürzen unter den Tisch kehrt. Das Offset habe ich zu dieser
 
Zahlenfolge gemacht, da ich es für noch ein Schritt sicherer halte, dass das ganze nicht
 
Zahlenfolge gemacht, da ich es für noch ein Schritt sicherer halte, dass das ganze nicht
 
gekürzt wird. Noch haben wir aber das Problem mit dem AX nicht geklärt. Da dies auf
 
gekürzt wird. Noch haben wir aber das Problem mit dem AX nicht geklärt. Da dies auf
−
jeden Fall nicht geht wandeln wir auch das um: „JMP DWORD 0x1234:0x12345678“.
+
jeden Fall nicht geht wandeln wir auch das um: <tt>JMP DWORD 0x1234:0x12345678</tt>
 
Dies wird der Assembler zwar problemlos schlucken, aber leider bringt es uns nicht
 
Dies wird der Assembler zwar problemlos schlucken, aber leider bringt es uns nicht
 
viel. Wie man vielleicht schon merkt setze ich viel darauf, dass der Assembler dies
 
viel. Wie man vielleicht schon merkt setze ich viel darauf, dass der Assembler dies
 
vollständig und ungekürzt in die Opcodes umwandelt, denn das ist unser Angriffspunkt:
 
vollständig und ungekürzt in die Opcodes umwandelt, denn das ist unser Angriffspunkt:
 
HexDump. Wir manipulieren einfach den vom Assembler erstellten Maschinencode
 
HexDump. Wir manipulieren einfach den vom Assembler erstellten Maschinencode
−
während der Ausführung. Ein paar Versuche haben ergeben: „JMP DWORD
+
während der Ausführung. Ein paar Versuche haben ergeben: <tt>JMP DWORD
−
0x1234:0x12345678“ wird vom Assembler in die Bytes 0xED, 0x78, 0x56, 0x34, 0x12,
+
0x1234:0x12345678</tt> wird vom Assembler in die Bytes 0xED, 0x78, 0x56, 0x34, 0x12,
 
0x34 und 0x12 umgewandelt. Wir sehen also, dass die letzten zwei Bytes den Selektor
 
0x34 und 0x12 umgewandelt. Wir sehen also, dass die letzten zwei Bytes den Selektor
 
angeben. Wir bleiben also weiterhin dabei, in AX unsere TSS-Deskriptornummer zu
 
angeben. Wir bleiben also weiterhin dabei, in AX unsere TSS-Deskriptornummer zu
Zeile 298: Zeile 319:
 
derartigen Verdrehungen, da die CPU andere Zahlendarstellungsverfahren benutzt, als
 
derartigen Verdrehungen, da die CPU andere Zahlendarstellungsverfahren benutzt, als
 
der Mensch und die Zahlen konvertiert werden (näheres in meinem Tutorial
 
der Mensch und die Zahlen konvertiert werden (näheres in meinem Tutorial
−
„Datenveränderungen im RAM“). Wir können also getrost einfach ein Word an die
+
''Datenveränderungen im RAM''). Wir können also getrost einfach ein Word an die
 
Stelle mit dem Selektor schreiben, der sich 5 Bytes hinter Befehlbeginn befindet. Den
 
Stelle mit dem Selektor schreiben, der sich 5 Bytes hinter Befehlbeginn befindet. Den
 
Befehlsbeginn finden wir anhand eines Labels und schon haben wir alles zusammen um
 
Befehlsbeginn finden wir anhand eines Labels und schon haben wir alles zusammen um
 
folgenden Code zu bauen:
 
folgenden Code zu bauen:
−
</p><pre class="code"><div id="title"><br></div>
+
 
 +
<pre>
 
PUSH EDI
 
PUSH EDI
 
MOV EDI, tss_switch_label ; Offset laden
 
MOV EDI, tss_switch_label ; Offset laden
Zeile 310: Zeile 332:
 
JMP DWORD 0x1234:0x12345678 ; springen!
 
JMP DWORD 0x1234:0x12345678 ; springen!
 
POP EDI
 
POP EDI
−
</pre><br><p style="text-align:justify">
+
</pre>
 +
 
 
Der Code verlangt lediglich, dass sich in AX die TSS-Deskriptornummer des Tasks
 
Der Code verlangt lediglich, dass sich in AX die TSS-Deskriptornummer des Tasks
 
befindet, zu dem gewechselt werden soll. Alles andere macht die CPU. Somit dürfte das
 
befindet, zu dem gewechselt werden soll. Alles andere macht die CPU. Somit dürfte das
 
Wechseln auch kein Problem mehr sein.
 
Wechseln auch kein Problem mehr sein.
−
<br><b>10. Implementierung und Praxis</b><br>
+
 
 +
===Implementierung und Praxis===
 
Tja, wir haben jetzt alles wichtige besprochen, Zeit, zusammen mal einen Code zu
 
Tja, wir haben jetzt alles wichtige besprochen, Zeit, zusammen mal einen Code zu
 
coden, den ihr in euer TimerIRQ einbauen könnt, und der für euch den Taskwechsel
 
coden, den ihr in euer TimerIRQ einbauen könnt, und der für euch den Taskwechsel
 
macht. Zuerst einmal brauchen wir den Scheduler. Ich gehe davon aus, dass die Task-
 
macht. Zuerst einmal brauchen wir den Scheduler. Ich gehe davon aus, dass die Task-
 
Tabelle an der Adresse 0x00005000 liegt. Die Größe wird anhand einer Variable
 
Tabelle an der Adresse 0x00005000 liegt. Die Größe wird anhand einer Variable
−
angegeben, die das Offset des Endes beinhaltet, welche „tasking_end“ heißt und vom
+
angegeben, die das Offset des Endes beinhaltet, welche ''tasking_end'' heißt und vom
 
Typ Double Word ist. Außerdem existiert eine Variable mit dem Namen
 
Typ Double Word ist. Außerdem existiert eine Variable mit dem Namen
−
„tasking_current“, welche ebenfalls vom Typ Double Word ist und den aktuellen Punkt
+
''tasking_current'', welche ebenfalls vom Typ Double Word ist und den aktuellen Punkt
−
in der Tabelle festhält.<br>
+
in der Tabelle festhält.
  
 
In unserem Code bauen wir reines Round Robin, wir brauchen also Task-Tabellen-
 
In unserem Code bauen wir reines Round Robin, wir brauchen also Task-Tabellen-
 
Einträge nach der Form, welche unter „4. Task-Tabelle“ gezeigt ist. Ein Eintrag ist also
 
Einträge nach der Form, welche unter „4. Task-Tabelle“ gezeigt ist. Ein Eintrag ist also
−
6 Bytes groß.<br>
+
6 Bytes groß.
 +
 
 
Coden wir nun einfach mal den Scheduler. Da wir am Anfang des IRQs keinen
 
Coden wir nun einfach mal den Scheduler. Da wir am Anfang des IRQs keinen
 
Dispatcher brauchen, können wir gleich mit dem Scheduler anfangen.
 
Dispatcher brauchen, können wir gleich mit dem Scheduler anfangen.
−
</p><pre class="code"><div id="title"><br></div>
+
 
 +
<pre>
 
ADD DWORD [tasking_current],0x00000006 ;Werte des
 
ADD DWORD [tasking_current],0x00000006 ;Werte des
 
; aktuellen Tasks überspringen
 
; aktuellen Tasks überspringen
Zeile 425: Zeile 451:
 
POP EDI
 
POP EDI
 
POP EAX
 
POP EAX
−
</pre><br><p style="text-align:justify">
+
</pre>
 +
 
 
So, fertig ist der Praxisteil. Ich bitte alle Leser, diesen Code nicht einfach nur blind zu
 
So, fertig ist der Praxisteil. Ich bitte alle Leser, diesen Code nicht einfach nur blind zu
 
kopieren, denn er lässt sich sicher nicht fehlerlos in das System integrieren, und so
 
kopieren, denn er lässt sich sicher nicht fehlerlos in das System integrieren, und so
 
schwer ist das Implementieren nicht. Also selber versuchen und bei Fragen mich einfach
 
schwer ist das Implementieren nicht. Also selber versuchen und bei Fragen mich einfach
−
kontaktieren.<br>
+
kontaktieren.
 +
 
 
Damit dieser Code richtig geht brauchen wir aber auch einen Task, der gerade läuft.
 
Damit dieser Code richtig geht brauchen wir aber auch einen Task, der gerade läuft.
 
Angezeigt wird dieser Task durch das TR, das TaskRegister. Dieses beinhaltet einen
 
Angezeigt wird dieser Task durch das TR, das TaskRegister. Dieses beinhaltet einen
Zeile 436: Zeile 464:
 
Deskriptornummer des Tasks, der gerade aktiv ist angegeben. Beim Wechsel aktualisiert
 
Deskriptornummer des Tasks, der gerade aktiv ist angegeben. Beim Wechsel aktualisiert
 
die CPU das TR dann automatisch. Man braucht den Befehl also nur einmal.
 
die CPU das TR dann automatisch. Man braucht den Befehl also nur einmal.
−
<br><b>11. Tipps & Tricks</b><br>
+
 
 +
===Tipps & Tricks===
 
Jetzt habe ich zu Schluss noch ein paar Tipps zum Programmieren von Multitasking und
 
Jetzt habe ich zu Schluss noch ein paar Tipps zum Programmieren von Multitasking und
−
zum Programmieren allgemein.<br>
+
zum Programmieren allgemein.
 +
 
 
1. Programmiere auf Sicherheit! Es geht nicht um einen Geschwindigkeitsrekord,
 
1. Programmiere auf Sicherheit! Es geht nicht um einen Geschwindigkeitsrekord,
 
sondern um ein stabiles Betriebssystem. Deswegen beachte und bedenke jeden
 
sondern um ein stabiles Betriebssystem. Deswegen beachte und bedenke jeden
Zeile 447: Zeile 477:
 
2. Schreibe nicht alles ab! Code den Code selber. Es wird dir helfen, das Zeug zu
 
2. Schreibe nicht alles ab! Code den Code selber. Es wird dir helfen, das Zeug zu
 
verstehen und eigener Code lässt sich besser ins System integrieren.<br>
 
verstehen und eigener Code lässt sich besser ins System integrieren.<br>
−
 
 
3. Entscheide dich fest für ein System und mach keine Abstriche. Versuche deine
 
3. Entscheide dich fest für ein System und mach keine Abstriche. Versuche deine
 
Gedanken durchzusetzen und gib' nicht auf, blos, weil was nicht klappt.<br>
 
Gedanken durchzusetzen und gib' nicht auf, blos, weil was nicht klappt.<br>
Zeile 454: Zeile 483:
 
5. Halte alles Modular, sprich baue auch für deinen Scheduler eine Funktion. Das wird
 
5. Halte alles Modular, sprich baue auch für deinen Scheduler eine Funktion. Das wird
 
es später erleichtern, die Funktion auszutauschen, wenn du eine andere Strategie
 
es später erleichtern, die Funktion auszutauschen, wenn du eine andere Strategie
−
verwenden willst.<br>
+
verwenden willst.
 
6. Und als letztes: Kommentiere deinen Code gut. Gerade in Assembler ist das sehr
 
6. Und als letztes: Kommentiere deinen Code gut. Gerade in Assembler ist das sehr
−
wichtig.<br>
+
wichtig.
 +
 
 
Tja, das waren auch schon meine Tipps.
 
Tja, das waren auch schon meine Tipps.
−
<br><b>12. Schluss</b><br>
+
 
 +
===Schluss===
 
Wir sind nun schon am Ende dieses Tutorials angelangt. Es ist etwas lang geworden,
 
Wir sind nun schon am Ende dieses Tutorials angelangt. Es ist etwas lang geworden,
 
warscheinlich deshalb, dass Multitasking nunmal ein komplexes Thema ist, dass man
 
warscheinlich deshalb, dass Multitasking nunmal ein komplexes Thema ist, dass man