Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: rizor am 18. March 2009, 15:42
-
Hallo,
ich bin gerade dabei mein System multitasking-fähig zu kriegen.
Als erstes muss ich ja eine TSS einbauen.
Muss ich die schon mit werten befüllen, wenn ja mit welchen?
Dann habe ich mir das mit dem User- und dem Kernel-Stack angeschaut.
Mir ist klar, dass der einen eigenen Kernelstack benötigt.
Den kann ich ja einfach nach ganz unten setzen und dann wars das, oder?
Aber wieso habt ihr den Kernelstack noch einmal new_task->esp gelegt?
Muss der Prozess nicht einen eigenen Stack bekommen?
[EDIT]
Wenn ich nun ein Modul in den Kernelspace lade, wie muss ich denn da dann die Stack-Pointer setzen?
-
Muss ich die schon mit werten befüllen, wenn ja mit welchen?
Kommt drauf an ob du Software- oder Hardwaremultitasking machen möchtest. Normalerweise macht man Softwaremultitasking und da reicht im Prinzip SS0:ESP0. Alle anderen Werte speichert man normalerweise auf einem Stack oder sonst irgendwo anders.
Den kann ich ja einfach nach ganz unten setzen und dann wars das, oder?
zB, du solltest halt trotzdem nie zwei Codeabschnitte gleichzeitig den gleichen Stack nutzen lassen, aber das kriegst du mit ein bisschen Gehirnschmalz sicherlich hin (oder alternativ kackt dein Kernel unvorhergesehen ab und du darfst Debuggen).
Aber wieso habt ihr den Kernelstack noch einmal new_task->esp gelegt?
Die Frage verstehe ich leider nicht, da ich meine Grammatik-Kristallkugel leider verlegt habe :wink:
Muss der Prozess nicht einen eigenen Stack bekommen?
Nicht nur jeder Prozess sondern sogar jeder Thread.
Wenn ich nun ein Modul in den Kernelspace lade, wie muss ich denn da dann die Stack-Pointer setzen?
Wenn deine Module keine eigenständigen Tasks sind, brauchst du logischerweise keinen eigenen Stack. Erst wenn du darauf eigens schedulbare EInheiten (aka Tasks) machst brauchst du einen Stack.
-
Ok. Danke.
Kommt drauf an ob du Software- oder Hardwaremultitasking machen möchtest. Normalerweise macht man Softwaremultitasking und da reicht im Prinzip SS0:ESP0. Alle anderen Werte speichert man normalerweise auf einem Stack oder sonst irgendwo anders.
Das mit SS0:ESP0 verstehe ich nicht.
Was meinst du damit?
zB, du solltest halt trotzdem nie zwei Codeabschnitte gleichzeitig den gleichen Stack nutzen lassen, aber das kriegst du mit ein bisschen Gehirnschmalz sicherlich hin (oder alternativ kackt dein Kernel unvorhergesehen ab und du darfst Debuggen).
Das ist klar.
Die Frage verstehe ich leider nicht, da ich meine Grammatik-Kristallkugel leider verlegt habe :wink:
ja, ja.
Deutsch Sprach schwer Sprach
Ich habe mich nur bei dem Lost-Code gewundert, warum es einmal heißt: new_task->kernel_stack = kernelstack und new_task->esp = kernelstack.
Das hat mich nur gewundert.
Dann habe ich aber gesehen, dass new_task->user_stack = userstack gibt.
Wieso wird das mit dem ganzen Stack-Geschiebe gemacht?
Wenn deine Module keine eigenständigen Tasks sind, brauchst du logischerweise keinen eigenen Stack. Erst wenn du darauf eigens schedulbare EInheiten (aka Tasks) machst brauchst du einen Stack.
Wie sieht dass denn dann mit dem Rücksprung aus dem Modul in den Kernel aus?
Geht das nur über Syscalls?
-
Das mit SS0:ESP0 verstehe ich nicht.
Was meinst du damit?
wiki: TSS (http://lowlevel.brainsware.org/wiki/index.php/TSS#Task_State_Segment_.28TSS.29): Das Feld SS0:ESP0 gibt an welcher Stack bei einem Interrupt benutzt wird (um den Interrupt Stackframe darauf zu pushen), d.h. es gibt den Kernelstack an.
Wieso wird das mit dem ganzen Stack-Geschiebe gemacht?
Ich nehme an, dass das eine angibt wo sich den der Anfang des Kernelstacks ist und der andere wo genau sich der Kernelstack momentan befindet. Ist aber wohl eher nicht von großer Bedeutung.
Wie sieht dass denn dann mit dem Rücksprung aus dem Modul in den Kernel aus?
Geht das nur über Syscalls?
Das kommt drauf an. Wenn du deine Module nicht im Kernelspace (also im CPL0) laufen lässt dann ja, ansonsten nein. Bei ersterem ist das Modul im Prinzip ein eigener Prozess (und du bist bei einem Microkernel), bei letzterem ist der Aufruf ein ganz normaler Funktionsaufruf (und du bist bei einem monolithischen Kernel mit ladbaren Modulen).
-
Danke.
Wie funktioniert das mit dem Kernelstack?
Bekommt jeder Task seinen eigenen Kernelstack oder teilen sich alle einen?
Wenn sich alle einen teilen, bekomme ich beim Mehr-CPU-Betrieb Konsistenzprobleme, oder?
Wie funktioniert das allgemein mit dem Aufrufen der Tasks?
Aus dem Elf-header bekomme ich alle benötigten Infos, oder?
Ich habe das leider nur noch nicht mit dem cmdline verstanden.
An sich wird ein Prozess doch mit der Main gestartet und wenn ein Prozess Startparameter braucht, liegen diese ja auf dem Stack. An sich müsste ich doch nur das Startregister setzen, damit er ordentlich laufen kann, oder?
Die Programme werden doch mit der Startadresse 0x0 versehen, oder?
Wie müsste ich das dann mit dem Offset machen, weil ich die Programme nicht auf 0x0 lege.
-
Wie funktioniert das mit dem Kernelstack?
Bekommt jeder Task seinen eigenen Kernelstack oder teilen sich alle einen?
Wenn sich alle einen teilen, bekomme ich beim Mehr-CPU-Betrieb Konsistenzprobleme, oder?
Du hast normalerweise einen pro CPU.
Wie funktioniert das allgemein mit dem Aufrufen der Tasks?
Register auf den Stack pushen, iret -> Taskswitch. Nächster Interrupt -> Wechsel zum Kernelstack, pushen der Register, etc. auf diesen -> Zurück im Kernel.
Aus dem Elf-header bekomme ich alle benötigten Infos, oder?
Das einzig wirklich interessante aus den ELF-Headern ist der Entry-Point des Programs (also der start EIP-Wert).
Ich habe das leider nur noch nicht mit dem cmdline verstanden.
Ich habe keine Ahnung welche cmdline du meinst und inwiefern die in den momentanen Kontext passt.
An sich wird ein Prozess doch mit der Main gestartet
Nein wird er nicht. Normalerweise läuft ein Codefitzelchen der libc davor um eben sowas wie die argc und argv Parameter für main zu holen, main aufrufen und eventuell vorher noch irgendwas falls notwendig in der libc selbst initialisieren.
An sich müsste ich doch nur das Startregister setzen, damit er ordentlich laufen kann, oder?
Was ist das "Startregister"? Ich denke du meinst EIP, siehe oben
Die Programme werden doch mit der Startadresse 0x0 versehen, oder?
Nein, normalerweise möchte man beim folgen eines NULL-Pointers einen Pagefault haben, deshalb legt man die Programme etwas höher, ich zB hab sie bei 4MB.
Wie müsste ich das dann mit dem Offset machen, weil ich die Programme nicht auf 0x0 lege.
Du kannst sie doch im Linkerskript dahin linken wo du sie eben brauchst.
-
Ok.
Wie bekomme ich das hin, dass für dem Programmcode noch die libc läuft?
-
Wie bekomme ich das hin, dass für dem Programmcode noch die libc läuft?
Linkerscript für Programme (https://lightos.bountysource.com/svn/!source/481/trunk/linkerscript/x86-c.ld) (Das wesentliche ist "ENTRY(startup)")
Assembler stub (https://lightos.bountysource.com/svn/!source/481/trunk/lib/libc/libOS/lightOS/crt0/x86-crt0.S) (Das wesentliche ist das Definieren des startup-Symbols)
_main() (https://lightos.bountysource.com/svn/!source/481/trunk/lib/libc/libOS/lightOS/_main.c) zum Initialisieren der und Aufrufen von main()
-
Ich habe mal ein paar Fragen zu deiner _main().
Wenn ich es richtig verstehe, dann zählt _cmdline die Anzahl an Argumenten, oder?
Woher holst du argv?
Das müsste an sich doch von der Konsole oder den Aufrufparametern kommen, oder?
Was genau ist denn dieses environ?
Das kommt von environment aber ich kann mir nicht vorstellen was das macht (den Code env.c habe ich gesehen).
Dann habe ich noch eine Frage wie ich dann am besten Kernel-Module linke.
Denen kann ich ja keine feste Startadresse zuteilen, bzw. das möchte ich nicht.
-
Wie du argv bekommst hängt davon ab, wie du das gestaltest. Bei meinOS werden beim Aufruf eines exec() Daten wie argv und environ in ein SharedMemory-Segment gelegt und die ID des Segmentes im Kernel gespeichert. Wenn der neue Prozess gestartet wird, lädt er die Daten aus dem SharedMemory-Segment.
environ ist ein Array von Umgebungsvariablen:
http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
Bei Kernelmodulen musst du selbst die Adressen relocaten und dann die Symbole für Hooks (z.B driver_init()) auflösen.
-
Wie kann ich die Module denn relocaten?
An sich müsste ich dem Code ja beibringen, dass es immer noch ein Offset gibt.
-
Dann habe ich noch eine Frage wie ich dann am besten Kernel-Module linke.
Du linkst sie am besten garnicht, sondern machst wohl nur Objektdateien, würde ich mal sagen.
-
Wie kann ich die dann laden?
Also über GRUB
-
menu.lst (https://lightos.bountysource.com/svn/!source/481/trunk/build/x86/boot/grub/menu.lst) (über "module")
-
Ja, das dachte ich mir.
Aber wie kann ich den Code dann ausführen?
So, als ob es ein normalen Prozess starten würde oder wie?
-
Jo natürlich. Grub gibt dir die Liste der geladenen Module mit Adressen der in den RAM geladenen ELF-Datei. Dann die ELF-Datei Parsen, die Segmente extrahieren und halt einen Task starten.
Ob irgendein Prozess einen anderen gestartet hat ist doch dafür komplett irrelevant.