migrate coroutines to threads
This commit is contained in:
113
c_os/kernel/threads/Thread.asm
Executable file
113
c_os/kernel/threads/Thread.asm
Executable file
@ -0,0 +1,113 @@
|
||||
;*****************************************************************************
|
||||
;* *
|
||||
;* C O R O U T I N E *
|
||||
;* *
|
||||
;*---------------------------------------------------------------------------*
|
||||
;* Beschreibung: Assemblerdarstellung der 'struct CoroutineState' aus *
|
||||
;* CoroutineState.h *
|
||||
;* *
|
||||
;* Die Reihenfolge der Registerbezeichnungen muss unbedingt *
|
||||
;* mit der von 'struct CoroutineState' uebereinstimmen. *
|
||||
;* *
|
||||
;* Autor: Olaf Spinczyk, TU Dortmund *
|
||||
;*****************************************************************************
|
||||
|
||||
%include "kernel/threads/Thread.inc"
|
||||
|
||||
; EXPORTIERTE FUNKTIONEN
|
||||
|
||||
[GLOBAL Thread_switch]
|
||||
[GLOBAL Thread_start]
|
||||
|
||||
; IMPLEMENTIERUNG DER FUNKTIONEN
|
||||
|
||||
[SECTION .text]
|
||||
|
||||
; COROUTINE_START : Startet die erste Coroutine ueberhaupt.
|
||||
;
|
||||
; C Prototyp: void Coroutine_start (struct CoroutineState* regs);
|
||||
|
||||
;; Coroutine_start is called with one arg: CoroutineState* regs, so the main stack looks like this:
|
||||
;; NOTE: Since this assembly is not generated by the compiler, there is no ebp prelude.
|
||||
;; To address parameters we use esp.
|
||||
;; == High address ==
|
||||
;; *REGS
|
||||
;; ESP: RET ADDR
|
||||
;; == Low address ==
|
||||
Thread_start:
|
||||
; *
|
||||
; * Hier muss Code eingefuegt werden
|
||||
; *
|
||||
|
||||
;; Set eax to the address where the CoroutineState struct is in memory:
|
||||
mov eax, [esp + 0x4]
|
||||
|
||||
;; Now eax points to the beginning of CoroutineState:
|
||||
;; struct CoroutineState {
|
||||
;; == Low address ==
|
||||
;; EAX: void *ebx;
|
||||
;; void *esi;
|
||||
;; void *edi;
|
||||
;; void *ebp;
|
||||
;; void *esp;
|
||||
;; == High address ==
|
||||
;; };
|
||||
|
||||
;; Load the contents of CoroutineState to the registers
|
||||
mov esp, [eax + esp_offset]
|
||||
|
||||
;; The stackpointer now points to the coroutine stack
|
||||
;; struct CoroutineState {
|
||||
;; == High address ==
|
||||
;; *OBJECT
|
||||
;; 0x13115
|
||||
;; SP: *KICKOFF
|
||||
;; == Low address ==
|
||||
;; };
|
||||
|
||||
;; Return to kickoff
|
||||
ret
|
||||
|
||||
|
||||
; COROUTINE_SWITCH : Coroutinenumschaltung. Der aktuelle Registersatz wird
|
||||
; gesichert und der Registersatz der neuen Coroutine
|
||||
; wird in den Prozessor eingelesen.
|
||||
;
|
||||
; C Prototyp: void Coroutine_switch (struct CoroutineState* regs_now, struct CoroutineState* reg_then);
|
||||
;
|
||||
; Achtung: Die Parameter werden von rechts nach links uebergeben.
|
||||
;
|
||||
;; == High address ==
|
||||
;; *REGS_THEN
|
||||
;; *REGS_NOW
|
||||
;; SP --> RET ADDR
|
||||
;; == Low address ==
|
||||
Thread_switch:
|
||||
; *
|
||||
; * Hier muss Code eingefuegt werden
|
||||
; *
|
||||
|
||||
;; Make eax point to beginning of regs_now in memory
|
||||
mov eax, [esp + 0x4]
|
||||
|
||||
;; Save current coroutine registers
|
||||
mov [eax + ebx_offset], ebx
|
||||
mov [eax + esi_offset], esi
|
||||
mov [eax + edi_offset], edi
|
||||
mov [eax + ebp_offset], ebp
|
||||
mov [eax + esp_offset], esp
|
||||
|
||||
;; Make eax point to beginning of regs_then in memory
|
||||
mov eax, [esp + 0x8]
|
||||
|
||||
;; Load next coroutine registers
|
||||
mov ebx, [eax + ebx_offset]
|
||||
mov esi, [eax + esi_offset]
|
||||
mov edi, [eax + edi_offset]
|
||||
mov ebp, [eax + ebp_offset]
|
||||
mov esp, [eax + esp_offset]
|
||||
|
||||
;; Stackpointer now points to kickoff address, ret jumps there
|
||||
;; NOTE: The stackpointer only points to kickoff if the next coroutine was just initialized.
|
||||
;; Otherwise it just points somewhere in the next coroutines stack
|
||||
ret
|
||||
126
c_os/kernel/threads/Thread.cc
Executable file
126
c_os/kernel/threads/Thread.cc
Executable file
@ -0,0 +1,126 @@
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* C O R O U T I N E *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Implementierung eines Koroutinen-Konzepts. *
|
||||
* Die Koroutinen sind miteinander verkettet, weswegen die *
|
||||
* Klasse Coroutine ein Subtyp von 'Chain' ist. *
|
||||
* *
|
||||
* Im Konstruktor wird der initialie Kontext der Koroutine *
|
||||
* eingerichtet. Mit 'start' wird ein Koroutine aktiviert. *
|
||||
* Das Umschalten auf die naechste Koroutine erfolgt durch *
|
||||
* Aufruf von 'switchToNext'. *
|
||||
* *
|
||||
* Um bei einem Koroutinenwechsel den Kontext sichern zu *
|
||||
* koennen, enthaelt jedes Koroutinenobjekt eine Struktur *
|
||||
* CoroutineState, in dem die Werte der nicht-fluechtigen *
|
||||
* Register gesichert werden koennen. *
|
||||
* *
|
||||
* Autor: Michael, Schoettner, HHU, 13.08.2020 *
|
||||
*****************************************************************************/
|
||||
|
||||
#include "kernel/threads/Thread.h"
|
||||
|
||||
// Funktionen, die auf der Assembler-Ebene implementiert werden, muessen als
|
||||
// extern "C" deklariert werden, da sie nicht dem Name-Mangeling von C++
|
||||
// entsprechen.
|
||||
extern "C" {
|
||||
void Thread_start(struct ThreadState* regs);
|
||||
void Thread_switch(struct ThreadState* regs_now, struct ThreadState* reg_then);
|
||||
}
|
||||
|
||||
unsigned int ThreadCnt = 0;
|
||||
|
||||
/*****************************************************************************
|
||||
* Prozedur: Coroutine_init *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Bereitet den Kontext der Koroutine fuer den ersten *
|
||||
* Aufruf vor. *
|
||||
*****************************************************************************/
|
||||
void Thread_init(struct ThreadState* regs, unsigned int* stack, void (*kickoff)(Thread*), void* object) {
|
||||
|
||||
register unsigned int** sp = (unsigned int**)stack;
|
||||
|
||||
// Stack initialisieren. Es soll so aussehen, als waere soeben die
|
||||
// eine Funktion aufgerufen worden, die als Parameter den Zeiger
|
||||
// "object" erhalten hat.
|
||||
// Da der Funktionsaufruf simuliert wird, kann fuer die Ruecksprung-
|
||||
// adresse nur ein unsinniger Wert eingetragen werden. Die aufgerufene
|
||||
// Funktion muss daher dafuer sorgen, dass diese Adresse nie benoetigt
|
||||
// wird, sie darf also nicht terminieren, sonst kracht's.
|
||||
|
||||
*(--sp) = (unsigned int*)object; // Parameter
|
||||
*(--sp) = (unsigned int*)0x131155; // Ruecksprungadresse (Dummy)
|
||||
|
||||
// Nun legen wir noch die Adresse der Funktion "kickoff" ganz oben auf
|
||||
// den Stack. Wenn dann bei der ersten Aktivierung dieser Koroutine der
|
||||
// Stackpointer so initialisiert wird, dass er auf diesen Eintrag
|
||||
// verweist, genuegt ein ret, um die Funktion kickoff zu starten.
|
||||
// Genauso sollen auch alle spaeteren Threadwechsel ablaufen.
|
||||
|
||||
*(--sp) = (unsigned int*)kickoff; // Adresse
|
||||
|
||||
// Initialisierung der Struktur ThreadState mit den Werten, die die
|
||||
// nicht-fluechtigen Register beim ersten Starten haben sollen.
|
||||
// Wichtig ist dabei nur der Stackpointer.
|
||||
|
||||
regs->ebx = 0;
|
||||
regs->esi = 0;
|
||||
regs->edi = 0;
|
||||
regs->ebp = 0;
|
||||
regs->esp = sp; // esp now points to the location of the address of kickoff
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Funktion: kickoff *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Funktion zum Starten einer Korutine. Da diese Funktion *
|
||||
* nicht wirklich aufgerufen, sondern nur durch eine *
|
||||
* geschickte Initialisierung des Stacks der Koroutine *
|
||||
* angesprungen wird, darf er nie terminieren. Anderenfalls *
|
||||
* wuerde ein sinnloser Wert als Ruecksprungadresse *
|
||||
* interpretiert werden und der Rechner abstuerzen. *
|
||||
*****************************************************************************/
|
||||
void kickoff(Thread* object) {
|
||||
object->run();
|
||||
|
||||
// object->run() kehrt hoffentlich nie hierher zurueck
|
||||
for (;;) {}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Methode: Coroutine::Coroutine *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Initialer Kontext einer Koroutine einrichten. *
|
||||
* *
|
||||
* Parameter: *
|
||||
* stack Stack für die neue Koroutine *
|
||||
*****************************************************************************/
|
||||
Thread::Thread() : stack(new unsigned int[1024]), tid(ThreadCnt++) {
|
||||
Thread_init(®s, stack + 1024, kickoff, this); // Stack grows from top to bottom
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Methode: Coroutine::switchToNext *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Auf die nächste Koroutine umschalten. *
|
||||
*****************************************************************************/
|
||||
void Thread::switchTo(Thread& next) {
|
||||
|
||||
/* hier muss Code eingefügt werden */
|
||||
|
||||
Thread_switch(&this->regs, &next.regs);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Methode: Coroutine::start *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Aktivierung der Koroutine. *
|
||||
*****************************************************************************/
|
||||
void Thread::start() {
|
||||
|
||||
/* hier muss Code eingefügt werden */
|
||||
|
||||
Thread_start(&this->regs);
|
||||
}
|
||||
@ -25,30 +25,27 @@
|
||||
#include "lib/Chain.h"
|
||||
|
||||
class Thread : public Chain {
|
||||
|
||||
private:
|
||||
Thread(const Thread ©); // Verhindere Kopieren
|
||||
Thread(const Thread& copy) = delete; // Verhindere Kopieren
|
||||
|
||||
private:
|
||||
struct ThreadState regs;
|
||||
unsigned int *stack;
|
||||
|
||||
public:
|
||||
unsigned int tid; // Thread-ID (wird im Konstruktor vergeben)
|
||||
unsigned int* stack;
|
||||
|
||||
public:
|
||||
unsigned int tid; // Thread-ID (wird im Konstruktor vergeben)
|
||||
|
||||
Thread();
|
||||
|
||||
Thread () { }
|
||||
|
||||
// Thread aktivieren
|
||||
void start ();
|
||||
void start();
|
||||
|
||||
// Umschalten auf Thread 'next'
|
||||
void switchTo (Thread& next);
|
||||
void switchTo(Thread& next);
|
||||
|
||||
// Methode des Threads, muss in Sub-Klasse implementiert werden
|
||||
virtual void run () = 0;
|
||||
virtual void run() = 0;
|
||||
|
||||
~Thread ();
|
||||
|
||||
};
|
||||
~Thread();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
26
c_os/kernel/threads/Thread.inc
Executable file
26
c_os/kernel/threads/Thread.inc
Executable file
@ -0,0 +1,26 @@
|
||||
;*****************************************************************************
|
||||
;* *
|
||||
;* C O R O U T I N E *
|
||||
;* *
|
||||
;*---------------------------------------------------------------------------*
|
||||
;* Beschreibung: Assemblerdarstellung der 'struct CoroutineState' aus *
|
||||
;* CoroutineState.h *
|
||||
;* *
|
||||
;* Die Reihenfolge der Registerbezeichnungen muss unbedingt *
|
||||
;* mit der von 'struct CoroutineState' uebereinstimmen. *
|
||||
;* *
|
||||
;* Autor: Olaf Spinczyk, TU Dortmund *
|
||||
;*****************************************************************************
|
||||
|
||||
; Nicht-fluechtige Register des Intel 80386 Prozessors
|
||||
|
||||
; Die folgenden Angaben belegen ebx_offset mit dem Wert 0, esi_offset mit 4
|
||||
; edi_offset mit 8 usw. Dies entspricht den Abstaenden der Elemente ebx, esi,
|
||||
; edi usw. der Struktur CoroutineState zum Anfang der Struktur (in Bytes).
|
||||
|
||||
[ABSOLUTE 0]
|
||||
ebx_offset: resd 1
|
||||
esi_offset: resd 1
|
||||
edi_offset: resd 1
|
||||
ebp_offset: resd 1
|
||||
esp_offset: resd 1
|
||||
39
c_os/kernel/threads/ThreadState.h
Executable file
39
c_os/kernel/threads/ThreadState.h
Executable file
@ -0,0 +1,39 @@
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* C O R O U T I N E S T A T E *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* Beschreibung: Die Struktur CoroutineState dient dazu, bei einem *
|
||||
* Koroutinenwechsel die Werte der nicht-fluechtigen *
|
||||
* Register zu sichern. Beim GNU C Compiler sind eax, ecx *
|
||||
* und edx fluechtige Register, die bei Funktionsaufrufen *
|
||||
* und somit auch bei einem Koroutinenwechsel keine spaeter *
|
||||
* noch benoetigten Werte haben duerfen. Daher muss in der *
|
||||
* Struktur CoroutineState auch kein Platz fuer sie bereit- *
|
||||
* gestellt werden. *
|
||||
* *
|
||||
* Achtung: Fuer den Zugriff auf die Elemente von *
|
||||
* struct CoroutineState aus einer Assemblerfunktion *
|
||||
* heraus werden in der Datei Coroutine.inc Namen fuer die *
|
||||
* benoetigten Abstaende der einzelnen Elemente zum Anfang *
|
||||
* der Struktur definiert. Damit dann auch auf die richtigen*
|
||||
* Elemente zugegriffen wird, sollten sich die Angaben von *
|
||||
* CoroutineState.h und Coroutine.inc exakt entsprechen. *
|
||||
* Wer also CoroutineState.h aendert, muss auch *
|
||||
* Coroutine.inc anpassen (und umgekehrt.) *
|
||||
* *
|
||||
* Autor: Olaf Spinczyk, TU Dortmund *
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __CoroutineState_include__
|
||||
#define __CoroutineState_include__
|
||||
|
||||
struct ThreadState {
|
||||
void* ebx;
|
||||
void* esi;
|
||||
void* edi;
|
||||
void* ebp;
|
||||
void* esp;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user