Eigenes OS?


  • Mod

    gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert

    Das solltest Du nicht machen, sondern diese nach C:\djgpp installieren. Pfad und Env korrekt setzen wie beschrieben. Nimm genau den DJGPP aus dem im Tutorial angegebenen Link wegen dem aout-Format. Falls dann etwas nicht klappt, bitte erst Fehlermeldung lesen und probieren. Wenn Du nicht mehr weiter kommst, genaue Meldung posten. Hast Du schon mit Assembler und C-Compiler gearbeitet?



  • upps
    stimmt ja *boing*
    ich probiers demnächst aus 😃


  • Mod

    Das Thema Heap ist nun ebenfalls im Tutorial grundlegend verarbeitet. Wer sich als Leser tiefer hinein bohren möchte, kann es mit den Links machen, für PrettyOS ist nur die Anwendung wichtig.


  • Mod

    Ich wollte das Ende des Kernelbereichs bestimmen. Dazu veränderte ich das Linker-Skript wie folgt:

    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    phys = 0x00008000;
    SECTIONS
    {
      .text phys  : {
        *(.text)
      }
      .data  : {
        *(.data)
      }
      .bss : {
        *(.bss)   
      }
       _endkernel = .;   
    }
    
    //os.c
    extern ULONG* endkernel;
    ULONG get_end_kernel(){ return *endkernel; }
    
    //ckernel.c
    int main()
    {
        k_clear_screen();
        printformat("end of kernel %x\n", get_end_kernel());
        //...
    }
    

    liefert:

    end of kernel F000FF53h

    Nach meinen Berechnungen sollte natürlich ein deutlich niedrigerer Wert heraus kommen. 0x8000 (start) + 0x3540 (Hexeditor MyOS.bin) - 0x0200 (bootloader sector) = 0xB340
    Auch wenn ich _endkernel = .; hinter data setze, kommt das Gleiche heraus. Was mache ich verkehrt? 😕



  • so wie ich das sehe, deklarierst du mit

    extern ULONG* endkernel;
    

    die Variable als Zeiger; das ist noch korrekt, aber du willst da die Adresse, und nicht den Inhalt an dieser Stelle, also

    ULONG get_end_kernel(){ return endkernel; }
    

    (beachte: ohne stern!) somit kannst du den Pointer auch vom Typ void* oder char* machen, da du ja die Daten, die am Ende deines Kernels sind nicht auslesen möchtest, oder vl. schon^^


  • Mod

    Klar Adresse der Variablen, nicht Inhalt. Habe da beides durchmischt.
    So klappt es:

    extern ULONG endkernel;
    ULONG get_end_kernel()
    {
        return (ULONG) &endkernel;
    }
    

    und _endkernel vor bss (Block Started by Symbol)

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

    ergeben:

    end of kernel 0000B320h

    Das passt gut zu meiner Schätzung.

    Hinter dem .bss Block ist der Wert höher:

    end of kernel 0000BC00h

    bss: http://en.wikipedia.org/wiki/Block_Started_by_Symbol

    Was ist das wirkliche Ende des Kernels? B320h (ohne bss) oder BC00h (incl. bss)?
    Ich denke incl. bss ist korrekt.



  • bei der ckernel.c, wozu ist der string gut?
    der wird doch nirgendwo ausgegeben oder?
    bei mir werdeb nur die strings aus dem bootloader ausgegeben.
    mfg
    lukas
    ps:
    es wäre cool, wenn du dein tutorial mit einem dateisystem erweitern würdest.


  • Mod

    bei der ckernel.c, wozu ist der string gut?

    Den PrettyOS ...? Ja, den kannst Du streichen, das erfolgt auch in den weiteren Versionen, da man Version und Datum nur an einer Stelle halten sollte. 😉

    es wäre cool, wenn du dein Tutorial mit einem Dateisystem erweitern würdest.

    Eigenes Dateisystem, VFS, RAM Disk etc. kommt noch. Zur Zeit hänge ich immer noch am Taskswitching fest.



  • gut, ich hab mich schon gewundert.

    task switching?
    also wie fenster ohne fenster?


  • Mod

    task switching?

    Multitasking, also z.B. Timer-gesteuertes Umschalten zwischen Prozessen mit allem Drum und Dran. Schwieriges Thema, zumindest für mich.



  • Erhard Henkes schrieb:

    Multitasking, also z.B. Timer-gesteuertes Umschalten zwischen Prozessen mit allem Drum und Dran. Schwieriges Thema, zumindest für mich.

    das grundprinzip ist doch klar, oder? task wird vom timer-interrupt unterbrochen, der speichert alle registerinhalte der task und beim verlassen setzt er registerinhalte für die nächste task. schwierig sind dabei nur irgendwelche abgefahrenen scheduling-algorithmen, aber darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.
    🙂


  • Mod

    darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.

    Der Mechanismus funktioniert bereits, über mit vier Tasks.

    das grundprinzip ist doch klar, oder?

    Ja, mir macht die Abstimmung zwischen TSS entry (nur eine), CR3 (Physikalische Adresse der Page Directory des jeweiligen virtuellen Adressraums), Struktur task_t (für jede Task eine) und eigener Kernel_Stack pro Task noch etwas Probleme. Da stecke ich noch etwas fest. Ich möchte den Task Switch prinzipiell auch außerhalb eines Interrupts durchführen können, um ihn zu studieren. Denn im timer_handler macht es schnell bumm, dann ist es schwer zu analysieren, was eigentlich los ist (GPF, OpcodeF, DebugF, Reset, doing nothing, ...). Erst mit vollständigen log-Funktionen - passend zu den Strukturen task_t und tss_entry_t erkennt man, was im Hintergrund abgeht:

    void task_log(task_t* t)
    {
        printformat("id: %d ", t->id);               // Process ID
        printformat("ebp: %x ",t->ebp);              // Base pointer
        printformat("esp: %x ",t->esp);              // Stack pointer
        printformat("eip: %x ",t->eip);              // Instruction pointer
        printformat("PD: %x ", t->page_directory);   // Page directory.
        printformat("k_stack: %x ",t->kernel_stack); // Kernel stack location.
        printformat("next: %x\n",  t->next);         // The next task in a linked list.
    }
    
    void TSS_log(tss_entry_t* tss)
    {
        printformat("prev_tss: %x ", tss->prev_tss);
        printformat("esp0: %x ", tss->esp0);
        printformat("ss0: %x ", tss->ss0);
        printformat("esp1: %x ", tss->esp1);
        printformat("ss1: %x ", tss->ss1);
        printformat("esp2: %x ", tss->esp2);
        printformat("ss2: %x ", tss->ss2);
        printformat("cr3: %x ", tss->cr3);
        printformat("eip: %x ", tss->eip);
        printformat("eflags: %x ", tss->eflags);
        printformat("eax: %x ", tss->eax);
        printformat("ecx: %x ", tss->ecx);
        printformat("edx: %x ", tss->edx);
        printformat("ebx: %x ", tss->ebx);
        printformat("esp: %x ", tss->esp);
        printformat("ebp: %x ", tss->ebp);
        printformat("esi: %x ", tss->esi);
        printformat("edi: %x ", tss->edi);
        printformat("es: %x ", tss->es);
        printformat("cs: %x ", tss->cs);
        printformat("ss: %x ", tss->ss);
        printformat("ds: %x ", tss->ds);
        printformat("fs: %x ", tss->fs);
        printformat("gs: %x ", tss->gs);
        printformat("ldt: %x ", tss->ldt);
        printformat("trap: %x ", tss->trap); //only 0 or 1
        printformat("iomap_base: %x ", tss->iomap_base);
    }
    

    Siehe Intel Manual 3A, "Figure 6-2. 32-Bit Task-State Segment (TSS)"

    Die Struktur task_t muss sicher noch erweitert werden. ich möchte diese für das Tutorial aber auch nicht überfrachten, damit man den Überblick nicht verliert.


  • Mod

    Da es im timer_handler mit einer exception ausgestoppt wird, könnte das Problem im asm stub liegen. Ich verwende dort folgenden Code:
    ..
    Der task_switch erfolgt im timer_handler.

    ..

    Was ist die beste Lösung und warum?

    ..



  • Ein wesentlicher Punkt, auf den Du unbedingt achten mußt:
    Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch und alles geht daneben. Bei dem 16-bit-taskswitching, was ich Dir in der Mail geschickt habe, war das ein entscheidender Faktor.
    Da ist übrigens auch eine Funktion mit drin, die einen Taskswitch erzwingt.
    Hab mich aber nie getraut, das alles auf 32-bit zu erweitern.


  • Mod

    Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch

    Danke für den Hinweis! Ich werde da noch ein ts-flag in meiner ODA (OS Data Area) Struktur einbauen, das zuvor abgefragt wird. Die nutze ich sowieso viel zu wenig.

    typedef struct oda
    {
        //...    
        //tasking
        UCHAR  ts_flag; // 0: taskswitch off  1: taskswitch on
    }oda_t;
    
    //...
    pODA->ts_flag = 1;
    
    //...
    if( pODA->ts_flag == 1 )
        task_switch();
    


  • Besser einen counter.

    int task_freeze()
    {
      if (!taskstop)
      { ...
      }
      return ++taskstop;
    }
    
    void task_unfreeze()
    {
       if (taskstop>1)   
         --taskstop;     
       else             
       { taskstop=0;
         ...
       }
    }
    

    und dann überleg mal, wie sich die Timerfunktion eventuell verhalten kann.
    Bei mir wurde der Originalvektor auf eine Routine umgelenkt, der erst den Originalvektor bedient hat, und dann noch einen Ticker inkrementiert hat.

    Was ich auch noch Wichtiges gefunden habe - Turbo C hatte da eine Semaphore namens INDOS. Solange die gesetzt war, durfte der Switcher nicht umschalten.
    So ein Festplattenkopf kann da genervt reagieren 😉


  • Mod

    Besser einen counter.

    Kannst Du den Mechanismus bitte erklären?

    Momentan habe ich auf die task_switch Technik von tyndur (OS einer Community) umgestellt, irgendwo ist aber noch ein müder Fehler versteckt, der zu GPF führt.

    Ich benutze nur ein TSS. Was muss alles in diese TSS geschrieben werden beim task_switch?



  • Der Counter ermöglicht es Paare von freeze und unfreeze auf verschiedenen Ebenen zu bilden. Erst wenn das 'oberste' unfreeze wieder gerufen wurde, gibt er das switching wirklich wieder frei. Wenn Du nur ein einfaches Bit nimmst, verlierst Du früher oder später den Überblick.
    Nimm mal an, Du sperrst per Bit vor einem Diskzugriff und gibst es später wieder frei. Was ist, wenn der User auch einen Grund hatte das Switching zu sperren, und unmittelbar nach dem Diskzugriff wird es aber wieder freigegeben?
    Mit dem Counter erschlägst Du alle Probleme. (Außer dem, daß Du Fehler bei der Paarbildung machst ;))

    Habt ihr in anderen Fällen nicht auch schon solche Counter drin? Ich dachte, ich hätte da schon was gelesen.
    Was in die TSS muß - tja, für 32-bit-mode weiß ich's eben auch nicht!


  • Mod

    Danke für diese Information.

    Hier ist ein Link zum ersten erfolgreichen Multitasking-Beispiel:
    http://www.henkessoft.de/OS_Dev/Downloads/36p.zip 🙂

    Damit ist eine für mich nicht einfache Hürde genommen.



  • glückwunsch *daumen hoch*
    🙂


Anmelden zum Antworten