Eigenes OS?


  • Mod

    Ich habe hier ein Kapitel zum Thema "Aufruf einer C-Funktion" geschrieben:
    http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId342494

    Vielleicht könnte mal jemand drüber schauen, ob Fehler/Unklarheiten enthalten sind. 🙂



  • Erhard Henkes schrieb:

    Vielleicht kann man mal jemand drüber schauen, ob Fehler/Unklarheiten enthalten sind.

    Vielleicht wäre es noch wichtig zu erwähnen, dass es bestimmte Aufrufkonventionen gibt, wie cdecl, stdcall, pascal und fastcall (gibt es noch welche?) und dass damit geregelt wird, wie die Parameter auf dem Stack abgelegt werden, also in welcher Reihenfolge, und wer sie aufräumen muss (caller oder callee)...



  • Erhard Henkes schrieb:

    Vielleicht kann man mal jemand drüber schauen, ob Fehler/Unklarheiten enthalten sind

    du solltest noch dazuschreiben, auf welchen compiler du sich beziehst (GCC version 4.x nehme ich an) ein anderer compiler könnte vieles anders machen. ach ja, die benamung der funktion 'exit_task()' finde ich nicht sonderlich gelungen. dem namen nach könnte man vermuten, dass die funktion die task beendet, so dass sie nicht wieder dran kommt (vergl. exit() in standard C). womöglich wäre next_task(), switch_now(), yield(), oder sowas eine alternative?
    🙂


  • Mod

    'exit_task()' finde ich nicht sonderlich gelungen

    Ja, das ist eindeutig falsch. "switch_context()" klingt gut, hat vor allem keinen Bezug zu irgendwelchen POSIX-Begriffen. Normalerweise machen Prozesse so was nicht "freiwillig", nur Threads kennen yield(). Da jede task ihren eigenen Adressraum hat, handelt es sich eindeutig um Prozesse. Das Ganze ist noch relativ experimentell und rudimentär. Ich denke gerade über eine vernünftige Weiterentwicklung nach, aber vielleicht ist es an dieser Stelle dafür noch zu früh, denn Scheduling und Threading ist ein zwar wichtiges aber leider ebenso komplexes Thema mit vielen Möglichkeiten. Das Schlimme dabei ist, dass es keine optimale Lösung gibt. Selbst Linux hat hier im Laufe seiner Entwicklung einen vollen Salto hingelegt. Das Thema Deadlock und entsprechende Deadlock-Algos zur Vermeidung oder Vorbeugung lauert bei Multithreading ebenso bereits um der Ecke.

    GCC version 4.x nehme ich an

    Das ist ein ganz übles Thema, da ich wegen des eigenen Bootloaders und dieses blöden aout-Formats (16/32-Bit-Code gemischt in kernel.asm) beim Linker immer noch an die Version gcc 3.1, ld 2.13 (in DJGPP) gebunden bin.
    Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB. Dies werde ich in Teil 3 wohl auch machen müssen. Dann kommen allerdings die ganzen GRUB-Themen (magic numbers, ...) hinzu. Jemand, der auf längere Sicht OS-Development betreibt, muss sich allerdings diese Tool-Basis schaffen, um fremde OS kompilieren/testen zu können.



  • Bin in dem neuen Artikel nur über eine eher unwesentliche Kleinigkeit gestolpert:

    Es gibt übrigens eine Konvention, dass der callee bevorzugt die Register EAX (z.B. zum Rechnen, Transferieren und als Rückgabewert), ECX (z.B. als Schleifenzähler) und EDX (in obigem Bsp. z.B. zum Rechnen) verwendet.

    Das ist weniger eine Konvention sondern mehr eine Vorgabe des Prozessors. Manche Instruktionen arbeiten nur mit bestimmten Registern. "LOOP" als Beispiel funktioniert nur mit ECX als Zähler.



  • dat is ne konvention. du musst eax ja ned als rückgaberegister nehmen oder ecx für die l00ps etc. das is schon richtig so wie es da steht.


  • Mod

    http://de.wikipedia.org/wiki/Aufrufkonvention#stdcall
    http://www.agner.org/optimize/calling_conventions.pdf (Kap. 6, Register Usage)

    Ich habe diese beiden Links in den Artikel übernommen. Danke für die Durchsicht. 🙂



  • Eine Frage zum Artikel hätte ich da noch:

    Warum ist in dem Beispiel der Wert vom ESP so extrem groß (0x40205xxx), aber der vom EIP (0x00009Cxx) so niedrig ?



  • warum nicht? was hat der 1 mit dem anderen zu tun?



  • Speichermanagement ? Virtualisierung ? Noch mehr ? 🙂



  • häh? ich kenn jetzt erhards OS nicht aber wenns generell gemeint ist versteh ich dein posting nicht. er kann doch die dinge mappen wo er will



  • Erhard Henkes schrieb:

    Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.

    das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.

    Erhard Henkes schrieb:

    Dies werde ich in Teil 3 wohl auch machen müssen. Dann kommen allerdings die ganzen GRUB-Themen (magic numbers, ...) hinzu. Jemand, der auf längere Sicht OS-Development betreibt, muss sich allerdings diese Tool-Basis schaffen, um fremde OS kompilieren/testen zu können.

    es geht bestimmt mit jedem compiler z.b. msvc express edition, watcom, oder dem LCC. natürlich nicht von allein, etwas basteln muss man schon. vorteil: du wärst diese ätzende AT&T-syntax endlich los.
    🙂



  • Erhard Henkes schrieb:

    Das ist ein ganz übles Thema, da ich wegen des eigenen Bootloaders und dieses blöden aout-Formats (16/32-Bit-Code gemischt in kernel.asm) beim Linker immer noch an die Version gcc 3.1, ld 2.13 (in DJGPP) gebunden bin.
    Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.

    Wieso muss es eigentlich im aout Format sein? Ich habe vor kurzem das OS + Bootloader unter Ubuntu 9.04 mit recht aktuellem gcc 4.3.3, ld 2.19.1 und nasm 2.05.1 compiliert. Dabei habe ich unter anderem das aout in elf geändert, und es lief ohne Probleme. Das aktuelle DJGPP hat ähnliche Versionen, daher sollte das doch eigentlich auch gehen, oder gibt es da noch einen anderen Unterschied?

    Im NASM Manual steht auch, wenn ich das richtig verstehe, dass 16 Bit im ELF Format möglich ist, da NASM die nötigen Informationen übergibt, damit ld weiß was damit zu tun ist: http://www.nasm.us/doc/nasmdoc7.html (7.7.6)

    Unter http://pastebin.com/m3b4d8bcd ist mein Makefile. Das Basis Makefile war die aktuelle Testversion am 20. Mai. Ansonsten habe ich nur noch bei sämtlichen Variablen und Funktionen, die von c benutzt wurden, aber in asm definiert waren, den Unterstrich entfernen müssen. Anscheinend ist es nicht mehr Standard, dass gcc den Unterstrich benutzt und ich habe auch kein Compilerflag dafür gefunden das wieder zu aktivieren.


  • Mod

    @Tobiking2: danke für den Hinweis. Da muss ich doch noch mal probieren.
    Ich kopiere das makefile von 'Tobiking2' mal hier rein:

    all:
          nasm      -f bin file_data.asm -o file_data.dat
          nasm -O32 -f bin boot.asm -o boot.bin            
          nasm -O32 -f elf kernel.asm -o kernel.o
          nasm -O32 -f elf isr.asm -o isr.o
          nasm -O32 -f elf process.asm -o process.o
          nasm -O32 -f elf flush.asm -o flush.o
    
          gcc  -Wall -O -fno-stack-protector -fno-builtin -fno-stack-protector -fno-builtin -nostdlib  -fno-builtin -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -c ckernel.c -o ckernel.o    
    
    usw. 
    
          ld -T kernel.ld kernel.o isr.o ckernel.o video.o flush.o gdt.o idt.o isrs.o irq.o util.o math.o timer.o keyboard.o process.o ordered_array.o paging.o kheap.o descriptor_tables.o task.o  -o ckernel.bin -Map kernel.map
    
          cat boot.bin > MyOS
          cat ckernel.bin >> MyOS
          mv MyOS MyOS.bin
    

    Wenn ich das mit meinem DJGPP (gcc 3.1) ausprobiere erhalte ich vom Linker (ld 2.13) folgende Fehlermeldung:

    kernel.o: file not recognized: File format not recognized

    Im Linker-Skript habe ich übrigens 'rodata' ergänzt, weil einige Compiler das brauchen:

    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    phys = 0x00008000;
    SECTIONS
    {
      .text phys  : {
        *(.text)
      }
      .data  : {
        *(.data)
      } 	
      .rodata  : {
        *(.rodata)
      }
      .bss  :  { 					
        *(.bss)
      }
    }
    

  • Mod

    Warum ist in dem Beispiel der Wert vom ESP so extrem groß (0x40205xxx), aber der vom EIP (0x00009Cxx) so niedrig?

    Die Kernel-Stacks für die erzeugten Prozesse wurden jeweils auf dem Heap angelegt, und der beginnt bei mir ab 0x40000000.

    kheap.h:

    #define KHEAP_START         0x40000000 // 1GB
    #define KHEAP_INITIAL_SIZE  0x00200000
    #define KHEAP_MAX           0x4FFFF000
    

    Das Programm selbst ist unten bei 0x00008000 (siehe phys = 0x00008000 im Linker-Skript oben) abgelegt, siehe auch Datei 'kernel.map':

    Linker script and memory map
    
                    0x00008000                phys = 0x8000
    
    .text           0x00008000     0x55d0
     *(.text)
     .text          0x00008000       0xd0 kernel.o
                    0x00008000                RealMode
     .text          0x000080d0      0x1d8 isr.o
                    0x00008178                _isr20
    //...
    
     .text          0x000082b0      0x3c0 ckernel.o
                    0x000082b0                _f2
                    0x000082f0                _f3
                    0x00008430                _main
    //...
    

  • Mod

    das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.

    Interessante Bemerkung. Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema. Alternativ kann man den bootloader aufmotzen. Mal sehen. 🙂



  • Erhard Henkes schrieb:

    Wenn ich das mit meinem DJGPP (gcc 3.1) ausprobiere erhalte ich vom Linker (ld 2.13) folgende Fehlermeldung:

    kernel.o: file not recognized: File format not recognized

    Habs grad mal mit dem aktuellen djgpp probiert und das gleiche Problem. DJGPP benutzt nur COFF und unterstützt ELF gar nicht (http://www.delorie.com/djgpp/v2faq/faq22_22.html). Bei coff ist darüber hinaus kein 16 Bit möglich.

    Ich bin beim Suchen auf http://exec.h1.ru/docs/os-devel-faq/os-faq-elf.html gestoßen, einer Anleitung um binutils mit elf support zu compilieren.



  • Erhard Henkes schrieb:

    Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema.

    was willste mit 16 bit? dachte dein OS schaltet beim start schon in den (flat memory model) 32bit protected mode und bleibt da auch. oder hab' ich was übersehen?
    🙂


  • Mod

    was willste mit 16 bit?

    Schau mal in kernel.asm ... und hier auf das Bild:
    http://www.henkessoft.de/OS_Dev/Bilder/make_process.PNG

    Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm. 🙄

    Vielleicht kann man das umbauen (ich sehe gerade, dass ich org 0x8000 schon auskommentiert habe, das geht offenbar bereits über das Linker-Skript), damit man coff-Format(?) verwenden kann. Vielleicht kann man auch in zwei Hälften (16 Bit u. 32 Bit Teil trennen, den ersten könnte man als bin resultieren lassen, den zweiten als o(bjekt)-Datei wegen des 'call _main')

    Die wating-loop am Ende des 16-Bit-Teils sieht auch nicht sauber aus (war vorher im 32-Bit-Teil, wurde aber aus 16 Bit aufgerufen.



  • Erhard Henkes schrieb:

    Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm.

    aber das ist doch ok so, oder? pc-kisten starten nun mal im real-mode, dein asm-code schaltet in den protected mode und ruft die 'main' des kernels auf. von nun an läuft alles im protected mode ab, egal ob C oder assembler. selbst die portabelsten betriebssysteme haben ein paar plattformabhängige assemblermodule. ich weiss garnicht, was dich daran stört, oder ich hab' heute ne besonders lange leitung.
    🙂


Anmelden zum Antworten