Autor Thema: FreeBasic: Software Multitasking  (Gelesen 10322 mal)

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« am: 14. May 2009, 18:27 »
@ehenkes: Hab deine Tuts gelesen. Baust du das mit dem Multitasking auch ein?

Weil, naja, also irgendwie verstehe ich das alles noch nicht.
Ich habe auch das Tutorial von James Molloy gelesen, aber ich habe da noch ein paar Fragen:
Wie funktioniert das genau, das er den richtigen Wert in eip hat?
Warum verlässt er die Routine mit "jmp *%%ecx" ? Müsste das nicht den Stack zerstören? Warum springt er nicht mir IRET zurück?
Wo sichert er die ganzen Register?
Warum überprüft er, ob in eip 0x12345 steht?

Bei meinem Kernel gibt es da auch noch ein Problem. Der Kernel läuft nach dem laden der Register des zweiten Tasks nicht mehr weiter. Falls jemand sich das mal anschauen will: http://rapidshare.com/files/232940400/FROST_V0.2.0a_unstable.zip
Sorry wenn ich dämliche Fehler gemacht habe, das ist mein erster Versuch. Ich hab noch kein TSS drin, da ich das ganze momentan sowieso nur innerhalb des Kernels teste.

Ehrlich gesagt hab ich so meine Probleme damit, fertigen Code zu verstehen. Und, naja, die OS-Projekte die ich mir bisher angesehen habe, sind nicht gerade ausführlich kommentiert. Kurz: Ich verstehe bei pedigree & co überhaupt nichts. Und das ganze ASM & C gemischte bringt mich dann komplett durcheinander.

Wenn Software-Multitasking bei meinem Kernel läuft, schreib ich ein Tutorial, mit dem jeder das zum laufen kriegt ;)
« Letzte Änderung: 14. May 2009, 22:55 von PorkChicken »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 14. May 2009, 22:46 »
Hm, naja das mit dem Thema Trennen hat dann doch nicht so geklappt, wie ich wollte.

Also FreeBasic Code kann ich nur bedingt interpretieren. Was mir auffällt, ist dass du sehr viele "naked" Funktionen hast, die du als Interrupt Handler oder für andere spezifische Assembler Sachen nimmst. Ich würd mich nicht darauf verlassen, dass bloß weil du "naked" ranschreibst, auch der FreeBasic Code ohne Seiteneffekte ist.

Z.B. in Multitasking_switch sieht das mit der lokalen Variable auch sehr verdächtig nach Problemen aus. Wenn dir da der Compiler zum falschen Zeitpunkt eine lokale Variable auf dem Stack anlegt, dann geht was kaputt. Da solltest du dir mal den disassemblieren Code ansehen.

Das Erstellen eines Stacks in Multitasking_Init musst du für jeden einzelnen Task machen, weil natürlich jeder Task einen Stack braucht. In Task_Create gehört das vermutlich hin. Außerdem musst du natürlich auch bevor du [oldesp] lädst, erstmal das aktuelle esp in Tasks(i).esp speichern.
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #2 am: 14. May 2009, 22:58 »
Zitat
Weil, naja, also irgendwie verstehe ich das alles noch nicht.
Was genau verstehst Du nicht?  :?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 15. May 2009, 00:33 »
Hm, jetzt wo ich mir das ursprüngliche Thema so anschaue, denke ich, dass ich eigentlich wohl ehenkes Fragen hätte abspalten sollen. Bin wohl etwas verplant gewesen 8-)

Also mal um deine ursprünglichen Fragen etwas zu beantworten:

Ich würde die Erstellung von Tasks so betrachten, dass man versucht das nachzubauen, als wäre der Task an seinem Anfang durch einen Interrupt unterbrochen worden.

Also was dein Code tut (oder zumindest vielleicht tut, wenn FreeBasic mitspielt), ist ja schon mal nicht ganz verkehrt:
Dein Interrupt Handler sichert alle Register, scheduled (mittels Round Robin) und lädt alle Register vom Stack.

Das Erstellen des Tasks sollte praktisch so ablaufen, dass du nach dem scheduling mit deinem neuerstellten Task fortfahren kannst. Das ist übrigens etwas, dass du in einem Debugger wie Bochs überprüfen kannst, indem du auf den Stack schaust (Befehl war print-stack glaub ich). Wenn du das mit dem vergleichst, was rauskommt, wenn dein OS statt Scheduling einfach gar nichts macht (oder nur einen Task hat), dann solltest du Hinweise darauf bekommen, was an deinem Stack falsch ist.
Dieser Text wird unter jedem Beitrag angezeigt.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 15. May 2009, 07:44 »
Hm, ich hab das Multitasking-Zeug mal neu geschrieben, nach einem Tut das ich auf google code gefunden habe. Der Kernel sorgt dafür, das zwei Tasks vorbereitet werden, erlaubt IRQs und geht in eine Endlosschleife.

Das Problem ist: Sobald der erste Task aktiviert wird, kriege ich einen GPF.

Screenshot: http://darkinsanity.freehoster.ch/FROST_V0.2.0a.PNG
Den Code dazu gibts hier: http://darkinsanity.freehoster.ch/FROST_V0.2.0a_unstable.zip

Ein paar Auszüge:

type TaskType
    ID as USHORT                                 '// the ID of the task
    stack as UINTEGER                            '// the stack of the task
    eip as UINTEGER
    status as BYTE                               '// the status of the task (blocked, ready, active...)
end type

dim shared Tasks(100) as TaskType

function Multitasking_scheduler (stack as UINTEGER) as UINTEGER
    Tasks(currentPID).stack = stack

    currentPID += 1
    if currentPID > AllTasks then currentPID = 1

    return Tasks(currentPID).stack
end function



sub Multitasking_Switch naked
    dim tempstack as UINTEGER
    asm
        pusha
        push ds
        push es
        push fs
        push gs
       
        mov eax, &h10
        mov ds, eax
        mov es, eax
        mov fs, eax
        mov gs, eax
       
        mov eax, esp
        mov [tempstack], eax
    end asm
   
    tempstack = Multitasking_scheduler(tempstack)
   
    asm
        mov eax, [tempstack]
        mov esp, eax
        mov al, &h20
        out &h20, al
       
        pop gs
        pop fs
        pop es
        pop ds
        popa
       
        iret
    end asm
end sub

sub Task_Create (ID as USHORT, address as any ptr)
    if Tasks(ID).status <> TASK_EMPTY then return  '// place not free
   
    dim stack as UINTEGER
    dim kernelstack as UINTEGER
   
    asm cli                                        '// we donŽt want to be interrupted
   
    stack = cast(UINTEGER, malloc(4096) + 4096)    '// allocate space for 4kb stack
    print "Allocated Stack: ";
    print stack
   
    asm
        mov eax, esp                               '// get the stackpointer in eax
        mov [kernelstack], eax                     '// save the kernelstack-pointer
       
        mov eax, [stack]                           '// load the new stack into eax
        mov esp, eax                               '// get the new stack to esp
       
        mov eax, &h202
        push eax
        mov eax, &h08
        push eax
        push [address]
       
        mov eax, &h0
        push eax
        push eax
        push eax
        push eax
        push eax
        push eax
        push eax
        push eax
       
        mov eax, &h10
        push eax
        push eax
        push eax
        push eax
    end asm
   
    Tasks(ID).status = TASK_READY
    Tasks(ID).stack = stack
    Tasks(ID).eip = cast(UINTEGER, address)
   
    AllTasks += 1
   
    asm sti
end sub
« Letzte Änderung: 15. May 2009, 07:47 von TheThing »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 15. May 2009, 11:43 »
Ich hab mir mal FreeBasic geholt (hätte mir mal wer gesagt, dass erst die SVN Version naked kann -.-), und rausgekommen ist folgendes:

.globl _MULTITASKING_SWITCH@0
_MULTITASKING_SWITCH@0:
.Lt_000A:
mov dword ptr [ebp-4], 0 ; <--- das ist die lokale variable. das darf natürlich nicht passieren
pusha
push ds
...

Also musst du auf die lokale Variable verzichten. Zum Beispiel indem du die Funktion Multitasking_scheduler aus dem Assembler-Code heraus aufrufst:

...
mov fs, eax
mov gs, eax

push esp
call _MULTITASKING_SCHEDULER@4
mov esp, eax

mov al, 32
out 32, al

pop gs
pop fs
...

Testen kann ich es leider nicht, weil dein Download nicht geht.

Task_Create ist auch noch nicht korrekt. Du setzt Tasks(ID).stack auf den Anfang des Stacks, nicht auf die Stelle, wo du kurz vorher noch Daten hingetan hast. Außerdem musst du den Kernelstack wiederherstellen:

     ...
        mov eax, &h10
        push eax
        push eax
        push eax
        push eax

        mov eax, esp     ' esp sichern
        mov [stack], eax

        mov eax, [kernelstack]               ' kernelstack wiederherstellen
        mov esp, eax

    end asm

Du solltest dir auch mal überlegen, ob du das vielleicht ohne Stackwechsel machen willst. Langfristig macht dieses Gefummel nur Probleme.
   
Dieser Text wird unter jedem Beitrag angezeigt.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 17. May 2009, 22:39 »
vielen Dank, jetzt funktioniert alles!  :-) :-)
/edit: versprochenes Tutorial ist in Arbeit

ehenkes

  • Gast
Gespeichert
« Antwort #7 am: 17. May 2009, 22:53 »
Zitat
@ehenkes: Hab deine Tuts gelesen. Baust du das mit dem Multitasking auch ein?
Hier findest Du das Thema Multitasking:
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId114565

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 19. May 2009, 12:49 »
danke :)

Ich habe noch ein paar Fragen, weil ich ELF-Dateien nutzen möchte:
Brauch ich Paging, um ELF-Dateien zu nutzen?
Auf was muss ich beim Multitasking achten, wenn ich Paging benutze?

Bisher funktioniert das nämlich nicht :)
Ist mein ELF-header denn richtig?

Der header liegt hier: http://rapidshare.com/files/234717136/ELF.bi.html

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 25. May 2009, 12:34 »
ok, ähm, kann mir jemand erklären, welche Schritte ich tun muss damit ich eine ELF-Datei ausführen kann? Ich meine ich hätte sowas hier im Forum schon mal gesehen, aber ich finds nicht mehr...

Und wie oft pro Sekunde sollte ich denn den Task wechseln?

/edit: Hab meine Tutorials mal ins Wiki eingetragen:
http://lowlevel.brainsware.org/wiki/index.php/Software_Multitasking_Tutorial
http://lowlevel.brainsware.org/wiki/index.php/Paging_Tutorial

Bitte testlesen :)
« Letzte Änderung: 25. May 2009, 12:37 von TheThing »

FreakyPenguin

  • Administrator
  • Beiträge: 301
    • Profil anzeigen
    • toni.famkaufmann.info
Gespeichert
« Antwort #10 am: 25. May 2009, 12:41 »
Eigentlich musst du nur alle Program Headers (mit Typ LOAD) der ELF-Datei durchgehen und den Inhalt der Datei an die angegebene Stelle im Adressraum des Prozesses legen, und noch mit Nullen auffüllen wo mehr in den Adressraum soll als in der Datei ist.

In Code ausgedrückt (zwei unabhägnige Versionen):

Und zur Frequenz: Es kommt darauf an. ;-) Da probierst du am besten selbst aus, was für dich am besten tut.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 25. May 2009, 13:23 »
Bitte testlesen :)
Also an dem Software Multitasking Tutorial stört mich ein wenig der Aufbau des Stacks mittels Inline Assembler und esp-Gebastel. Gibts in FreeBasic keine Möglichkeit, das mit Zeigern, Structs oder Arrays zu lösen? Du hattest selbst ja auch mit dieser uneleganten Lösung Probleme, und für ein Tutorial ist es sehr ungünstig, wenn so ein Stolperstein mehr oder weniger als Black Box präsentiert wird.

Wenn du das zum Beispiel mit einem Struct (wie auch immer diese type xxx/end type-Geschichte heißt) machst, dann sparst du dir eventuell sogar einiges an umständlichen Erklärungen mit stack-60 und so ;)
Dieser Text wird unter jedem Beitrag angezeigt.

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #12 am: 25. May 2009, 13:33 »
in Text ausgedrückt:
ELF Dateien ausführen
Im lowlevel-wikiartikel zu ELF ist ein Link zu wikipedia. Dort sind wiederum alle ELF-Spezifikationen gelinkt, in denen man dann die Details nachschlagen kann.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 25. May 2009, 21:38 »
danke für die Links :)

in dem Tutorial, das ich mir über Multitasking durchgelesen habe, ist das irgendwie mit "*--stack = 0;" gemacht worden. Unter FreeBASIC geht das leider (so weit ich weiß) noch nicht. Ich könnte die Werte in den Speicher schreiben und dann mit "stackpointer -= 1" den stackpointer niedriger machen, aber das wäre sehr lästig :)

Aber ich glaub ich setz mal einen Feature-Request dafür ab, wenns das noch nicht gibt.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 25. May 2009, 22:24 »
Also ich dachte an sowas (mit Hilfe von Google zusammengeschnipselt):

Type StackFrame
     GS As Integer
     FS As Integer
     ES As Integer
     ...
     Eax As Integer
     ...
     Eip As Integer
     Cs As Integer
     ...
End Type

sub Task_Create (ID as USHORT, address as any ptr)
...
   Dim StackFrame As StackFrame Ptr

    ' den Stack Frame an den Anfang des Stacks (Ende der Seite) legen
   stack = cast(UINTEGER, malloc(4096))+4096 - sizeof(StackFrame)

    StackFrame = stack ' StackFrame soll auch auf das Ende des Stacks zeigen
    StackFrame.Gs = &H10
    StackFrame.Fs = &H10
    StackFrame.Es = &H10
    StackFrame.Ds = &H10
    ...
    StackFrame.Cs = &H08
    StackFrame.Eip = address

       Tasks(ID).stack = stack ' Den Wert haben wir oben ja schon berechnet


So klappt das vermutlich noch nicht, aber ich hoffe meine Idee wird deutlich. Ich glaube dass FreeBasis alle Sprachmittel (UDT, Zeiger dafür hat.
Dieser Text wird unter jedem Beitrag angezeigt.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 27. May 2009, 12:11 »
hm, ja könnte man machen :)

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 01. June 2009, 13:02 »
So, ich hab mal meinen Kernel und das Tutorial überarbeitet. Jetzt brauch ich keinen Stack-Wechsel mehr :)

btw könnte mal jemand über meinen floppy-treiber-source drüberlesen und mir sagen ob da alles so in Ordnung ist? Hier der Link zum source: http://darkinsanity.netne.net/nerdpole/source/floppy.bas
« Letzte Änderung: 01. June 2009, 13:05 von TheThing »

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 02. June 2009, 15:23 »
So, ich hätte da noch ein paar Fragen zu ELF-Dateien.
Ich hab ein kleines testprogramm in ASM geschrieben (ruft in einer Endlosschleife int 0x30 auf), assembliert und gelinkt. Rausgekommen ist eine ELF-Datei, die ich nun mit meinem Kernel ausführen möchte.
Daher unternimmt mein Kernel jetzt folgende Schritte:
ELF-Header prüfen
Program-Header-Einträge durchlaufen und alle PT_LOAD-Segmente in den virtuellen Speicher kopieren (der Bereich von p_offset bis p_offset+p_filesz wird kopiert)
Task erstellen, der e_entry als eip hat

Das ganze funktioniert aber noch nicht (eine ganze Weile tut sich einfach nix, irgendwann fliegt dann ein Page-Fault). Also hab ich ein bisschen herumprobiert und herausgefunden, das mein Programm gar keine PT_Load-Segmente hat. Alle anderen Segmente, die in der ELF-Datei existieren, haben als virtuelle Zieladresse 0. Wie soll jetzt aber der ausführbare Code im Speicher landen, wenn gar keine Segmente zum kopieren da sind?

Und für was ist eigentlich der Sektionsheader gut? Brauch ich den?

Btw Ich glaub ich schreib am besten gleich ein ganzes Buch über OS-dev. Ich werd nämlich auch zum Thema ELF-Dateien ein Tutorial schreiben, und wenn ich über jedes Thema ein Tutorial schreib, werden das ganz schön viele. Aber vorerst sind es nur ein paar, und da benutze ich erstmal das Lowlevel-Wiki um meine Tutorials anderen zugänglich zu machen.

An dieser Stelle muss ich jetzt einfach mal ein großes Lob an alle Leute von Lowlevel aussprechen. Ich bin erst seit August 2008 am OS-coden, und ohne eure Hilfe hätte ich schon längst aufgegeben. :) Weiter so.

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 02. June 2009, 16:12 »
Zitat
Also hab ich ein bisschen herumprobiert und herausgefunden, das mein Programm gar keine PT_Load-Segmente hat. Alle anderen Segmente, die in der ELF-Datei existieren, haben als virtuelle Zieladresse 0. Wie soll jetzt aber der ausführbare Code im Speicher landen, wenn gar keine Segmente zum kopieren da sind?

Dann ist da wohl irgend etwas beim linken schiefgegangen.

Was sagt den "objdum -x <foo>" und wie rufst du den linker auf bzw. wie sieht dein link-script aus?
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #19 am: 02. June 2009, 18:30 »
Und für was ist eigentlich der Sektionsheader gut? Brauch ich den?
Die Sectionheaders sind für die Objektdateien (also *.o) und die Segmentheader für die ausführbaren Dateien. Man hat in den ELF-Specs einen Standard für beide Arten von Dateien gemacht, weil viele Sachen eben relativ gleich sind.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

 

Einloggen