Autor Thema: runtime loader - Programme laden/ausführen  (Gelesen 15648 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« am: 27. December 2010, 22:00 »
Ich wollte mich mal wieder mit meinem OS beschäftigen und das Programme laden/ausführen besser implementieren, da es mir so wie ich es im Moment noch habe nicht gefällt.

Im Moment muss ich dem Kernel nen Pointer auf nen Speicherbereich, mit zugehöriger Größe, übergeben in dem Sich die auszuführenden Datei befindet.

Dieser Speicher wird dann in einen neuen Prozess gemappt und erst dann wird der Speicher geparst und das Programm-Image erstellt. Erst jetzt würden auch (wenn ich es denn schon implementiert hätte) die erforderlichen Libraries geladen werden.

Eigentlich finde ich dieses Vorgehen ganz gut, aber eine Art Programm, den Runtime-Loader, zu haben hätte auch so seine Vorteile (ich kann leichter mehrere Dateiformate unterstützen und noch andere nette Dinge leichter machen).

Aber wie funktioniert so ein Runtime-Loader eigentlich und welche Variante ist eurere Meinung nach besser und warum?

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 28. December 2010, 02:53 »
Nabend,

das Unix-Konzept geht so: der Kernel hat eine Liste mit Identifizierungsmerkmalen für ausführbare Dateien (im Zweifelsfall ist das nur ein Eintrag für ELF, aber vgl. BINFMT) und dazu passend den jeweiligen Loader.

Unter Linux ist das die /lib/ld-linux.so für ELF-Dateien, /lib/ld.so für a.out.

Der Loader zerbröselt nun das Executable und tut dann mehrere Dinge:

Er schaut bei dynamisch gelinkten Programmen in der Executable nach, welche Bibliotheken in welcher Version benötigt werden und lädt diese in den Speicher. Dann merkt er sich die in den Bibliotheken verwendeten Symbole und stopft die in eine Symboltabelle.

Er legt für jeden Bestandteil des Executables (.code, .data) den Speicherbereich an und füllt diesen bei Bedarf, anschließend baut er die Symboltabellen aus dem ersten Schritt noch so ein, dass das Programm damit was anfangen kann (ich weiß aber nicht, wie genau).

Dann führt er das Programm aus.

Unter Linux wird die Variable LD_LIBRARY_PATH (nebst anderen) vom Loader benutzt, um die Bibliotheken zu finden. Das ist hässlich, bitte tu es nicht. Außerdem kann man über Variablen und Parameter noch Debugging einschalten und Einfluss auf den Loader nehmen, während er Dinge tut (vgl. ld-linux(8)).

Ob und was du genau implementieren musst, hängt auch noch von deinem gewählten Binärformat ab (z.B. musst du Scripts an den Interpreter übergeben, das macht den Loader einfacher). Eventuell lädt der Loader das Programm auch direkt vom Datenträger (der Kernel liest nur z.B. die ersten 8K, um den Loader zu ermitteln - das ermöglicht z.B. XIP oder transparente Code-Kompression ohne, dass der Kernel das können muss).

Gruß,
Svenska
« Letzte Änderung: 28. December 2010, 02:58 von Svenska »

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 28. December 2010, 10:32 »
Ich würde halt gerne nen Runtime-Loader nutzen, weil ich damit den ganzen Kram aus dem Kernel habe und weil ich dann einfacher auch andere Dateiformate unterstützen kann.

Ein Vorteil (je nachdem wie man es sieht) ist ja das der Runtime-Loader im UserSpace läuft. Ich kann mir halt nur nicht so richtig erklären wie man das dann alles so macht.

Ich habe mir das so vorgestellt das man als erstes mal nen fork() vom Runtime-Loader macht und dann die Datei in den Speicher laden lässt.
Problem ist jetzt halt, das der Runtime-Loader ja dem Executeable-Image nicht im Weg rumliegen darf, sprich das der Runtime-Loader nicht an einer Adresse sein darf, die eigentlich das Executeable-Image nutzen will. Das war auch ein Grund warum ich das alles in den Kernel gepackt habe.

Vorallem wenn ich das richtig verstanden habe, dann hat Linux eigentlich auch nen Runtime-Loader, sprich die ld-linux.so parst die Datei und erstellt das Image?

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 28. December 2010, 11:46 »
Ich habe mir das so vorgestellt das man als erstes mal nen fork() vom Runtime-Loader macht und dann die Datei in den Speicher laden lässt.
Richtig. Sinnvollerweise lädst du die Datei nicht am Stück, sondern parst den Header und lädst die Bestandteile an verschiedene Adressen.

Problem ist jetzt halt, das der Runtime-Loader ja dem Executeable-Image nicht im Weg rumliegen darf, sprich das der Runtime-Loader nicht an einer Adresse sein darf, die eigentlich das Executeable-Image nutzen will. Das war auch ein Grund warum ich das alles in den Kernel gepackt habe.
Das Executable darf keine fixen Adressen anfordern (du könntest natürlich Segmentierung nutzen, dann geht das), sondern der Loader verrät dem Executable, an welche Adresse es geladen wurde. Gleiches kann auch für die benötigten Libraries gelten.

Windows CE hat die DLLs an fixe Adressen gelinkt (verschieden für jede DLL), d.h. jede DLL kann nur einmal im System existieren. Normales Windows und Linux erlauben DLLs/Libs mehrfach an verschiedenen Adressen im Speicher (jede Anwendung kann ihre eigene SDL.DLL/libSDL haben).

Vorallem wenn ich das richtig verstanden habe, dann hat Linux eigentlich auch nen Runtime-Loader, sprich die ld-linux.so parst die Datei und erstellt das Image?
Ja. Linux hat nicht nur einen, sondern sogar linuxtypisch mehrere (ld.so für a.out, ld-linux.so für ELF plus das binfmt_misc-Framework und den Shebang).

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 28. December 2010, 11:57 »
Zitat von: svenska
Richtig. Sinnvollerweise lädst du die Datei nicht am Stück, sondern parst den Header und lädst die Bestandteile an verschiedene Adressen.
Da fällt mir auch wieder ein was noch ein Grund war, das alles im Kernel zu machen. Ich gehe halt davon aus, das die Datei schon komplett geladen ist und das die Segmente (wenn es eine ELF-Datei ist) schon in der Datei an 4KB ausgerichtet sind, so dass ich nichts kopieren muss, sondern einfach nur den Speicher an die richtige Stelle mappen muss.
Das sollte schneller gehen und selbst wenn ich das vom UserSpace aus machen könnte, dann hätte ich noch das Problem das ich zum ummappen jedes Mal in den Kernel müsste und damit wäre dann der Vorteil eh wieder weg.

Zumal ich es eh so sehe, dass das Laden der kompletten Datei schneller sein sollte, als wenn man (bei einem MikroKernel) sie in mehreren Stücken laden lässt.

Zitat von: svenska
Das Executable darf keine fixen Adressen anfordern (du könntest natürlich Segmentierung nutzen, dann geht das), sondern der Loader verrät dem Executable, an welche Adresse es geladen wurde. Gleiches kann auch für die benötigten Libraries gelten.
Das sehe ich anders (und der ELF-Standard auch ;) ). Eine ganz normale Executeable-ELF-Datei (kein shared-object) kann komplett fertig gelinkt sein, sprich man muss nur noch die Segmente an die entsprechenden Adressen packen und dann läuft das Programm (Libraries werden in dem Fall statisch gelinkt).

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 28. December 2010, 13:22 »
Hallo,

Da fällt mir auch wieder ein was noch ein Grund war, das alles im Kernel zu machen. Ich gehe halt davon aus, das die Datei schon komplett geladen ist und das die Segmente (wenn es eine ELF-Datei ist) schon in der Datei an 4KB ausgerichtet sind, so dass ich nichts kopieren muss, sondern einfach nur den Speicher an die richtige Stelle mappen muss.
Du kannst natürlich das Laden der Datei im Kernel machen, dann kriegt der Loader nur noch die Adresse des ELF-BLOBs, aus dem er Symboltabellen generieren muss. Damit kannst du natürlich nicht mehr .code und .data zufällig voneinander trennen (Adressraum-Randomisierung), weil sie im Block im Speicher liegen. Du kannst kein XIP (eXecute In Place) mehr machen, ohne die schon geladenen Daten wieder aus dem RAM zu entfernen und du brauchst einen vollwertigen ELF-Parser im Kernel, um Alignments erzwingen zu können.

Wenn der Loader sich um das ELF-Parsen kümmert, kannst du .code/.data dynamisch komprimieren/verschlüsseln/usw., die Datei braucht kein inhärentes Alignment haben und du kannst alle möglichen witzigen Dinge tun. Außerdem hast du dann die Freiheit, mehrere Binärformate durch ein gemeinsames Framework zu unterstützen. Die Linux-Emulation von FreeBSD funktioniert auf der Ebene, der Loader erkennt ein Linux-ELF und erstellt eine Umgebung, in der die Syscalls umnummeriert (auf die Linux-Nummern) werden. Oder du kannst eine PE-Datei durch Wine/DOSEMU schicken.

Henne-Ei-Problem: Der Kernel muss den Runtime-Loader starten können... dessen Format muss aber kein full-blown ELF sein.

Das sollte schneller gehen und selbst wenn ich das vom UserSpace aus machen könnte, dann hätte ich noch das Problem das ich zum ummappen jedes Mal in den Kernel müsste und damit wäre dann der Vorteil eh wieder weg.
Sehe ich keinen Nachteil, denn Programme laden und ausführen ist eine sehr seltene Sache (verglichen mit der Laufzeit der Anwendungen) und du musst ja nur ein Mapping pro Section machen, statt alle gleichzeitig. Die paar zusätzlichen Syscalls fallen nicht ins Gewicht, weil die Ladezeit vom Medium einen Großteil der Zeit ausmacht.

Zumal ich es eh so sehe, dass das Laden der kompletten Datei schneller sein sollte, als wenn man (bei einem MikroKernel) sie in mehreren Stücken laden lässt.
Du lädst einmal die ersten 8K als Kernel (um den korrekten Loader zu ermitteln). Ist es keine erkannte Datei (*), brauchst du den Rest nicht mehr laden. Der Loader kümmert sich um das Laden und Parsen. Er kann die Datei trotzdem am Stück laden, das schließt sich ja nicht aus.

(*) Jemand hat ein NTFS gemountet, wo Dateien standardmäßig als executable markiert sind und im "mc" auf eine Videodatei gedrückt. Dann erstmal 3 GB in den RAM laden zu müssen ist unschön.

Zitat von: svenska
Das Executable darf keine fixen Adressen anfordern (du könntest natürlich Segmentierung nutzen, dann geht das), sondern der Loader verrät dem Executable, an welche Adresse es geladen wurde. Gleiches kann auch für die benötigten Libraries gelten.
Das sehe ich anders (und der ELF-Standard auch ;) ). Eine ganz normale Executeable-ELF-Datei (kein shared-object) kann komplett fertig gelinkt sein, sprich man muss nur noch die Segmente an die entsprechenden Adressen packen und dann läuft das Programm (Libraries werden in dem Fall statisch gelinkt).
Okay, ich schwäche ab: sollte keine fixen Adressen anfordern. Was passiert denn, wenn du zwei Executables an die gleiche Adresse gelinkt hast und sie ausführen möchtest?

Entweder du kannst die Sections im Adressraum beliebig verteilen, dann kann der Loader sie dorthin schieben, wo er sie gern hätte und wo Platz ist. Oder du linkst sie an fixe Adressen und kannst nur eine Anwendung gleichzeitig (pro Segment) haben; der Loader berücksichtigt das oder verweigert den Start.

PS: Zum Zitieren kannst du den Knopf drücken, dann bleibt die Verlinkung und Formatierung erhalten.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 28. December 2010, 14:16 »
Zitat von: svenska
Henne-Ei-Problem: Der Kernel muss den Runtime-Loader starten können... dessen Format muss aber kein full-blown ELF sein.
Das ist ein Problem was ich mit dem Runtime-Loader habe, aber das sollte sich irgendwie lösen lassen.

Zitat von: svenska
Du lädst einmal die ersten 8K als Kernel (um den korrekten Loader zu ermitteln). Ist es keine erkannte Datei (*), brauchst du den Rest nicht mehr laden. Der Loader kümmert sich um das Laden und Parsen. Er kann die Datei trotzdem am Stück laden, das schließt sich ja nicht aus.
Richtig und guter Punkt. Ich würde dann sogar sagen, das man zuerst 8K lädt und guckt was für ein Format es ist bzw. ob es unterstützt wird und dann wird ein fork() gemacht.

Zitat von: svenska
Okay, ich schwäche ab: sollte keine fixen Adressen anfordern. Was passiert denn, wenn du zwei Executables an die gleiche Adresse gelinkt hast und sie ausführen möchtest?

Entweder du kannst die Sections im Adressraum beliebig verteilen, dann kann der Loader sie dorthin schieben, wo er sie gern hätte und wo Platz ist. Oder du linkst sie an fixe Adressen und kannst nur eine Anwendung gleichzeitig (pro Segment) haben; der Loader berücksichtigt das oder verweigert den Start.
Denk nochmal darüber nach was du hier geschrieben hast ;)

Jedes Programm hat seinen eigenen Adressraum und somit gibt es keine Probleme mit fixen Adressen. Das Problem was ich halt mit fixen Adressen sehe ist, dass das Programm gerne an eine Adresse geladen werden möchte wo der Runtime-Loader ist (und um überhaupt Programme erstellen zu können darf der Runtime-Loader auch nicht an der Standard-Start-Adresse stehen) und das geht ja dann nicht.

Zitat von: svenska
PS: Zum Zitieren kannst du den Knopf drücken, dann bleibt die Verlinkung und Formatierung erhalten.
Zu faul ;)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 28. December 2010, 20:28 »
Zitat von: svenska
Henne-Ei-Problem: Der Kernel muss den Runtime-Loader starten können... dessen Format muss aber kein full-blown ELF sein.
Das ist ein Problem was ich mit dem Runtime-Loader habe, aber das sollte sich irgendwie lösen lassen.
Der Kernel muss in der Lage sein, ein Binärformat direkt ausführen zu können. Das kann ganz simpel sein (flat binary an fixe Adresse). Man kann im Kernel auch eine Liste statisch hinterlegen, welche Binaries damit ausgeführt werden können, dann ist der Sicherheitsaspekt nicht zu stark vernachlässigt.

Alternativ ist der Runtime Loader ein Teil des Kernels, aber das willst du sicherlich nicht. ;-)

Richtig und guter Punkt. Ich würde dann sogar sagen, das man zuerst 8K lädt und guckt was für ein Format es ist bzw. ob es unterstützt wird und dann wird ein fork() gemacht.
Der Kernel forkt sich selbst? Ich dachte, er macht ein CreateUserspaceProcess() oder Äquivalent; die 8K muss er übrigens selbst laden, um den richtigen Loader auswählen zu können.

Jedes Programm hat seinen eigenen Adressraum und somit gibt es keine Probleme mit fixen Adressen. Das Problem was ich halt mit fixen Adressen sehe ist, dass das Programm gerne an eine Adresse geladen werden möchte wo der Runtime-Loader ist (und um überhaupt Programme erstellen zu können darf der Runtime-Loader auch nicht an der Standard-Start-Adresse stehen) und das geht ja dann nicht.
Wenn ich ein paged-flat-memory-Speichermodell habe, dann gibt es die Adresse 0x1234 systemweit doch nur ein einziges Mal; wie kann dann jeder Prozess diese Adresse nutzen? Das ist doch der Sinn von flat memory? Nur Segmentierung erzeugt doch jedesmal einen neuen Adressraum? Ich hab da glaube grad ein Brett vorm Schädel, führ mal bitte genauer aus.

Gruß

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 28. December 2010, 20:47 »
Zitat von: svenska
Alternativ ist der Runtime Loader ein Teil des Kernels, aber das willst du sicherlich nicht.
Genau deswegen versuche ich mich mit nem Runtime-Loader anzufreunden. Damit ich das alles aus dem Kernel rausbekomme und der nur noch ne Art Blackbox ist, wo kein unbekannter Code rein kann und der auch ansonsten nicht viele Angriffspunkte biete.

Zitat von: svenska
Der Kernel forkt sich selbst? Ich dachte, er macht ein CreateUserspaceProcess() oder Äquivalent; die 8K muss er übrigens selbst laden, um den richtigen Loader auswählen zu können.
So wie ich mir das vorstelle, macht der Runtime-Loader so ziemlich alles (jetzt kommt mir gerade eine Idee :D ).

Ich bräuchte also nur nen fork() Syscall und nen createThread() Syscall. Der Runtime-Loader forkt sich selber (nachdem er überprüft hat ob die Datei die er ausführen soll in Ordnung ist) und erledigt dann die ganze Arbeit.

Somit würde es keinen exec() Syscall (oder halt createProcess()) geben, sondern das wäre nen RPC zum Runtime-Loader. So würde ich auch halbwegs POSIX (allerdings ohne Signale) bei mir unterbekommen ohne zu sehr die UNIX-Konzepte zu nutzen (und ich weiß endlich wofür fork() genutzt werden kann).

Wobei ich mir dann trotzdem noch was einfallen lassen muss um überhaupt den Runtime-Loader zum Laufen zu bekommen und auch wie ich meinen Storage-Server zum Laufen bekomme. Denn der Storage-Server wird ja benötigt um irgendwelche Dateien zu laden und genau das braucht der Runtime-Loader. Ich werde also wohl auch sowas wie erik machen müssen, eine lebend Geburt oder ich verpasse dem Runtime-Loader die Fähigkeit, das er auch nen Datei-Image direkt bekommt und dieses "ausführt" (könnte sowas noch woanders als beim Booten nützlich sein?).

Zitat von: svenska
Wenn ich ein paged-flat-memory-Speichermodell habe, dann gibt es die Adresse 0x1234 systemweit doch nur ein einziges Mal; wie kann dann jeder Prozess diese Adresse nutzen? Das ist doch der Sinn von flat memory? Nur Segmentierung erzeugt doch jedesmal einen neuen Adressraum? Ich hab da glaube grad ein Brett vorm Schädel, führ mal bitte genauer aus.
Der Sinn von Paging ist, das du so viele virtuelle (und das ist der Punkt) Adressräume haben kannst, wie der RAM hergibt. Du hast doch ein PageDirectory und jeder Prozess hat sein eigenes PD und somit seinen eigenen Adressraum.

Edit::

Ein Problem habe ich noch, wie bekommt man den Runtime-Loader wieder aus dem Adressraum des neuen Prozesses oder anderes gefragt, wie unmappt man gerade laufenden Code und springt zu neuem Code?
« Letzte Änderung: 28. December 2010, 21:42 von FlashBurn »

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #9 am: 28. December 2010, 21:43 »
Moin

Das FLAT Memory Modell bezieht sich auf den Physischen Speicher, den du mit der GDT verwaltest.. Der Pseicherschutz wird virtuell durch das Paging bereitgestellt. es könnten Theoretisch Millionen Programme an der adresse 0x1234 sein. da jedesmal das PD(Page Directory) neugeladen wird, kann man jedem Prozess einen eiogenen Adressraum geben.

PNoob

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 28. December 2010, 21:56 »
Zitat von: svenska
Alternativ ist der Runtime Loader ein Teil des Kernels, aber das willst du sicherlich nicht.
Genau deswegen versuche ich mich mit nem Runtime-Loader anzufreunden. Damit ich das alles aus dem Kernel rausbekomme und der nur noch ne Art Blackbox ist, wo kein unbekannter Code rein kann und der auch ansonsten nicht viele Angriffspunkte biete.
Der Kernel muss Binaries ausführen können. Da kannst du mit Signaturen (oder vorgefertigten Listen im Kernel) arbeiten. Die weiteren Runtime-Loader können ja selbst wieder ELF-Dateien sein (finde ich aber unschön).

Ich bräuchte also nur nen fork() Syscall und nen createThread() Syscall. Der Runtime-Loader forkt sich selber (nachdem er überprüft hat ob die Datei die er ausführen soll in Ordnung ist) und erledigt dann die ganze Arbeit.

Somit würde es keinen exec() Syscall (oder halt createProcess()) geben, sondern das wäre nen RPC zum Runtime-Loader. So würde ich auch halbwegs POSIX (allerdings ohne Signale) bei mir unterbekommen ohne zu sehr die UNIX-Konzepte zu nutzen (und ich weiß endlich wofür fork() genutzt werden kann).
Du führst also alle Anwendungen als Teile des Runtime-Loaders aus? Das finde ich unschön. Der Sinn von Anwendungen ist es doch, als eigene Prozesse ausgeführt zu werden...?

Runtime-Loader wird vom Kernel gestartet (CreateUserspaceProcess) und bekommt als Parameter eine Datei. Diese wird geladen, validiert, mit allen Abhängigkeiten geladen (diese werden auch validiert) und schließlich führt der Runtime-Loader ein exec() auf das frisch geladene Image aus. Besser wäre, der Runtime-Loader erstellt einen neuen Process mit dem Speicher-Image (dann leaken keine Descriptoren/Datenstrukturen vom Loader in die Anwendungen).

Wobei ich mir dann trotzdem noch was einfallen lassen muss um überhaupt den Runtime-Loader zum Laufen zu bekommen und auch wie ich meinen Storage-Server zum Laufen bekomme. Denn der Storage-Server wird ja benötigt um irgendwelche Dateien zu laden und genau das braucht der Runtime-Loader. Ich werde also wohl auch sowas wie erik machen müssen, eine lebend Geburt oder ich verpasse dem Runtime-Loader die Fähigkeit, das er auch nen Datei-Image direkt bekommt und dieses "ausführt" (könnte sowas noch woanders als beim Booten nützlich sein?).
Hmm. Eine Idee: Du hast eine einzige Kernelfunktion CreateUserspaceProcessFromImage(), der du ein vorbereitetes Stück RAM übergibst. Alle Server, die du zum Booten brauchst, liegen bereits in diesem Format vor, werden also in den RAM geladen und direkt ausgeführt. Der Runtime-Loader liegt auch in diesem Format vor, liegt nur auf der Platte. Die Aufgabe des Runtime-Loaders ist es dann, ELF-Dateien in den RAM zu laden, zu validieren, dieses Format zu erzeugen und dann mittels dieser Funktion den Prozess zum Leben zu erwecken.

Das wäre ein Beispiel für das Binärformat, was ich die letzten Posts erwähnt hatte; quasi das "dumme", native Format des Kernels. Du kannst dafür übrigens auch gleich ELF nehmen, dann brauchst du keinen Runtime-Loader für ELF-Dateien mehr, sondern nur für "alles andere".

Zitat von: svenska
Wenn ich ein paged-flat-memory-Speichermodell habe, dann gibt es die Adresse 0x1234 systemweit doch nur ein einziges Mal; wie kann dann jeder Prozess diese Adresse nutzen? Das ist doch der Sinn von flat memory? Nur Segmentierung erzeugt doch jedesmal einen neuen Adressraum? Ich hab da glaube grad ein Brett vorm Schädel, führ mal bitte genauer aus.
Der Sinn von Paging ist, das du so viele virtuelle (und das ist der Punkt) Adressräume haben kannst, wie der RAM hergibt. Du hast doch ein PageDirectory und jeder Prozess hat sein eigenes PD und somit seinen eigenen Adressraum.
Stimmt, du tauschst ja beim Prozesswechsel einen Teil des Adressraumes aus. Bei der Segmentierung hast du mehrere Adressräume gleichzeitig in einem RAM, bei Paging hast du einen Adressraum, und mehrere RAMs (die sich beliebig überlappen).

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 28. December 2010, 22:17 »
Zitat von: svenska
Der Kernel muss Binaries ausführen können.
Da bin ich jetzt mal anderer Meinung. Das einzige was der Kernel können muss ist ein fork() bereitstellen, das reicht vollkommen aus!

Ich gehe jetzt einfach mal davon aus, dass das System schon läuft und man ein neues Programm ausführen will.
Man macht also nen Aufruf exec("./foobar") und das wiederum ruft den Runtime-Loader auf. Dieser lädt, meinetwegen die ersten 8KB, die Datei und guckt ob es ein Format ist das er unterstützt. Dann wird ein fork() gemacht und die Datei wird geparst und das Process-Image wird erstellt. Das Programm ist jetzt soweit fertig und der Runtime-Loader muss aus dem Process-Adressraum geunmappt werden und der Einsprungspunkt des Programms (meistens main()) muss ausgeführt werden.

Das einzige Mal wo der Kernel irgendetwas mit nem neuen Prozess erstellen zu tun hatte, war fork().

Zitat von: svenska
Du führst also alle Anwendungen als Teile des Runtime-Loaders aus? Das finde ich unschön. Der Sinn von Anwendungen ist es doch, als eigene Prozesse ausgeführt zu werden...?
Ein fork() erstellt doch im Endeffekt einen neuen Prozess, der dem alten halt nur verdammt ähnlich sieht. Exec() übernimmt dann das vorhandene PD und packt nen neuen Process-Image rein. Diesen ganzen exec()-Kram will ich dann im UserSpace vom Runtime-Loader machen lassen ohne das der Kernel das machen muss.

Zitat von: svenska
und schließlich führt der Runtime-Loader ein exec() auf das frisch geladene Image aus.
Korrigiere mich wenn ich unrecht habe, aber das entspricht so ziemlich fast genau dem was ich vorhabe, aber nicht dem wie es unter Linux läuft.
Unter Linux erstellt exec() das Image und lädt alle Dateien.

Zitat von: svenska
Das wäre ein Beispiel für das Binärformat, was ich die letzten Posts erwähnt hatte; quasi das "dumme", native Format des Kernels. Du kannst dafür übrigens auch gleich ELF nehmen, dann brauchst du keinen Runtime-Loader für ELF-Dateien mehr, sondern nur für "alles andere".
Ziel von mir ist es aber das ganze Zeug aus dem Kernel raus zu haben. Ich würde meinen Kernel gerne so flexibel wie möglich halten und da ist nunmal ein Runtime-Loader im UserSpace nötig (frag mich nicht warum ich das machen will, ist einfach so ;) ).

Wozu muss der Kernel auch mehr darüber wissen?

Ich muss nur noch gucken, das ich es hinbekomme meinen Bootvorgang flexibler zu gestalten, weil im Moment muss der Kernel sehr genau wissen welche Server zu starten sind und das ist mir alles noch zu unflexibel. Ich bräuchte halt doch sowas wie nen init-Process (schon wieder so ein UnixKonzept ;) ).

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 28. December 2010, 23:23 »
Zitat von: svenska
Der Kernel muss Binaries ausführen können.
Da bin ich jetzt mal anderer Meinung. Das einzige was der Kernel können muss ist ein fork() bereitstellen, das reicht vollkommen aus!
Nein. fork() kopiert einen vorhandenen Prozess. Der muss aber erstmal vorhanden sein! Der Kernel selbst ist an sich kein Prozess, außerdem willst du ihn nicht einfach duplizieren. Du brauchst also einen CreateUserspaceProcess-Syscall. Und der braucht entweder eine Binärdatei, die im RAM liegt (z.B. ELF) oder einen bereits präparierten Bereich im RAM.

Man macht also nen Aufruf exec("./foobar") und das wiederum ruft den Runtime-Loader auf. Dieser lädt, meinetwegen die ersten 8KB, die Datei und guckt ob es ein Format ist das er unterstützt. Dann wird ein fork() gemacht und die Datei wird geparst und das Process-Image wird erstellt. Das Programm ist jetzt soweit fertig und der Runtime-Loader muss aus dem Process-Adressraum geunmappt werden und der Einsprungspunkt des Programms (meistens main()) muss ausgeführt werden.
Anderer Ansatzpunkt, der mir besser gefällt: exec() ist eine Funktion der libc. Diese Funktion öffnet die Datei, sucht den passenden Runtime Loader, und startet ihn. Der Runtime-Loader lädt die ersten 8KB, konstruiert ein Prozess-Image und weist den Kernel mit einem speziellen Syscall an, aus diesem Prozess-Image einen neuen Userspace-Prozess zu bauen. Anschließend endet der Runtime-Loader, er ist also ein Prozess wie jeder andere, liegt aber im gleichen Format vor wie die Server.

Der Runtime-Loader lädt und analysiert die gesamte Datei, nicht nur die ersten 8K; das sollte (in meiner Welt) vorher geschehen. (Findet er dennoch Unsinn, bricht er natürlich trotzdem ab.)

Der Vorteil des speziellen Syscalls CreateUserProcessFromImage (toller Name) liegt darin, dass er keine Unterstützung vom Userspace benötigt, also benutzt werden kann, um den Userspace beim Booten zu erzeugen. Deine Server brauchen also nur in diesem Format irgendwo im RAM liegen und der Kernel kann sie von alleine starten; die libc kann damit ebenfalls Prozesse anlegen.

Ein fork() erstellt doch im Endeffekt einen neuen Prozess, der dem alten halt nur verdammt ähnlich sieht. Exec() übernimmt dann das vorhandene PD und packt nen neuen Process-Image rein. Diesen ganzen exec()-Kram will ich dann im UserSpace vom Runtime-Loader machen lassen ohne das der Kernel das machen muss.
Ich dachte eigentlich, du wolltest fork()/exec() nicht haben, weil dir das zu sehr Unix ist... und für den ersten Prozess im System kannst du fork() nicht benutzen (es gibt noch keine Vorlage) und exec() in der libc sollte ein Wrapper um einen CreateProcess-Syscall sein.

Zitat von: svenska
und schließlich führt der Runtime-Loader ein exec() auf das frisch geladene Image aus.
Korrigiere mich wenn ich unrecht habe, aber das entspricht so ziemlich fast genau dem was ich vorhabe, aber nicht dem wie es unter Linux läuft.
Unter Linux erstellt exec() das Image und lädt alle Dateien.
Ja, das exec() der libc tut das. Was der Syscall tut, weiß ich nicht. Ich vermute, der tut das alles nicht.

Ziel von mir ist es aber das ganze Zeug aus dem Kernel raus zu haben. Ich würde meinen Kernel gerne so flexibel wie möglich halten und da ist nunmal ein Runtime-Loader im UserSpace nötig (frag mich nicht warum ich das machen will, ist einfach so ;) ).
Stimmt. Du musst nur trotzdem in der Lage sein, den Runtime Loader zu starten, ohne ihn benutzen zu können und das setzt eine Möglichkeit voraus, einen Prozess erstellen zu können. Und diese Fähigkeit muss unabhängig von vorhandenem Userspace sein - du musst deine Server auch ohne den Runtime-Loader starten können, weil der ist noch nicht verfügbar.

Ich muss nur noch gucken, das ich es hinbekomme meinen Bootvorgang flexibler zu gestalten, weil im Moment muss der Kernel sehr genau wissen welche Server zu starten sind und das ist mir alles noch zu unflexibel. Ich bräuchte halt doch sowas wie nen init-Process (schon wieder so ein UnixKonzept ;) ).
Also "init" sollte keine notwendigen Server starten. Sinnvoll wäre, dass der Bootloader das Kernel-Image und die notwendigen Server lädt und dem Kernel mitteilt, dass diese gestartet werden sollen. Wenn alle Server fertig initialisiert sind und blocken, dann wird der Userspace gestartet. "init" ist der Einstieg in den Userspace, alles kernelartige ist dann schon fertig, alle notwendigen Treiber geladen.

Das noch weiter zu abstrahieren halte ich nicht für sinnvoll. Der Bootloader hat in der Regel nur sehr wenige Möglichkeiten, auf Hardware zuzugreifen (generischer HDD-Treiber, TFTP) und ist daher für den Ladevorgang sehr langsam. Er sollte also grundsätzlich nur wenige, bestimmte Server laden, um die Datenmenge klein zu halten. Diese notwendige Liste ist bekannt und relativ statisch. Wenn die notwendigen Server laufen, startet der Userspace. Wenn der Userspace einen Service benötigt, der noch nicht vorhanden ist, wird er nachträglich gestartet. (Ich bin ein Feind von Bluetooth-Daemons unter Linux, wenn ich keinen Bluetooth-Adapter habe. Oder die Funktionalität noch nicht benötige.)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 29. December 2010, 10:56 »
Ich habe mir heute auf Arbeit nochmal Gedanken gemacht.

Die ersten 4MB eines jeden Prozesses sind bei mir immer frei (unter anderen um Null-Pointer abzufangen), die würde ich bzw. könnte ich für den Runtime-Loader nutzen.

Mein Bootloader müsste also den Runtime-Loader soweit fertig machen, das praktisch nur noch die Pages in ein PD kopiert werden müssen und dann kann der Runtime-Loader laufen.

Ein fork() Aufruf würde dann ein neues PD erstellen und den Runtime-Loader in die ersten 4MB packen. Der Runtime-Loader erledigt dann seine Arbeit und macht nen exec() Aufruf.

fork() wäre bei mir einfach nur das ein neuer Prozess (als Vater den aufrufenden Prozess) anlegen würde, in dem außer des Runtime-Loaders nichts anderes drin wäre.
exec() würde dann bei mir den Runtime-Loader wieder aus dem Adressraum entfernen und würde main() ausführen.

Ich sollte aber besser andere Namen für fork() und exec() wählen. Denn ansonsten wird es zu Missverständnissen kommen.

Zitat von: svenska
Ich dachte eigentlich, du wolltest fork()/exec() nicht haben, weil dir das zu sehr Unix ist... und für den ersten Prozess im System kannst du fork() nicht benutzen (es gibt noch keine Vorlage) und exec() in der libc sollte ein Wrapper um einen CreateProcess-Syscall sein.
Ich bin meistens anderer Meinung als die Unix-Konzepte (lasse mich aber gerne von diesen Konzepten überzeugen). Ich würde aber schon gerne Programme leicht auf mein OS portieren können und da kommt man dann dummerweise nicht an POSIX vorbei und das ist wie ich finde, nen zeimlicher Showstopper :( Ich hatte daher eh schon mit dem Gedanken gespielt fork() bei mir noch zusätzlich zu implementieren.

Zitat von: svenska
Stimmt. Du musst nur trotzdem in der Lage sein, den Runtime Loader zu starten, ohne ihn benutzen zu können und das setzt eine Möglichkeit voraus, einen Prozess erstellen zu können. Und diese Fähigkeit muss unabhängig von vorhandenem Userspace sein - du musst deine Server auch ohne den Runtime-Loader starten können, weil der ist noch nicht verfügbar.
Das will ich alles im Bootloader machen, sprich der lädt nur das was alles zum Laufen nötig ist (Kernel, Runtime-Loader, Storage-Server, Device-Server, die wichtigsten Treiber) und er übernimmt praktisch auch die Aufgabe des Runtime-Loaders und macht aus den Dateien (in meinem Fall ELF) fertige Process-Images. Diese müssen vom Kernel nur noch in ein PD gepackt werden (obwohl man das auch schon im Bootloader machen kann) und dann gestartet werden.

Zitat von: svenska
Also "init" sollte keine notwendigen Server starten. Sinnvoll wäre, dass der Bootloader das Kernel-Image und die notwendigen Server lädt und dem Kernel mitteilt, dass diese gestartet werden sollen. Wenn alle Server fertig initialisiert sind und blocken, dann wird der Userspace gestartet. "init" ist der Einstieg in den Userspace, alles kernelartige ist dann schon fertig, alle notwendigen Treiber geladen.

Das noch weiter zu abstrahieren halte ich nicht für sinnvoll. Der Bootloader hat in der Regel nur sehr wenige Möglichkeiten, auf Hardware zuzugreifen (generischer HDD-Treiber, TFTP) und ist daher für den Ladevorgang sehr langsam. Er sollte also grundsätzlich nur wenige, bestimmte Server laden, um die Datenmenge klein zu halten. Diese notwendige Liste ist bekannt und relativ statisch. Wenn die notwendigen Server laufen, startet der Userspace. Wenn der Userspace einen Service benötigt, der noch nicht vorhanden ist, wird er nachträglich gestartet. (Ich bin ein Feind von Bluetooth-Daemons unter Linux, wenn ich keinen Bluetooth-Adapter habe. Oder die Funktionalität noch nicht benötige.)
Ich brauche deswegen einen init-Prozess, weil ja irgendjemand die anderen Server starten muss und ich das am liebsten in irgendein Skript auslagern würde, so dass es möglich ist auch andere als die Standard-Server zu nutzen (ist ein Angriffspunkt, aber egal).

Was den Runtime-Loader und den Rest den der Bootloader lädt betrifft, die gebe ich auch über ein Skript beim Bootloader an, das ist also schon flexibel.

Was dein Bsp. mit dem Bluetooth-Daemon betrifft, da würde ich es so handhaben, dass der Bluetooth-Treiber einfach den Server startet und ansonsten läuft der nicht.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 29. December 2010, 14:20 »
Mein Bootloader müsste also den Runtime-Loader soweit fertig machen, das praktisch nur noch die Pages in ein PD kopiert werden müssen und dann kann der Runtime-Loader laufen.
Dein Runtime-Loader läuft also immer, wenn das System läuft und es gibt prinzipiell nur einen. Kann man so machen.

Ein fork() Aufruf würde dann ein neues PD erstellen und den Runtime-Loader in die ersten 4MB packen. Der Runtime-Loader erledigt dann seine Arbeit und macht nen exec() Aufruf.
Du weißt aber schon, was fork() macht? Für ein fork() brauchst du den Runtime-Loader nämlich nicht, du führst ja keine auf Platte vorliegende Binärdatei aus.

fork() wäre bei mir einfach nur das ein neuer Prozess (als Vater den aufrufenden Prozess) anlegen würde, in dem außer des Runtime-Loaders nichts anderes drin wäre.
Das ist kein fork(), das ist ein CreateProcessFromELFFile(). Du brauchst den Runtime-Loader nicht, wenn die Datei bereits im Kernel-nativen Format vorliegt (für den Runtime-Loader selbst ist das nötig), und du brauchst ihn nicht, wenn der Prozess bereits läuft.

exec() würde dann bei mir den Runtime-Loader wieder aus dem Adressraum entfernen und würde main() ausführen.
Du musst unterscheiden zwischen exec(), wie es Anwendungen nutzen (also das fork/exec-Pärchen) und wie es der Runtime-Loader nutzt. Denn exec() ersetzt die laufende Anwendung durch eine auf Platte liegende Datei und benötigt dazu einen Runtime-Loader. Das geht offensichtlich nicht, wenn es der Runtime-Loader selbst ist... zumal, wenn der Runtime-Loader in den ersten 4MB liegt, die Anwendung dort gerade nicht liegt, du also nicht den Runtime-Loader mit exec() ersetzen kannst, ohne einen Spezialfall zu deklarieren.

Ich sollte aber besser andere Namen für fork() und exec() wählen. Denn ansonsten wird es zu Missverständnissen kommen.
Oder mit fork()/exec() das bezeichnen, was es ist... und für den Rest neue Namen erfinden.

Ich bin meistens anderer Meinung als die Unix-Konzepte (lasse mich aber gerne von diesen Konzepten überzeugen). Ich würde aber schon gerne Programme leicht auf mein OS portieren können und da kommt man dann dummerweise nicht an POSIX vorbei und das ist wie ich finde, nen zeimlicher Showstopper :( Ich hatte daher eh schon mit dem Gedanken gespielt fork() bei mir noch zusätzlich zu implementieren.
Also Perl implementiert ein fork() innerhalb des Interpreters...  ;-) Wenn du fork() zusätzlich implementierst, dann musst du es nicht fürs Booten erzeugen. Und wenn du fork() im POSIX-Layer implementierst, kannst du zum Booten trotzdem die nativen Funktionen benutzen. Denn fork() ist ein tolles Teil, aber damit kann man nicht alles erschlagen.

Das will ich alles im Bootloader machen, sprich der lädt nur das was alles zum Laufen nötig ist (Kernel, Runtime-Loader, Storage-Server, Device-Server, die wichtigsten Treiber) und er übernimmt praktisch auch die Aufgabe des Runtime-Loaders und macht aus den Dateien (in meinem Fall ELF) fertige Process-Images. Diese müssen vom Kernel nur noch in ein PD gepackt werden (obwohl man das auch schon im Bootloader machen kann) und dann gestartet werden.
Also ist der Runtime-Loader ein Server und du führst eine Lebendgeburt des Betriebssystems durch. Finde ich eher unelegant, aber gut.

Ich brauche deswegen einen init-Prozess, weil ja irgendjemand die anderen Server starten muss und ich das am liebsten in irgendein Skript auslagern würde, so dass es möglich ist auch andere als die Standard-Server zu nutzen (ist ein Angriffspunkt, aber egal).
Du musst unterscheiden zwischen dem Bootvorgang und dem Zeitraum, in dem das System läuft. Der Bootvorgang kann (und sollte) statisch sein, der Bootloader übergibt an den Kernel eine Liste mit den Services, die er bereits vorbereitet hat und fertig. Um diese Liste zu ändern, musst du den Bootloader konfigurieren (vgl. GRUB). Zur Laufzeit des Systems brauchst du nur die Fähigkeit, Services auch nachträglich starten und beenden zu können. Und das ist wieder unabhängig vom init-Prozess, sondern kann alles mögliche (und auch konfigurierbar) sein.

Was den Runtime-Loader und den Rest den der Bootloader lädt betrifft, die gebe ich auch über ein Skript beim Bootloader an, das ist also schon flexibel.
Eben. Der init-Prozess muss keine weiteren Services starten, solange sie nicht benötigt werden.

Was dein Bsp. mit dem Bluetooth-Daemon betrifft, da würde ich es so handhaben, dass der Bluetooth-Treiber einfach den Server startet und ansonsten läuft der nicht.
Jetzt habe ich aber einen Bluetooth-USB-Stick. Der USB-Stack stellt das fest, lädt den Treiber, startet den Bluetooth-Server und ist freut sich. Ich mag aber garkein Bluetooth nutzen... weil ich möchte heute nur Youtube gucken. Also ist der Server überflüssig, kostet RAM und Performance.

Das ist im Übrigen das, was der systemd tun soll, du kannst dir mal die Abstracts dazu anschauen, da sind schöne Gedanken dabei.

Was du vorschlägst, funktioniert. Ich finds nur nicht unbedingt hübsch. ;-)

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 29. December 2010, 14:46 »
Zitat von: svenska
Dein Runtime-Loader läuft also immer, wenn das System läuft und es gibt prinzipiell nur einen. Kann man so machen.
Ich würde das nicht laufen nennen. Der ist halt einfach nur vorhanden, laufen tut er immer nur im Kontext des Prozess erstellens.

Eigentlich hatte ich geplant das man den Runtime-Loader per Add-Ons erweitern kann, da muss ich dann aber mal sehen wie ich das mache. Auch dort sollten dann 4MB vollkommen ausreichend sein.

Zitat von: svenska
Das ist kein fork(), das ist ein CreateProcessFromELFFile(). Du brauchst den Runtime-Loader nicht, wenn die Datei bereits im Kernel-nativen Format vorliegt (für den Runtime-Loader selbst ist das nötig), und du brauchst ihn nicht, wenn der Prozess bereits läuft.
Entweder willst du mich nicht verstehen oder verstehst mich wirklich nicht ;)

Es gibt bei mir kein Kernel-natives Format. Der Kernel hat damit rein gar nichts zu tun! Der Kernel erstellt einfach nur nen nackten Adressraum und kopiert den Runtime-Loader (der fix und fertig ist) in die ersten 4MB. Der Runtime-Loader erledigt dann die ganzen Sachen wie Parsen, Dateien laden und Adressen auflösen.

Was mein "exec"() betrifft, sollte ich vllt noch dazu schreiben, das nur der Runtime-Loader in der Lage ist diesen Syscall zu benutzen.

Ich sag mal mein fork() sollte eventuell createEmptyProcess heißen und mein exec() dann unmapRuntimeLoaderStartProcess oder sowas.

Zitat von: svenska
Also ist der Runtime-Loader ein Server und du führst eine Lebendgeburt des Betriebssystems durch. Finde ich eher unelegant, aber gut.
Anders gefragt, wie willst du das Problem mit der Lebendgeburt umgehen (bei einem MikroKernel)? Und mein Runtime-Loader ist kein Server, sieh es eher als einen Teil des Kernels, der im UserSpace liegt und läuft und auf der Platte auch eine extra Datei darstellt.

Zitat von: svenska
Der Bootvorgang kann (und sollte) statisch sein, der Bootloader übergibt an den Kernel eine Liste mit den Services, die er bereits vorbereitet hat und fertig.
Das funktioniert so nicht und soll es auch nicht machen. Mir geht es darum, das nirgends fest einprogrammiert ist, das der App-Server auch die Datei "app-server" sein muss (mal als Bsp.). Sicherlich kann ich das alles in mein Bootskript packen und der Bootloader parst dieses Skript und gibt die Daten dann fertig und einfach lesbar an den Kernel weiter und dieser dann an den Storage-Server, der dann die verbleibenden Server startet.

Zitat von: svenska
Jetzt habe ich aber einen Bluetooth-USB-Stick. Der USB-Stack stellt das fest, lädt den Treiber, startet den Bluetooth-Server und ist freut sich. Ich mag aber garkein Bluetooth nutzen... weil ich möchte heute nur Youtube gucken. Also ist der Server überflüssig, kostet RAM und Performance.
Dem muss ich erstmal entgegen halten, das man sowas manuel deaktivieren kann und ein anderes Bsp wäre, nur weil du den Sound gerade Stumm geschalten hast, wird nicht der Treiber beendet und alles was sonst noch zum Sound dazu gehört. Es ist also gang und gäbe das es so gehandhabt wird.

Um es mal deutlicher zu machen, wenn du Bluetooth eh nicht nutzt, warum dann nicht einfach den Stick (wenn man schon einen hat) abmachen?

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 29. December 2010, 15:45 »
Eigentlich hatte ich geplant das man den Runtime-Loader per Add-Ons erweitern kann, da muss ich dann aber mal sehen wie ich das mache. Auch dort sollten dann 4MB vollkommen ausreichend sein.
Plugin-Systeme im Kernel. Pfui. Du sollst nicht den ultimativen Runtime-Loader haben, sondern du willst einen kleinen, speziellen Runtime-Loader für jedes zu unterstützende Format haben. Weniger Code, weniger Fehlerquellen, weniger Verschwendung.

Es gibt bei mir kein Kernel-natives Format. Der Kernel hat damit rein gar nichts zu tun! Der Kernel erstellt einfach nur nen nackten Adressraum und kopiert den Runtime-Loader (der fix und fertig ist) in die ersten 4MB. Der Runtime-Loader erledigt dann die ganzen Sachen wie Parsen, Dateien laden und Adressen auflösen.
Dann ist das native Kernel-Format genau das, was du als "Runtime-Loader (der fix uns fertig ist)" bezeichnest. Man kann es dann nicht für andere Dinge benutzen, weil der Runtime-Loader somit ein Teil des Kernels ist (ein BLOB, was in jeden Prozess kopiert wird).

Was mein "exec"() betrifft, sollte ich vllt noch dazu schreiben, das nur der Runtime-Loader in der Lage ist diesen Syscall zu benutzen.
Also eher ein Exec (Syscall) statt ein exec() (Funktion), das meinte ich mit dem Spezialfall. Der Runtime-Loader ist also ein Teil des Kernels, läuft im Userspace-Kontext, hat keinen Zugriff auf die libc-Funktionalität, dafür aber einen speziellen Syscall und ist allgemein etwas ganz besonderes. Stimmt das soweit?

Ich sag mal mein fork() sollte eventuell createEmptyProcess heißen und mein exec() dann unmapRuntimeLoaderStartProcess oder sowas.
Damit sind fork() und exec() nicht mehr das, was man landläufig darunter versteht. ;-)

Was du als unmapRuntimeLoaderStartProcess bezeichnest, hatte ich CreateProcessFromImage genannt, mit einem kleinen Unterschied: Dein Runtime-Loader baut aus der ELF-Datei eine Umgebung auf, mit der du eine Wiedergeburt des aktuellen Prozesses vornimmst, mein Gedankengang baut eine neue Umgebung auf, die vom Kernel zu einem Prozess wird.

Was besser ist, weiß ich nicht. Ich halte meinen Ansatz für flexibler.

Anders gefragt, wie willst du das Problem mit der Lebendgeburt umgehen (bei einem MikroKernel)? Und mein Runtime-Loader ist kein Server, sieh es eher als einen Teil des Kernels, der im UserSpace liegt und läuft und auf der Platte auch eine extra Datei darstellt.
Hatte ich schonmal beschrieben... der Bootloader lädt den Kernel und die zwingend notwendigen Module (im kernel-nativen Binärformat, z.B. flat binary) in den RAM und übergibt eine Liste mit Zeigern auf diese Strukturen an den Kernel. Dann springt er in den Kernel, der fängt an zu laufen, initialisiert alles mögliche und ruft für jeden dieser Zeiger ein CreateProcessFromImage() auf. Dann laufen die bootkritischen Server und können verwendet werden. Wenn das geschehen ist, wird vom Kernel der eigentliche Userspace initialisiert, also konkret "init" gestartet. Das geht, weil ja alles, was dafür nötig ist, bereits läuft.

Der Runtime-Loader ist bei mir kein Teil des Kernels, sondern eine Anwendung wie jede andere auch, mit dem Unterschied, dass sie keinen Runtime-Loader benötigt, sondern bereits im kernel-nativen Binärformat vorliegen muss. Es gibt also ein Format, welches der Kernel ausführen kann und darauf kann der Runtime-Loader später aufbauen.

Zur Laufzeit erzeugt der Kernel keine Prozesse von selbst, zumindest fallen mir keine ein. Ich bitte um Gegenbeispiele. Mein Gedanke ist, dass neue Prozesse immer vom Userspace (Shell, GUI) aus erzeugt werden und das geschieht natürlich über die systemeigene libc/libsonstwas. Diese wickelt also das Starten von Prozessen ab und nutzt dazu den Runtime-Loader.

Zitat von: svenska
Der Bootvorgang kann (und sollte) statisch sein, der Bootloader übergibt an den Kernel eine Liste mit den Services, die er bereits vorbereitet hat und fertig.
Das funktioniert so nicht und soll es auch nicht machen. Mir geht es darum, das nirgends fest einprogrammiert ist, das der App-Server auch die Datei "app-server" sein muss (mal als Bsp.).
Dein Bootloader ist nicht konfigurierbar? Du lädst im Mikrokernel doch mehrere Dateien in den Speicher. Deren Name ist doch egal (der Bootloader kann auch 0x4000@0x94000000 im NAND-Flash laden). Der Kernel startet einfach alles, was da ist; die Services registrieren sich über das RPC selbst beim Kernel. (Ich möchte dazu jetzt kein Fass aufmachen.) Was interessiert es den Bootloader, ob der App-Server dabei ist? Fehlt am Ende des Tages der App-Server, gibts eine Kernel-Panic und gut ist.

Aufgabe des Benutzers ist es, die kritischen Dinge bereitzustellen, damit der Kernel überhaupt lebensfähig sein kann und dies dem Bootloader mitzuteilen (Konfiguration des Bootloaders, menu.lst im GRUB).

Sicherlich kann ich das alles in mein Bootskript packen und der Bootloader parst dieses Skript und gibt die Daten dann fertig und einfach lesbar an den Kernel weiter und dieser dann an den Storage-Server, der dann die verbleibenden Server startet.
Nein. Das Bootscript enthält eine Liste mit Dateien, die vom Bootloader an den Kernel übergeben werden. Mit diesen Dingen fängt der Kernel an zu leben. Alle weiteren Server werden aus dem Userspace gestartet, im Zweifelsfall von "init". Damit hat der Bootloader nichts mehr zu tun.

Zitat von: svenska
Jetzt habe ich aber einen Bluetooth-USB-Stick. Der USB-Stack stellt das fest, lädt den Treiber, startet den Bluetooth-Server und ist freut sich. Ich mag aber garkein Bluetooth nutzen... weil ich möchte heute nur Youtube gucken. Also ist der Server überflüssig, kostet RAM und Performance.
Dem muss ich erstmal entgegen halten, das man sowas manuel deaktivieren kann und ein anderes Bsp wäre, nur weil du den Sound gerade Stumm geschalten hast, wird nicht der Treiber beendet und alles was sonst noch zum Sound dazu gehört. Es ist also gang und gäbe das es so gehandhabt wird.
Also weil es eine Anwendung gibt, die potentiell den Wunsch haben könnte, einen Service zu benutzen, muss ich den Service dauerhaft bereitstellen? Das ist wie immer im ersten Gang fahren, ich könnte ja anfahren wollen...

Wenn ich stummschalte, dann senke ich die Lautstärke ab. Das ist etwas anderes, als den Treiber zu beenden. Wenn man den Linux-Ansatz nimmt, dann habe ich einen Soundserver (z.B. PulseAudio, ESound), der den Treiber benutzt. Beende ich diesen, weil ich im Büro der Mitarbeiter wegen kein lautes Audio haben darf, dann ist der letzte Nutzer des Audio-Servers weg und der Server würde somit automatisch beendet.

Andersrum: Was, wenn ich nur USB-Soundkarten habe? Der Treiber (USBAudioChipsatz-Server) startet beim Einstecken des Sticks. Der allgemeine Audio-Server, mit dem Anwendungen reden, startet aber noch nicht. Der startet erst, wenn Anwendungen wirklich Audio ausgeben wollen. Hat sich das erledigt, beendet er sich wieder. Und dabei setzt er vielleicht noch den USBAudioChipsatzServer in den Sleep-Modus, um Strom zu sparen.

Kann man drüber nachdenken, muss man nicht drüber nachdenken, aber man sollte es nicht komplett ignorieren.

Um es mal deutlicher zu machen, wenn du Bluetooth eh nicht nutzt, warum dann nicht einfach den Stick (wenn man schon einen hat) abmachen?
Weil er vielleicht im Notebook eingelötet ist? Außerdem sagt mir doch keiner, dass ich Bluetooth grundsätzlich nicht nutzen möchte. Ich möchte es nur jetzt nicht nutzen. Wenn ich die erste Bluetooth-Verbindung aufbauen möchte, startet der Server automatisch und wenn ich die letzte Bluetooth-Verbindung trenne (und die dazugehörigen Anwendungen schließe), wird der Server automatisch wieder beendet. Das ist im Übrigen eine Frage der Energieeffizienz: Was nicht verwendet wird, muss nicht laufen. Das spart CPU-Zyklen.

Gruß,
Svenska
« Letzte Änderung: 29. December 2010, 15:53 von Svenska »

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 29. December 2010, 16:16 »
Zitat von: svenska
Plugin-Systeme im Kernel. Pfui. Du sollst nicht den ultimativen Runtime-Loader haben, sondern du willst einen kleinen, speziellen Runtime-Loader für jedes zu unterstützende Format haben. Weniger Code, weniger Fehlerquellen, weniger Verschwendung.
Also nochmal, der Runtime-Loader läuft weder im Kernel noch ist er Bestandteil des Kernels (er liegt irgendwo dazwischen), er ist ein ganz "normales" Programm (welches keine Libraries bzw. shared-Libaries nutzen darf).

Das mit den Add-ons finde ich nicht weiter schlimm. Im Endeffekt registriert sich jedes Add-On im Runtime-Loader und dann wird bei jeder Datei, die Liste mit Add-Ons durchgegangen und wenn ein Add-On sagt, ok ich unterstütze dieses Format, erstellt es das Process-Image.

Ist im Endeffekt das gleiche wie für jedes Format nen Runtime-Loader.

Zitat von: svenska
Also eher ein Exec (Syscall) statt ein exec() (Funktion), das meinte ich mit dem Spezialfall. Der Runtime-Loader ist also ein Teil des Kernels, läuft im Userspace-Kontext, hat keinen Zugriff auf die libc-Funktionalität, dafür aber einen speziellen Syscall und ist allgemein etwas ganz besonderes. Stimmt das soweit?
Jein ;) Also der Runtime-Loader hat insofern zugriff auf irgendwelche Libraries, wenn er sie statisch mit in die Executeable packt und damit noch weit unter 4MB bleibt. Ich habe auch desweiteren eine Art exec() Funktion für alle Programme. Diese Funktion führt im Endeffekt mein createEmptyProcess() aus, welche dann den Runtime-Loader "startet". Der wiederum ruft dann den exec-Syscall auf und damit bekommt der aufrufende Prozess nen Rückgabewert und der neue Prozess wird gestartet.

Zitat von: svenska
Damit sind fork() und exec() nicht mehr das, was man landläufig darunter versteht.
Deswegen muss ich mir ja neue Namen einfallen lassen, mal sehen wie die Funktionen im Endeffekt heißen werden.

Zitat von: svenska
Was besser ist, weiß ich nicht. Ich halte meinen Ansatz für flexibler.
Wo siehst du deinen Ansatz flexibler und wo meinen unflexibel?

Zitat von: svenska
der Bootloader lädt den Kernel und die zwingend notwendigen Module (im kernel-nativen Binärformat, z.B. flat binary) in den RAM und übergibt eine Liste mit Zeigern auf diese Strukturen an den Kernel. Dann springt er in den Kernel, der fängt an zu laufen, initialisiert alles mögliche und ruft für jeden dieser Zeiger ein CreateProcessFromImage() auf. Dann laufen die bootkritischen Server und können verwendet werden. Wenn das geschehen ist, wird vom Kernel der eigentliche Userspace initialisiert, also konkret "init" gestartet. Das geht, weil ja alles, was dafür nötig ist, bereits läuft.
So viel anders ist meine Lebendgeburt auch nicht.

Bei mir wird es halt nur so sein, das der Kernel mit den schon vom Bootloader fertig erstellten Images einfach noch die nötigen Datenstrukturen anlegt und initialisert und dann einfach laufen lässt.

Zitat von: svenska
Der Runtime-Loader ist bei mir kein Teil des Kernels, sondern eine Anwendung wie jede andere auch, mit dem Unterschied, dass sie keinen Runtime-Loader benötigt, sondern bereits im kernel-nativen Binärformat vorliegen muss. Es gibt also ein Format, welches der Kernel ausführen kann und darauf kann der Runtime-Loader später aufbauen.
Und genau hier finde ich meinen Ansatz besser bzw. könnten die auch gleich sein/gemacht werden. Denn mein Runtime-Loader liegt auf der Platte als normale ELF-Datei vor, ist also ein Programm wie jedes andere auch (theoretisch, praktisch würde sich der Runtime-Loader sofort wieder beenden und nichts tun, wenn man ihn ausführt).

Dieses Format was der Kernel ausführen kann, wird entweder vom Bootloader oder vom Runtime-Loader erstellt.

Zitat von: svenska
Mein Gedanke ist, dass neue Prozesse immer vom Userspace (Shell, GUI) aus erzeugt werden und das geschieht natürlich über die systemeigene libc/libsonstwas. Diese wickelt also das Starten von Prozessen ab und nutzt dazu den Runtime-Loader.
Wird bei mir dann ungefähr genauso aussehen.

Zitat von: svenska
Dein Bootloader ist nicht konfigurierbar?
Doch.

In meinem Bootskript steht halt welche Datei der Kernel ist und welche Module noch geladen werden sollen und ich würde jetzt noch ein paar weitere Punkte (Runtime-Loader, Storage-Server, Device-Server, TUI-Server) hinzufügen.
Geladen werden nur die, die für den Bootvorgang auch benötigt werden, sprich der TUI-Server wird erst später vom Storage-Server geladen, aber ich möchte in diesem Bootskript schon alle wichtigen Server drin haben.

Zitat von: svenska
Was interessiert es den Bootloader, ob der App-Server dabei ist? Fehlt am Ende des Tages der App-Server, gibts eine Kernel-Panic und gut ist.
Weil es den Kernel einfacher und kompakter macht. Warum muss der Kernel nachgucken ob alles im Bootskript stand? Ich kann doch schon im Bootloader so viele Fehler wie möglich ausschließen ohne das System überhaupt geladen zu haben.

Zitat von: svenska
Alle weiteren Server werden aus dem Userspace gestartet, im Zweifelsfall von "init". Damit hat der Bootloader nichts mehr zu tun.
Und um nicht 2 Skripts zu haben oder 2 mal das selbe Skript parsen zu müssen (bzw. kann ich mir so dass ganze parsen außerhalb des Bootloaders vom Bootskript ganz sparen, weniger Fehler, weniger Code, weniger Speicherverbrauch, ...) mache ich das alles im Bootloader und gebe einfach nur eine Struktur an den Kernel weiter, wo alle Sachen die im Skript standen auch drin stehen, nur das diese Struktur wesentlich einfacher zu parsen ist.

Zitat von: svenska
Also weil es eine Anwendung gibt, die potentiell den Wunsch haben könnte, einen Service zu benutzen, muss ich den Service dauerhaft bereitstellen?
Um es mal auf die Spitze zu treiben, du erwartest doch auch das alle Treiber geladen werden, für die Geräte die vorhanden sind und nicht erst geguckt wird ob ein Gerät vorhanden ist, wenn es vllt irgendwann mal benutzt wird.

Was die Services betrifft, hängt das auch einfach vom Umfang ab, wenn ich dann 1min zum Starten des Services brauche, dann werden >90% der Nutzer sagen, lieber von Anfang an laufen lassen, als jedes Mal 1min zu warten bis ich es benutzen kann.

Zitat von: svenska
Das ist im Übrigen eine Frage der Energieeffizienz: Was nicht verwendet wird, muss nicht laufen. Das spart CPU-Zyklen.
Da hast du jetzt einen Denkfehler drin. Das einzige was du sparen kannst ist RAM! Denn wenn der Server nicht benutzt wird, läuft er auch nicht, er wartet das eine Anfrage an ihn gestellt wird und das kostet keine CPU-Zyklen.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 29. December 2010, 17:20 »
Also nochmal, der Runtime-Loader läuft weder im Kernel noch ist er Bestandteil des Kernels (er liegt irgendwo dazwischen), er ist ein ganz "normales" Programm (welches keine Libraries bzw. shared-Libaries nutzen darf).
Ist also ein BLOB im Kernel.

Also der Runtime-Loader hat insofern zugriff auf irgendwelche Libraries, wenn er sie statisch mit in die Executeable packt und damit noch weit unter 4MB bleibt.
Also hat er keinen Zugriff auf Libraries. Wenn ich die Bibliothek nicht besuchen darf, habe ich keinen Zugriff darauf, auch wenn ich alle Bücher der Bibliothek selbst besitze und mitnehmen darf. ;-)

Ich habe auch desweiteren eine Art exec() Funktion für alle Programme. Diese Funktion führt im Endeffekt mein createEmptyProcess() aus, welche dann den Runtime-Loader "startet". Der wiederum ruft dann den exec-Syscall auf und damit bekommt der aufrufende Prozess nen Rückgabewert und der neue Prozess wird gestartet.
Also hast du in der libc ein exec(), welches den Runtime-Loader in einen neuen Prozess mappt und startet, und du hast einen Syscall Exec, der den Runtime-Loader aus einem Prozess entfernt und ihn resettet (diesmal an der realen Position).

Zitat von: svenska
Was besser ist, weiß ich nicht. Ich halte meinen Ansatz für flexibler.
Wo siehst du deinen Ansatz flexibler und wo meinen unflexibel?
Es gibt nur einen Runtime-Loader, der ist Teil des Kernels (wenn er auch nicht als solcher ausgeführt wird). Damit ist er prinzipiell nicht ersetzbar. Du hast einen speziellen Exec-Syscall für den Runtime-Loader, den auch nur dieser nutzen kann, und es gibt keine andere Möglichkeit, einen Prozess zu erzeugen.

Dein Kernel ist nicht in der Lage, Prozesse zu starten, ohne ein Dateisystem und die dazugehörigen Services geladen zu haben. Außerdem kannst du keine Prozesse starten, wenn der Runtime-Loader defekt ist oder ersetzt werden muss. Ich find das alles nicht so pralle, aber funktionieren dürfte es.

Zitat von: svenska
Der Runtime-Loader ist bei mir kein Teil des Kernels, sondern eine Anwendung wie jede andere auch, mit dem Unterschied, dass sie keinen Runtime-Loader benötigt, sondern bereits im kernel-nativen Binärformat vorliegen muss. Es gibt also ein Format, welches der Kernel ausführen kann und darauf kann der Runtime-Loader später aufbauen.
Und genau hier finde ich meinen Ansatz besser bzw. könnten die auch gleich sein/gemacht werden.
Das hatte ich erwähnt, als ich meinte, der Kernel könnte das ELF auch direkt parsen und alle anderen Dinge laufen durch den Runtime-Loader.

Denn mein Runtime-Loader liegt auf der Platte als normale ELF-Datei vor, ist also ein Programm wie jedes andere auch (theoretisch, praktisch würde sich der Runtime-Loader sofort wieder beenden und nichts tun, wenn man ihn ausführt).
Wer parst den Runtime-Loader, wenn der eine ELF-Datei ist? Tut es der Kernel selbst, brauchst du keinen Runtime-Loader mehr für ELF-Dateien und andere Formate kannst du komplett im Userspace implementieren.

Der Linux-Runtime-Loader gehört zur libc(!) und wenn du ihn startest, kannst du damit ELF-Dateien ausführen. Und zwar so, als ob du sie direkt ausgeführt hättest.

In meinem Bootskript steht halt welche Datei der Kernel ist und welche Module noch geladen werden sollen und ich würde jetzt noch ein paar weitere Punkte (Runtime-Loader, Storage-Server, Device-Server, TUI-Server) hinzufügen.
Geladen werden nur die, die für den Bootvorgang auch benötigt werden, sprich der TUI-Server wird erst später vom Storage-Server geladen, aber ich möchte in diesem Bootskript schon alle wichtigen Server drin haben.
Halte ich für den falschen Ansatz. Du möchtest dem Bootloader nicht alle Server mitgeben. Möchtest du nicht. Ist einfach falsch. Du möchtest im Betrieb neue Server definieren können. Server sind bei dir ganz normale Anwendungen mit ein paar Eigenheiten. Warum die im Bootloader statisch definieren?

Warum (und woran) sollte der Bootloader entscheiden, welche Server geladen werden müssen und welche nicht? Können die Server das nicht selbst entscheiden? Was passiert, wenn die Autodetection fehlschlägt?

Warum sollte der Storage-Server den TUI-Server laden? Die haben nichts miteinander zu tun. Der TUI-Server sollte vom Kernel (oder von mir aus dem Servermanagement-Server) geladen werden, wenn eine Konsole daran gebunden werden soll.

Zitat von: svenska
Was interessiert es den Bootloader, ob der App-Server dabei ist? Fehlt am Ende des Tages der App-Server, gibts eine Kernel-Panic und gut ist.
Weil es den Kernel einfacher und kompakter macht. Warum muss der Kernel nachgucken ob alles im Bootskript stand? Ich kann doch schon im Bootloader so viele Fehler wie möglich ausschließen ohne das System überhaupt geladen zu haben.
Der Bootloader soll dem Kernel nicht vorschreiben, was er wie zu tun hat. Der Bootloader soll den Kernel laden und starten und mehr nicht (zumindest ist das meine Definition).

Gedankenspiel: Jeder Server, der gestartet wird, registriert sich beim Kernel. Der App-Server (egal, welcher es ist) registriert sich als "APP". Wenn der Kernel jetzt darauf zugreifen möchte, dann ruft er über IPC nach einem Server mit dem Namen "APP". Ist keiner da: Panic. Damit kannst du im Betrieb den App-Server ändern (oder neustarten), ohne den gesamten Kernel neustarten zu müssen.

Bei dir muss man also, um einen Server zu registrieren, das System neustarten und zerschießt sich unter Umständen die Bootkonfiguration.

Ich halte das für nicht sinnvoll, aber diskutiere nicht weiter. Mach es, wie du es für richtig hältst.

Um es mal auf die Spitze zu treiben, du erwartest doch auch das alle Treiber geladen werden, für die Geräte die vorhanden sind und nicht erst geguckt wird ob ein Gerät vorhanden ist, wenn es vllt irgendwann mal benutzt wird.
Erstmal gibt es zwei Treiber. Einmal den hardwarespezifischen Treiber, der die Hardware ansteuert, und einmal einen allgemeinen Treiber, der mit den Anwendungen und dem hardwarespezifischem Treiber redet.

Konkret ein AC97-Treiber und ein Audio-Server. Letzterer stellt für Anwendungen eine definierte API für Mixer, Kanäle und so weiter zur Verfügung, kann Mischen und Remixen und was-auch-immer. Das kann man als Userspace-Programm (wie PulseAudio) lösen, halte ich aber nicht für sinnvoll, weil es an sich eine Kernfunktionalität von Betriebssystemen ist. Stecke ich jetzt meine USB-AC97-Soundkarte(*) dort an, wird der AC97-Treiber geladen. Fertig.

Der allgemeine Audio-Server wird nicht gestartet. Wenn eine Anwendung Audio ausgeben möchte, macht sie eine Anfrage (z.B. open /dev/audio). Diese Anfrage startet den Audio-Server und er schaut, welche Treiber vorhanden sind und gibt den Ton aus. Anschließend wird er wieder beendet. Die Treiber halten die Hardware ununterbrochen im Stromspar-Modus, solange sie niemand benutzt.

Gut, Audio ist da nicht unbedingt das beste Beispiel, weil man es ständig benutzt, aber für Bluetooth, UMTS oder WLAN ist das schon wesentlich sinnvoller. Außerdem verkürzt das die Bootzeit, weil Server erst gestartet werden, wenn die entsprechenden Anwendungen laufen.

(*) Ja ich weiß, sowas gibt es nicht.

Was die Services betrifft, hängt das auch einfach vom Umfang ab, wenn ich dann 1min zum Starten des Services brauche, dann werden >90% der Nutzer sagen, lieber von Anfang an laufen lassen, als jedes Mal 1min zu warten bis ich es benutzen kann.
Meine Lösung schließt ja nicht aus, dass es persistente Services geben kann. Aber sie macht es möglich, dass dem nicht so ist. Und man könnte es von externen Ereignissen abhängig machen (im Akkubetrieb sind alle Services "aus", im Netzbetrieb sind sie "an").

Da hast du jetzt einen Denkfehler drin. Das einzige was du sparen kannst ist RAM! Denn wenn der Server nicht benutzt wird, läuft er auch nicht, er wartet das eine Anfrage an ihn gestellt wird und das kostet keine CPU-Zyklen.
Er muss aber gestartet und verwaltet werden. Und der Start eines Servers kann recht teuer sein.

Aber wir driften ab, ich denke, wir werden uns da wieder nicht einigen können, daher lass ich es.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 29. December 2010, 17:50 »
Zitat von: svenska
Ist also ein BLOB im Kernel.
Naja, halt nicht so richtig. Sieh es als eine Library die immer benutzt wird, ob du es willst oder nicht.

Zitat von: svenska
Damit ist er prinzipiell nicht ersetzbar. Du hast einen speziellen Exec-Syscall für den Runtime-Loader, den auch nur dieser nutzen kann, und es gibt keine andere Möglichkeit, einen Prozess zu erzeugen.
Ersetzbar ist er in dem Sinne, das du im Bootskript einen anderen Runtime-Loader angeben kannst, aber dieser muss das selbe Interface unterstützen/nutzen.

Warum sollte es eine andere Möglichkeit geben einen Prozess zu erzeugen bzw. was unterscheidet meinen Runtime-Loader in dem Fall von dem von Linux (vorallem hat Linux eigentlich 2, einem im Kernel und einen in der libc)?

Zitat von: svenska
Dein Kernel ist nicht in der Lage, Prozesse zu starten, ohne ein Dateisystem und die dazugehörigen Services geladen zu haben. Außerdem kannst du keine Prozesse starten, wenn der Runtime-Loader defekt ist oder ersetzt werden muss.
Sagen wir mal jein. Also ohne Dateisysteme kann ich schon einen Prozess starten (muss ich auch, weil ich einfach alle nötigen Dateien an den Storage-Server übergeben will und dieser die schon geladenen Dateien nutzen kann ohne das er etwas von einem Speichermedium laden muss) und wenn der Runtime-Loader unter anderen OSs defekt ist, können auch keine Prozesse mehr gestartet werden (Vergleich: wenn dein Auto kaputt ist, kannst du solange damit nicht fahren bis es wieder in Ordnung ist).

Zitat von: svenska
Das hatte ich erwähnt, als ich meinte, der Kernel könnte das ELF auch direkt parsen und alle anderen Dinge laufen durch den Runtime-Loader.
Da frage ich mich dann was sind diese anderen Dinge? Denn was außer der ELF Datei zu parsen (und das Process-Image erzeugen) macht denn der Runtime-Loader noch so?

Zitat von: svenska
Wer parst den Runtime-Loader, wenn der eine ELF-Datei ist?
Habe ich doch geschrieben, das macht mein Bootloader (wo praktisch auch ein Runtime-Loader drin ist).

Zitat von: svenska
Halte ich für den falschen Ansatz. Du möchtest dem Bootloader nicht alle Server mitgeben. Möchtest du nicht. Ist einfach falsch. Du möchtest im Betrieb neue Server definieren können. Server sind bei dir ganz normale Anwendungen mit ein paar Eigenheiten. Warum die im Bootloader statisch definieren?
Richtig. Aber ich will die wichtigsten also nötigen Server definieren können. Also sowas wie Storage-Server und Device-Server.

Anders gefragt, wenn du die nicht irgendwo definierst, wer startet die dann und wenn dieser Jemand sie startet wurden sie ja doch irgendwo definiert. Du kannst ja schlecht den Benutzer danach fragen, das würde ja die Server schon vorraussetzen.

Zitat von: svenska
Warum sollte der Storage-Server den TUI-Server laden? Die haben nichts miteinander zu tun. Der TUI-Server sollte vom Kernel (oder von mir aus dem Servermanagement-Server) geladen werden, wenn eine Konsole daran gebunden werden soll.
Erstmal richtig, aber ich gehe halt davon aus, das z.B. entweder der TUI-Server oder der GUI-Server geladen wird und das muss ja irgendwo in irgendeinem Skript stehen und ich will nicht tausend Skripts haben.

Zitat von: svenska
Damit kannst du im Betrieb den App-Server ändern (oder neustarten), ohne den gesamten Kernel neustarten zu müssen.
Das kann ich auch so. Das schöne an einem MikroKernel (wenn er denn ein "richtiger" ist) ist doch das dir das als Kernel alles scheiß egal sein kann. Du machst als Kernel nichts weiter als IPC, VMM und Prozessmanagement. Wenn ein IPC Ziel nicht da ist, gibst du halt nen Fehler zurück, aber zu einem Kernel-Panic darf das nicht führen.

Der Kernel soll doch bei mir so wenig wie möglich darüber wissen. Also eigentlich gar nichts.

Zitat von: svenska
Bei dir muss man also, um einen Server zu registrieren, das System neustarten und zerschießt sich unter Umständen die Bootkonfiguration.
Jein. Ersten hatten wir es doch schonmal das einen Server zu resetten eher weniger schön ist bzw. bei einigen Sachen wahrscheinlich unmöglich und daher sowieso einen System Neustart erfordert und zweitens rede ich ja nur von den wirklich nötigen Servern. Mit allen anderen Servern habe ich nichts am Hut.

Zitat von: svenska
Der allgemeine Audio-Server wird nicht gestartet. Wenn eine Anwendung Audio ausgeben möchte, macht sie eine Anfrage (z.B. open /dev/audio). Diese Anfrage startet den Audio-Server und er schaut, welche Treiber vorhanden sind und gibt den Ton aus. Anschließend wird er wieder beendet. Die Treiber halten die Hardware ununterbrochen im Stromspar-Modus, solange sie niemand benutzt.
Also erstmal sowas wie ne USB-Soundkarte gibt es ;)

Was du bei der ganzen Sache vergisst (bzw. denke ich das ist deiner Linux Denkart geschuldet ;) ), wenn du einen Service nutzen willst muss dieser auch gestartet sein, sprich wenn du ein "open /dev/audio" machen willst, dann muss das auch vorhanden sein, das ist es aber nur wenn der Server auch gestartet ist, ansonsten gibt es das nicht.
Das kann man jetzt so lösen das jedes Programm welches einen bestimmten Service nutzen will, erstmal diesen starten muss und ich denke da sind wir uns beide einig, dass das weniger sinnvoll ist.
Ansonsten muss zumindest irgendeine Art Ansprechpartner vorhanden sein, der die Anfrage entgegen nimmt und den Server startet und dann die Anfrage weiterleitet und das wiederrum ist auch nicht wirklich sinnvoll und von Performance will ich erst gar nicht anfangen ;)

Zitat von: svenska
Er muss aber gestartet und verwaltet werden. Und der Start eines Servers kann recht teuer sein.
Genau das ist/war doch mein Punkt. Da der Start recht teuer sein kann, werden >90% der Nutzen sagen, das der Server lieber von Anfang an "laufen" soll als das man ihn jedes Mal startet wenn er benötigt wird.

Edit::

Habe ich doch glatt was vergessen.

Dynamisch ladbare Module/Add-Ons. Wie würde man sowas lösen mit nem Runtime-Loader der im UserSpace läuft?

Ich dachte da jetzt daran, das mein Runtime-Loader einfach immer im Adressraum bleibt und einfach als Systempages gekennzeichnet wird und wenn das Programm ein Modul laden will (dlopen() usw.) dann wird einfach der Runtime-Loader wieder "freigegeben" (also als Userpages markiert) und er führt sein dlopen() usw. aus.

Aus Sicherheitsgründen würde ich solange aber alle Threads des Prozesses anhalten, damit der Prozess nicht in den Daten des Runtime-Loader rumspielen kann.

Das würde auch heißen das ich einen weiteren Syscall (bzw. weitere Syscalls) bräuchte um den Runtime-Loader dann aufzurufen.

Noch eine Sache zu den Modulen/Add-Ons.

Sollte man für die Symboltabellen z.B. einen Patricia-Tree nehmen oder einfach nur ein großen Array, was man dann durchgeht? Oder anders gefragt ist Performance beim Symbol-Suchen wichtig und/oder erwünscht (würde ja mit etwas mehr Speicherverbrauch einhergehen)?
« Letzte Änderung: 29. December 2010, 17:56 von FlashBurn »

 

Einloggen