Page 1 of 1

ESP0 problem [ recursive switches usermode / kernelmode ]

Posted: Thu Feb 15, 2007 1:03 pm
by Otter
I just implemented simple pipes. My OS does not support real multitasking, but I use the following way:

If I call

Code: Select all

$ a | b
process <b> is started. When <b> calls a syscall to read a character from stdin the scheduler switches to process <a>. When <a> calls a syscall to write a character to stdout, this character is passed as return code to process <b> and process <b> gets active.

I use a very unclean implementation of that, but it works for simple examples, like

Code: Select all

$ hw | hexdump
00000000: 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 20 21 0A
If I try a more complex one, like

Code: Select all

$ hexdump hw | more
my kernel crashes because of a stack overflow ( "hexdump hw" calls more than a thausend times putc cause hw is statically linked to my libc-port ) .

Here are some excerpts from my syscalls. I know that they flood the stack, but I don't know how to fix that ...

Excerpt from syscall #0 ( putc )

Code: Select all

/* ... */
/* there is a master process so pass the character x to it */
currentProcess->master->tss.eax = x; // return value;
currentProcess->master->go();
/* ... */
Excerpt from syscall #1 ( readkey )

Code: Select all

/* ... */
/* there is a slave process so activate it and wait for the character */
return currentProcess->slave->go();
/* ... */
CProcess::go() changes currentProcess and perform a far jmp to the tss of the process, more to it later 'cause I think there is a line which is mainly important to the error.

So what happens is that:
I call "a | b" . <b> is activated and calls syscall 1 ( readkey ). My kernel activates process <a>. Process <a> calls syscall 0 ( putc ) . <a> is only active when the master has called readkey so it passes the character as return code to the master and re-activate the master.

Now to the problem, which is mainly caused by CProcess::go(). Everytime the kernel does a task switch, CProcess::go() is used.

Code: Select all

unsigned int CProcess::go() {
    currentProcess = this;
    tss.stack[0].esp = getESP()-12; // tss means this->tss. It's a local member of CProcess
    return farJmp(tr,0); // tr is a local member, too
}
The problem of course is "tss.stack[0].esp = getESP()-12;" . I did not think much about it when I wrote it ... I knew that it would cause errors later but it worked for a while ^^ ( unclean I know but I wanted some success ... ) ... getESP() should be clear, -12 because of the call to the assembler routine farJmp: There will be three more dwords pushed on the stack, the two params ( tr and 0 ) followed by the return address.

Maybe you already see the problem, but I give an example:

So lets use " a | b ". b has called readkey, and a has called putc, so the (kernel) stack could look like:

Code: Select all

syscall1 (readkey) // called by b
processA->go       // called by kernel
farJmp             // called by kernel
syscall0 ( putc )  // called by a
processB->go       // called by kernel
farJmp             // called by kernel
Some day farJmp will return and pop stack data but then currentProcess->tss.stack[0].esp is set to this position. I think there is a very simple solution but I think too complicated ...

Posted: Fri Feb 16, 2007 11:24 am
by Otter
Ok sorry, I found the solution :oops: :oops:
I was right I thougt too compilcated, it's really obvious ... I only need to create seperate (kernel) stacks for each process. Of course, every process use its own stack, but syscalls allways use the same kernel stack, indepedant of the process calling this syscall.

Of course that only works if the syscall called by process <x> returns to process <x>, but here the syscall switches the process so the stack get's fragmented.

solved