Commit 26337b29 authored by Bernhard Heinloth's avatar Bernhard Heinloth
Browse files

Vorgabe oostubs Aufgabe 4

parent 9a54c2d2
; vim: set et ts=4 sw=4:
%include "machine/toc.inc"
; EXPORTIERTE FUNKTIONEN
[GLOBAL toc_switch]
[GLOBAL toc_go]
[SECTION .text]
; IMPLEMENTIERUNG DER FUNKTIONEN
; TOC_GO : Startet den ersten Thread ueberhaupt.
;
; C Prototyp: void toc_go (struct toc *regs);
align 8
toc_go:
; TOC_SWITCH : Threadumschaltung. Der aktuelle Registersatz wird
; gesichert und der Registersatz des neuen "thread of control"
; wird in den Prozessor eingelesen.
;
; C Prototyp: void toc_switch (struct toc *regs_now,
; struct toc *reg_then);
align 8
toc_switch:
// vim: set et ts=4 sw=4:
#include "machine/toc.h"
// TOC_SETTLE: bereitet den Kontext der Koroutine fuer den ersten Aufruf vor.
void toc_settle (struct toc *regs, void *tos, void (*kickoff)(Thread*), Thread *object)
{
}
// vim: set et ts=4 sw=4:
/*! \file
* \brief Enthält die Struktur 'Thread of context'.
*/
#pragma once
/*! \brief Die Struktur toc dient dazu, bei einem Koroutinenwechsel die Werte
* der nicht-flüchtigen Register zu sichern.
*
* Beim GNU C Compiler sind `eax`, `ecx` und `edx` flüchtige Register, die bei
* Funktionsaufrufen und somit auch bei einem Koroutinenwechsel keine später
* noch benötigten Werte haben duerfen. Daher muss in der Struktur toc auch
* kein Platz für sie bereitgestellt werden.
*
* \note Für den Zugriff auf die Elemente der Struktur ::toc aus einer
* Assemblerfunktion heraus werden in der Datei *toc.inc* Namen für die
* benötigten Abstände der einzelnen Elemente zum Anfang der Struktur
* definiert. Damit dann auch auf die richtigen Elemente zugegriffen wird,
* müssen sich die Angaben von toc.h und *toc.inc* exakt entsprechen. Wer also
* toc.h ändert, muss auch *toc.inc* anpassen (und umgekehrt).
*/
struct toc {
public:
void *ebx;
void *esi;
void *edi;
void *ebp;
void *esp;
} __attribute__ ((packed));
class Thread;
/*! \brief Diese Funktion bereitet ein struct toc für die erste Aktivierung
* vor.
*
* Dazu müssen Registerinhalte und Stack so initialisiert werden, dass bei
* der ersten Aktivierung die Ausführung mit der Funktion kickoff beginnt,
* die wiederum den Zeiger object als ersten Parameter auf dem Stack
* vorfinden muss.
*
* toc_settle kann in C++ in der Datei toc.cc implementiert werden und
* deshalb muss sie nicht als extern "C" deklariert werden.
*
* \param regs Kontext, der auf eine Aktivierung vorbereitet werden soll
* \param tos Zeiger auf die oberste Adresse des Stackbereichs.
* \param kickoff Adresse der Funktion kickoff, mit der die Ausführung
* eines Fadens beginnt.
* \param object Zeiger auf ein Thread Objekt; Parameter für die
* Funktion kickoff.
*
* \todo Funktion implementieren
*
*/
void toc_settle(struct toc *regs, void *tos, void (*kickoff)(Thread*),
Thread *object);
extern "C" {
/*! \brief Diese Funktion lädt die Prozessorregister mit den Inhalten aus
* \b regs.
*
* toc_go wird verwendet, um auf jeder CPU den ersten Thread überhaupt zu
* starten. Die Implementierung von toc_go muss in Assembler in der Datei
* toc.asm erfolgen. Sie muss als extern "C" deklariert werden, da sie als
* Assemblerfunktion nicht dem C++ Namemangeling unterliegt.
* \param regs Initialer Kontext der Koroutine, die gestartet werden soll.
*
* \todo Methode implementieren
*
*/
void toc_go(struct toc *regs);
/*! \brief Diese Funktion führt einen Kontextwechsel durch. Dazu brauchen
* nur die aktuellen Registerwerte in regs_now gesichert und durch die
* Werte von regs_then ersetzt zu werden.
*
* Auch toc_switch wird in Assembler in der Datei toc.asm implementiert.
* Sie muss als extern "C" deklariert werden, da sie als
* Assemblerfunktion nicht dem C++ Namemangeling unterliegt.
* \param regs_now Zeiger auf ein struct toc, in den der
* aktuelle Prozessorkontext gesichert werden soll.
* \param reg_then Zeiger auf ein struct toc, der den zu
* ladenden Kontext enthält.
*
* \todo Methode implementieren
*
*/
void toc_switch(struct toc *regs_now, struct toc *reg_then);
}
; vim: set et ts=4 sw=4:
; Die folgenden Angaben belegen ebx_offset mit dem Wert 0, esi_offset mit 4
; edi_offset mit 8 usw. Dies entspricht den Abständen der Elemente ebx, esi,
; edi usw. der Struktur toc zum Anfang der Struktur (in Bytes).
; Die Reihenfolge der Registerbezeichnungen muss unbedingt mit der von
; der Struktur toc übereinstimmen.
[ABSOLUTE 0]
ebx_offset: resd 1
esi_offset: resd 1
edi_offset: resd 1
ebp_offset: resd 1
esp_offset: resd 1
// vim: set et ts=4 sw=4:
#include "thread/dispatcher.h"
// vim: set et ts=4 sw=4:
#pragma once
/*! \file
*
* \brief Enthält die Klasse Dispatcher.
*/
#include "thread/thread.h"
/*! \brief Der Dispatcher lastet Threads ein und setzt damit die Entscheidungen der Ablaufplanung durch.
* \ingroup thread
*
*
* \note
* Der Dispatcher verwaltet den Life-Pointer, der die jeweils aktive Koroutine
* angibt und führt die eigentlichen Prozesswechsel durch. In der
* Uniprozessorvariante wird nur ein einziger Life-Pointer benötigt, da
* lediglich ein Prozess auf einmal aktiv sein kann. Für die
* Mehrprozessorvariante wird hingegen für jede CPU ein eigener Life-Pointer
* benötigt.
*
*/
class Dispatcher
{
// Verhindere Kopien und Zuweisungen
Dispatcher(const Dispatcher&) = delete;
Dispatcher& operator=(const Dispatcher&) = delete;
protected:
void setActive(Thread* c) {
}
public:
/*! \brief Konstruktor
*
* Der Konstruktor initialisiert den oder die Life-Pointer mit Null,
* um anzuzeigen, dass auf der jeweiligen CPU noch keine Koroutine bekannt
* ist.
*
* \todo Konstruktor implementieren
*
*/
Dispatcher()
{
}
/*! \brief Hiermit kann abgefragt werden, welche Koroutine gerade im Besitz
* des aktuellen Prozessors ist.
*
*
* \todo Methode implementieren
*
*/
Thread* active() {
return 0;
}
/*! \brief Mit dieser Methode wird die Koroutine first im Life-Pointer des
* aktuellen Prozessores vermerkt und gestartet.
* \param first Erste auf dem aktuellen Prozessor zu startende Koroutine
* im System.
*
* \todo Methode implementieren
*
*/
void go(Thread *first);
/*! \brief Diese Methode setzt den Life-Pointer des aktuellen Prozessors auf
* next und führt einen Koroutinenwechsel vom alten zum neuen Life-Pointer
* durch.
* \param next Nächste auszuführende Koroutine.
*
* \todo Methode implementieren
*
*/
void dispatch(Thread *next);
/*! \brief Funktion zum Starten eines Threads.
*
* Für die allererste Aktivierung eines Threads muss eine "Rücksprungadresse"
* zu einer Funktion bekannt sein, von der aus dann der Übergang von der C- zur
* C++-Ebene erfolgen kann. Hierfür dient die Funktion kickoff.
*
* <b>Aktivierung von kickoff</b>
* Bei der Initialisierung eines Threads mit Hilfe der Funktion toc_settle()
* wird nicht nur die Struktur toc für den ersten Threadwechsel
* vorbereitet, sondern auch die Adresse der Funktion kickoff als
* Rücksprungadresse auf den Stack gelegt. Beim ersten Threadwechsel
* mit toc_switch wird daher an den Anfang von kickoff "zurück"gesprungen,
* womit die Ausführung beginnt.
*
* Diese Methode realisiert den Sprung von der C-Ebene zur C++-Ebene, indem sie
* ganz einfach die Methode action() des als Parameter vorgefundenen
* Threadobjektes aufruft.
*
* \b Hinweis
* Da diese Funktion nicht wirklich aufgerufen, sondern nur durch eine
* geschickte Initialisierung des Stacks der Koroutine angesprungen wird, darf
* sie nie terminieren. Anderenfalls würde ein sinnloser Wert als
* Rücksprungadresse interpretiert werden und der Rechner abstürzen.
*
* \param object Thread, der gestartet werden soll.
*
* \todo Methode implementieren
*
*/
static void kickoff(Thread* object);
};
// vim: set et ts=4 sw=4:
#include "thread/scheduler.h"
// vim: set et ts=4 sw=4:
#pragma once
/*! \file
*
* \brief Enthält die Klasse Scheduler
*/
#include "thread/dispatcher.h"
#include "thread/thread.h"
#include "object/queue.h"
/*! \brief Der Scheduler implementiert die Ablaufplanung und somit die Auswahl des nächsten Threads.
* \ingroup thread
*
*
* \note Der Scheduler verwaltet die Ready-Liste (ein privates Queue Objekt der
* Klasse), also die Liste der lauffähigen Threads. Die
* Liste wird von vorne nach hinten abgearbeitet. Dabei werden Threads, die
* neu im System sind oder den Prozessor abgeben, stets an das Ende der Liste
* angefügt.
*/
class Scheduler
: public Dispatcher
{
// Verhindere Kopien und Zuweisungen
Scheduler(const Scheduler&) = delete;
Scheduler& operator=(const Scheduler&) = delete;
public:
/*! \brief Konstruktor
*
*/
Scheduler() {}
/*! \brief Starten des Schedulings
*
* Diese Methode setzt das Scheduling in Gang, indem der erste
* Thread von der Ready-Liste entfernt und aktiviert wird. In MPStuBS muss
* diese Methode auf jeder CPU einmal aufgerufen werden, um auf dem
* jeweiligen Prozessor den ersten Thread einzulasten.
*
* \todo Methode implementieren
*
*/
void schedule();
/*! \brief Anmelden eines Threads zum Scheduling
*
* Mit dieser Methode wird der Thread that beim Scheduler angemeldet. Er
* wird an das Ende der Ready-Liste angefügt.
* \param that Thread, der angemeldet werden soll.
*
* \todo Methode implementieren
*
*/
void ready(Thread *that);
/*! \brief Selbstbeenden des aktuellen Threads.
*
* Hiermit kann sich ein Thread selbst beenden. Er wird nun nicht wieder
* an das Ende der Ready-Liste angefügt. Statt dessen wird nur der erste
* Thread von der Ready-Liste heruntergenommen und aktiviert.
*
* \todo Methode implementieren
*
*/
void exit();
/*! \brief Beenden eines beliebigen Threads
*
* Mit dieser Methode kann ein Thread einen anderen (\b that) beenden.
* In OOStuBS genügt es einfach den Thread \b that von der Ready-Liste
* zu entfernen. Er wird somit nie wieder vom Prozessor ausgeführt werden.
* In MPStuBS ist die Implementierung etwas kniffliger, da der Thread
* \b that auch während der Ausführung von kill auf anderen Prozessoren
* laufen kann. Wird \b that nicht in der Ready-Liste gefunden, so muss
* bei ihm vermerkt werden, dass der
* Thread beendet werden soll. Dies muss in resume überprüft werden,
* bevor ein Thread wieder in die Ready-Liste eingetragen wird.
* \param that Thread, der beendet werden soll.
*
* \todo Methode implementieren
*
*/
void kill(Thread *that);
/*! \brief Auslösen eines Threadwechsels
*
* Hiermit kann ein Threadwechsel ausgelöst werden, ohne dass der
* aufrufende Thread wissen muss, welche anderen Thread Objekte im System
* existieren und welcher davon sinnvollerweise aktiviert werden sollte.
* Diese Entscheidung trifft der Scheduler anhand der Einträge seiner
* Ready-Liste. Der Scheduler soll den gerade laufenden Thread
* an das Ende der Ready-Liste anfügen und den ersten Thread in der
* Ready-Liste aktivieren.
*
* \todo Methode implementieren
*
*/
void resume();
};
// vim: set et ts=4 sw=4:
#include "thread/thread.h"
#include "thread/dispatcher.h"
// vim: set et ts=4 sw=4:
/*! \file
* \brief Enthält die Klasse Thread
*/
/*! \defgroup thread Multithreading
* \brief Das Multithreading Subsystem
*
* Die Multithreading Gruppe enthält alle Elemente die Grundlegend für
* das CPU Multiplexing benötigt werden. Ziel dieses Modules ist es
* die Abstraktion des Threads an zu bieten, die für die Anwendung
* eine virtualisierte CPU bereit stellt.
*/
#pragma once
#include "machine/toc.h"
#include "object/queuelink.h"
/*! \brief Der Thread ist das Objekt der Ablaufplanung.
* \ingroup thread
*/
class Thread
{
public:
/*! \brief Konstruktor.
*
*
* Initialisiert den Kontext mit Hilfe von toc_settle
* \param tos Top of Stack, also die höchste Adresse des Speicherbereiches,
* der als Stack für diesen Thread fungieren soll.
*/
Thread(void* tos);
/*! \brief Verkettungszeiger für Scheduler und Waitingroom */
QueueLink<Thread> queue_link;
private:
struct toc regs;
public:
/*! \brief Aktiviert den ersten Thread auf einem Prozessor.
*
* Durch den Aufruf von Thread::go() wird der erste Thread auf dem
* jeweiligen Prozessor gestartet. Alle weiteren Threadwechsel sollen
* dann mit Hilfe der Methode Thread::resume() ausgeführt werden.
*
* \todo Methode implementieren
*
*/
void go();
/*! \brief Wechsel von dem aktuell laufenden Thread zum nächsten.
*
* Die aktuelle Belegung der nicht-flüchtigen Register wird in dem toc
* Element gesichert und durch die Werte von next (dem toc Element des
* nächsten Threads) ersetzt.
* \param next Zeiger auf den nächsten Thread, der laufen soll.
*
* \todo Methode implementieren
*
*/
void resume(Thread *next);
/*! \brief Methode, die als Thread ausgeführt werden soll.
*
* Abgeleitete Klassen können durch Überschreiben dieser Methode den Code
* angeben, der als Thread ausgeführt werden soll.
*/
virtual void action() = 0;
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment