Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: LittleFox 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 :-)
-
Hm, so sind dein Code in Ordnung aus... Bliebe für mich eigentlich nur das EOI, sendest du das?
-
Ich hab den Code nur mal so überflogen aber mir fällt auf, dass yPos gar nicht inkrementiert wird.
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?
-
@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
-
rufst du in dem irq_handler auch schdule auf wenn ein Timer Interrupt reinkommt? Ich gehe davon aus, aber vielleicht schaust du nochmal nach.
-
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 ...
-
keiner weiter ne idee? :cry:
-
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!
-
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
-
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!
-
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.
-
Ja auf das hätte ich als nächstes getippt ! So genau hab ich nicht geschaut :(
-
es geht !!!! :-D :-D
Danke an alle für die Hilfe
die -1 war's
nur komisch das es vorher nicht ging ...