Autor Thema: [gelöst]Problem mit Multitasking  (Gelesen 8193 mal)

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« am: 03. February 2010, 17:50 »
Hallo,
ich schreibe auch gerade ein Betriebssystem und hänge gerade beim Multitasking.
Aus irgendeinen Grund krieg ich das Beispiel aus dem Tutorial nicht hin.
Statt As und Bs zu schreiben schreibt er nur As  :x

Hier meine multitasking.c
#include "main.h"

int yPos = 0;
int xPos = 0;

void task_a(void)
{
    while (1) {
        print_string_at_pos("A", 0x02, xPos, yPos);
xPos++;
    }
}
 
void task_b(void)
{
    while (1) {
        print_string_at_pos("B", 0x04, xPos, yPos);
xPos++;
    }
}

static char stack_a[4096];
static char stack_b[4096];
 
/*
 * Jeder Task braucht seinen eigenen Stack, auf dem er beliebig arbeiten kann,
 * ohne dass ihm andere Tasks Dinge ueberschreiben. Ausserdem braucht ein Task
 * einen Einsprungspunkt.
 */
struct cpu_state* init_task(char* stack, void* entry)
{
    /*
     * CPU-Zustand fuer den neuen Task festlegen
     */
    struct cpu_state new_state = {
        .eax = 0,
        .ebx = 0,
        .ecx = 0,
        .edx = 0,
        .esi = 0,
        .edi = 0,
        .ebp = 0,
        //.esp = unbenutzt (kein Ring-Wechsel)
        .eip = (unsigned long) entry,
 
        /* Ring-0-Segmentregister */
        .cs  = 0x08,
        //.ss  = unbenutzt (kein Ring-Wechsel)
 
        /* IRQs einschalten (IF = 1) */
        .eflags = 0x202,
    };
 
    /*
     * Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am
     * Ende so aussieht als waere der Task durch einen Interrupt unterbrochen
     * worden. So kann man dem Interrupthandler den neuen Task unterschieben
     * und er stellt einfach den neuen Prozessorzustand "wieder her".
     */
    struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
    *state = new_state;
 
    return state;
}



static int current_task = 1;
static int num_tasks = 2;
static struct cpu_state* task_states[2];
 
void init_multitasking(void)
{
    task_states[0] = init_task(stack_a, task_a);
    task_states[1] = init_task(stack_b, task_b);
}
 
/*
 * Gibt den Prozessorzustand des naechsten Tasks zurueck. Der aktuelle
 * Prozessorzustand wird als Parameter uebergeben und gespeichert, damit er
 * beim naechsten Aufruf des Tasks wiederhergestellt werden kann
 */
struct cpu_state* schedule(struct cpu_state* cpu)
{
    /*
     * Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir
     * gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen
     * wir spaeter nicht wieder.
     */
    if (current_task >= 0) {
        task_states[current_task] = cpu;
    }
 
    /*
     * Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los
     */
    if(current_task == 0)
current_task = 1;

else if(current_task == 1)
current_task = 0;
 
    /* Prozessorzustand des neuen Tasks aktivieren */
    cpu = task_states[current_task];
 
    return cpu;
}

Ausschnitt aus der main.asm:
...

irq_common_stub:
    push ebp
    push edi
    push esi
    push edx
    push ecx
    push ebx
    push eax
 
    ; // Handler aufrufen
    ; // Der Rueckgabewert ist der Prozessorzustand des moeglicherweise
    ; // gewechselten Tasks. Wir muessen also den Stack dorthin wechseln
    ; // um die Register wiederherzustellen.
    push esp
    call irq_handler
    mov esp, eax
 
    ; // CPU-Zustand wiederherstellen

    pop eax
    pop ebx
    pop ecx
    pop edx
    pop esi
    pop edi
    pop ebp
 
    ; // Fehlercode und Interruptnummer vom Stack nehmen
    add esp, 8
 
    ; // Ruecksprung zum unterbrochenen Code
    iret
...

Multitasking initialisiere ich, direkt bevor ich die Hardwareinterrupts aktiviere, über init_multitasking();

Als Compiler verwende ich GCC und als Assembler NASM

Danke im Voraus für Hilfe  :-)
« Letzte Änderung: 05. February 2010, 11:44 von littlefox »

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #1 am: 03. February 2010, 18:04 »
Hm, so sind dein Code in Ordnung aus... Bliebe für mich eigentlich nur das EOI, sendest du das?

DaCodaaa

  • Gast
Gespeichert
« Antwort #2 am: 03. February 2010, 18:05 »
Ich hab den Code nur mal so überflogen aber mir fällt auf, dass yPos gar nicht inkrementiert wird.
Zitat
int yPos = 0;
int xPos = 0;

void task_a(void)
{
    while (1) {
        print_string_at_pos("A", 0x02, xPos, yPos);
xPos++;
    }
}

void task_b(void)
{
    while (1) {
        print_string_at_pos("B", 0x04, xPos, yPos);
xPos++;
    }
}
Vielleicht irre ich mich aber möglicherweise siehst du nur die erste Zeile, in der noch As zu sehen sind, und daraufhin wird xPos so gross, dass es nicht mehr auf den Bildschirm passt. Oder macht print_string-at_pos das von alleine?

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #3 am: 03. February 2010, 18:07 »
@XanClic
ja da ist alles in ordnung:


EDIT: irq.c:
if (r->intr >= 40)
{
    outb(0xA0, 0x20);
}
outb(0x20, 0x20);

@DaCodaaa
das macht die print_string_at_pos automatisch

DaCodaaa

  • Gast
Gespeichert
« Antwort #4 am: 03. February 2010, 18:13 »
rufst du in dem irq_handler auch schdule auf wenn ein Timer Interrupt reinkommt? Ich gehe davon aus, aber vielleicht schaust du nochmal nach.

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #5 am: 03. February 2010, 18:18 »
mach ich auch alles:

struct cpu_state* irq_handler(struct cpu_state *r)
{
   struct cpu_state* new_cpu = r;

    /* This is a blank function pointer */
    struct cpu_state* (*handler)(struct cpu_state *r);

    /* Find out if we have a custom handler to run for this
    *  IRQ, and then finally, run it */
    handler = irq_routines[r->intr - 32];
    if (handler)
    {
        handler(r);
    }
   
    if (r->intr == 0x20) {
        new_cpu = schedule(r);
    }


    /* If the IDT entry that was invoked was greater than 40
    *  (meaning IRQ8 - 15), then we need to send an EOI to
    *  the slave controller */
    if (r->intr >= 40)
    {
        outb(0xA0, 0x20);
    }

    /* In either case, we need to send an EOI to the master
    *  interrupt controller too */
    outb(0x20, 0x20);
   
   return new_cpu;
}

ich weiß echt nicht weiter ...
« Letzte Änderung: 03. February 2010, 18:56 von littlefox »

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #6 am: 04. February 2010, 15:55 »
keiner weiter ne idee? :cry:

gcalctool

  • Beiträge: 29
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 04. February 2010, 19:45 »
Hi littlefox!

Wenn ich dein Problem hätte würde ich zunächst folgendes probieren:
mal die task_a und task_b Funktion folgendermaßen abändern:
void task_a(void)
{
    int yPos = 0;
    int xPos = 0;

    while (1) {
        print_string_at_pos("A", 0x02, xPos, yPos);
xPos++;
    }
}

void task_b(void)
{
   int yPos = 0;
   int xPos = 0;
 
    while (1) {
        print_string_at_pos("B", 0x04, xPos, yPos);
xPos++;
    }
}

und dann würde ich mir in deiner Methode struct cpu_state* irq_handler(struct cpu_state *r) in dem if: if (r->intr == 0x20) mal etwas ausgeben lassen. So in etwa:
if (r->intr == 0x20) {
        printf("Scheduler Start!\n");
        new_cpu = schedule(r);
        printf("Scheduler Ende!\n");
    }

Dann würd ich mal schauen was passiert vl. Hilft dir das weiter! Wenn der Scheduler RICHTIG aufgerufen wird poste uns doch mal die Funktion print_string_at_pos vl hats da bissi was!

mfg!

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #8 am: 04. February 2010, 20:21 »
Hallo,
bringt leider auch nichts  :cry:

er zeigt immernoch nur As, aber zumindesten Scheduler Start und Scheduler Ende. langsam blick ich nicht mehr durch

print_string_at_pos():
void print_string_at_pos(char *text, int col, int x, int y)
{
x--;
int pos = (((y-1) * 80) + x) * 2;
char* VGAMem = (char*) (0xB8000 + pos);

    while(*text)
    {
        *VGAMem = *text;
        VGAMem++;
        *VGAMem = col;
        VGAMem++;
        text++;
    }
}

ich denke nicht das es an ihr liegt, sie funktioniert eigentlich immer ...

Trotzdem Danke

EDIT: gerade gesehen das yPos eigentlich mit 1 anfangen müsste, dürfte aber nicht viel ändern
« Letzte Änderung: 04. February 2010, 20:35 von littlefox »

gcalctool

  • Beiträge: 29
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 05. February 2010, 08:22 »
Hi!

Probier mal wenn du static int current_task; am Anfang nicht auf 1 setzt sondern auf 0 was dann Passiert. Werden dann lauter Bs ausgegeben? Wenn ja probiers mal so:


void task_a(void)
{
    int yPos = 0;
    int xPos = 0;

    while (1) {
        if(posX % 2 == 0)
            print_string_at_pos("A", 0x02, xPos, yPos);
xPos++;
    }
}

void task_b(void)
{
   int yPos = 0;
   int xPos = 0;

    while (1) {
        if(posX % 2 == 1)
            print_string_at_pos("B", 0x04, xPos, yPos);
xPos++;
    }
}

Vielleicht liegts daran!

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 05. February 2010, 08:56 »
Wenn man sich das Tutorial genau anschaut, wird current_task dort mit -1 initialisiert. Deswegen gibt es im Scheduler auch das if (current_task >= 0).

Was mit current_task == 1 am Anfang passiert ist vermutlich, dass beim ersten Wechsel in einen Task der Taskzustand vom B-Task (Index 1 == current_task)  mit dem bisherigen Kernelzustand überschrieben wird - und der Kernel hängt ja in dieser Endlosschleife in start.S und beim ersten Timerinterrupt geht das Multitasking los. Weil eine Endlosschleife nicht so richtig sichtbar ist, siehst du dann nur As. Was vielleicht sichtbar sein könnte, ist, dass der A-Task immer für eine Weile unterbrochen wird, damit der Kernel seine Endlosschleife abarbeiten kann, d.h. die As müssten "schubweise" kommen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

gcalctool

  • Beiträge: 29
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 05. February 2010, 11:40 »
Ja auf das hätte ich als nächstes getippt ! So genau hab ich nicht geschaut :(
« Letzte Änderung: 05. February 2010, 11:46 von gcalctool »

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #12 am: 05. February 2010, 11:43 »
es geht !!!! :-D :-D

Danke an alle für die Hilfe

die -1 war's
nur komisch das es vorher nicht ging ...

 

Einloggen