Bis jetzt hatten wir in unserem Stubs einen festen Faden pro CPU, haben also für jede
Anwendung einen Kern exklusiv verwendet.
Das wollen wir in dieser Aufgabe mittels kooperativen Scheduling so ändern, dass wir quasi beliebig
viele Anwendungen gleichzeitig laufen lassen können.
Im Beispiel laufen nur 9 Zähleranwendungen parallel.
Dabei wird in der Ausgabe für jeden Kern eine eigene Farbe verwendet, um das Durchwechseln
zu verdeutlichen.
Externe Geräte wie die Tastatur bleiben natürlich auch weiterhin aktiv.
Für die Umsetzung müssen wir nun erstmal down the rabbit hole und sowohl tiefer in
die x86 Architektur als auch die System5 API einarbeiten, um in Assembler einen Kontextwechsel
implementieren zu können.
Im Anschluss sollen die Anwendungen so umgebaut werden, dass diese als Threads in einem Scheduler
verwaltet werden können.
Diese Verwaltung soll im Ordner thread bereitgestellt werden.
Der systemneue Kontextwechsel wird in machine implementiert.
Der Scheduler erlaubt Threads zur Ausführung dynamisch hinzuzufügen und wieder zu entfernen,
mittels einer Liste von Zeigern auf Thread-Objekten.
Mit guarded Scheduler gibt es eine einfache Systemaufruf Schnittstelle, damit dies auch
von Anwendungen selbst gesteuert werden kann, zum Beispiel das Beenden einer anderen Anwendung.
Die Anwendungen selbst sind von der Klasse thread-abgeleitet und werden in der Methode
Action implementiert.
Diese Methode ist in thread als pure virtual deklariert, muss also zwingend in jeder Ableitung
implementiert werden.
Eine Anwendung, welche das erste Mal per Kontextswitch eingelastet wird, soll ihre Ausführung bei
der Methode Action beginnen.
Wir bräuchten also die Adresse dieser Methode als Einsprungsfunktion.
Allerdings ist diese Adresse gar nicht zu trivial zu ermitteln, wenn wir nur den Pointer
zum thread-Objekt haben.
Sie ist ein virtueller Member, die tatsächliche Adresse der Funktion wird also dynamisch zur
Laufzeit durch die Vtable der Tabelle virtueller Methoden ermittelt, abhängig von welcher
Anwendung das Objekt tatsächlich ist.
Und wie so eine Vtable aufgebaut ist, ist natürlich wieder nicht im C++-Standard spezifiziert,
sondern abhängig vom jeweiligen Übersetzer.
Wir wollen aber eine generische Lösung, versuchen also gar nicht erst selbst diese Struktur
nachzulaufen, sondern lassen den Übersetzer den entsprechenden Code generieren, mittels
der Hilfsfunktion kickoff.
Diese bekommt als Parameter den Zeiger auf den thread und ruft damit dann Action auf.
Der Übersetzer muss für diesen Aufruf in kickoff entsprechend selbst die Auflösung
mittels der Vtable einbauen.
Unsere Einsprungsfunktion ist somit nun also nicht die Methode Action selbst, sondern kickoff,
welches dann eben diese Methode aufruft.
Und entsprechend müssen wir auch den Stack vorbereiten, was wir über die Funktion prepare
context erledigen.
Diese erhält als ersten Parameter die Adresse des Top of Stack, also das obere Ende des
jeweiligen Stacks.
Dazu soll für jeden Thread ein 4096 Byte großer Speicher reserviert werden.
Der zweite Parameter ist der Funktionszeiger zur eben erwähnten kickoff-Funktion, welcher
mit den in param spezifizierten Parameter aufgerufen wird.
Der Rückkabewert ist die Adresse des letzten Eintrags, die auf diesem Stack hinzugefügt
wurde, somit also der initiale Stackpointer für den Thread.
Presenters
Zugänglich über
Offener Zugang
Dauer
00:11:58 Min
Aufnahmedatum
2020-08-12
Hochgeladen am
2021-09-20 19:16:12
Sprache
de-DE
Aufgabe 4 der Lehrveranstaltung Betriebssysteme.
Folien und Transkript zum Video.