allow task to make their own stacks

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
User avatar
jerryleecooper
Member
Member
Posts: 233
Joined: Mon Aug 06, 2007 6:32 pm
Location: Canada

Post by jerryleecooper »

I store the esp/eip/ss all registers of my processes in the ingeniously named "process" struct. Everytime there's a context switch I save the registers as saved on the user stack by entering in the timer interrupt, and I choose another process, and copy in the old process's stack its registers, and when the interrupt gets called again, this process's registers gets saved on from its user stack, and another process's registers are copied on the process's stack. My timer code is in c, called from my global interrupt code in assembler, and I don't want to change my assembler code too much because it is pretty code. My doing is probably clunky, but it works, and well enough for me.
But I would like to know, is saving the registers in the users stacks correct? Oh, I think I have the answer, the only time the registers gets read is from the interrupt, and it is known from there that the registers comes from entering in the interrupt. :oops:
User avatar
JoeKayzA
Member
Member
Posts: 79
Joined: Wed Aug 24, 2005 11:00 pm
Location: Graz/Austria

Post by JoeKayzA »

bewing wrote:A ring0 app will store EFLAGS/CS/EIP on its own stack. This is very nice.

If I only have one ring0 stack, then an interrupted ring3 app will store SS/ESP/EFLAGS/CS/EIP on the ring0 system stack! Isn't that correct? This is totally unacceptable -- I may not be returning to ring 3. I need that "state" info stored somewhere with the task -- preferably in the task's own memory space. So I have to remove it from the ring0 system stack, right? So I'm back with trying to determine whether I interrupted from CPL0 or 3.
*If* you only have one ring0 stack, of course, that changes the design completely. You should of course differentiate whether a ring0 or a ring3 task was interrupted, but that shouldn't be the problem as the interrupted task's CS-value is pushed onto the stack during an interrupt.
bewing wrote:
use a seperate stack in kernel space for each userspace thread
OK, now I'm seeing what you meant by this -- so every single userspace thread that gets spawned allocates both a new stack in userspace AND a new stack in physical kernelspace? Gawd, that's horrifying. I don't want to waste that quantity of memory in kernelspace just to handle interrupts from every possible running thread in userspace.
You got it right, yes. But as I said, that's the way most multitasking systems do it. You need some place in kernel space to store a ring3 task's saved state, just for security reasons (otherwise ring3 code could for example change it's own privilege level...) Typically the ring0 stack is kept small to not exhaust kernel address space, Linux for example uses 4K per kernel stack. If you design a microkernel system (and you don't have much systemcall-code to execute in a thread's context) you could make the kernel stack even smaller, of course.

cheers
Joe
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Post by bewing »

JoeKayzA wrote:You need some place in kernel space to store a ring3 task's saved state, just for security reasons (otherwise ring3 code could for example change it's own privilege level...)
Actually, it couldn't. During the time that any IRET info is stored in the userland app's memory space the app is swapped out. So it can't change a damned thing. If you were to implement stupid OS level memory security, then it is true that running userapp2 could change the priv level of userapp1 (by modifying userapp1's memory space) -- but that could easily be fixed by setting userapp1's stack pages (if that's where the IRET info is) to "read only" while the app is swapped out.

JoeKayzA wrote:you could make the kernel stack even smaller, of course.
Yeah, I'm thinking about just making it 48 bytes, and sticking it on the end of each ring3 job table entry.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

This only applies if there can only be one thread in each application. Think about it, if you stored the iret information on the user stack couldn't another thread from the same process change it? That's why I suggest you leave it on the kernel stack.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

frank wrote:This only applies if there can only be one thread in each application. Think about it, if you stored the iret information on the user stack couldn't another thread from the same process change it? That's why I suggest you leave it on the kernel stack.
Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

Tyler wrote:
frank wrote:This only applies if there can only be one thread in each application. Think about it, if you stored the iret information on the user stack couldn't another thread from the same process change it? That's why I suggest you leave it on the kernel stack.
Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.
But (usually) they can still legally access data on the stack of another thread.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

urxae wrote:
Tyler wrote:
frank wrote:This only applies if there can only be one thread in each application. Think about it, if you stored the iret information on the user stack couldn't another thread from the same process change it? That's why I suggest you leave it on the kernel stack.
Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.
But (usually) they can still legally access data on the stack of another thread.
Well i don't know about Linux, but in My Operating System and i am pretty sure Windows, the stack is swapped out in order to prevent having to dedicate new areas of the virtual address space for each thread's stack.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Tyler wrote:Well i don't know about Linux, but in My Operating System and i am pretty sure Windows, the stack is swapped out in order to prevent having to dedicate new areas of the virtual address space for each thread's stack.
I don't know about Linux either, but that's not how it works in Windows. Within a single process, all threads' stacks co-exist in the same address space at different locations.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

Colonel Kernel wrote:
Tyler wrote:Well i don't know about Linux, but in My Operating System and i am pretty sure Windows, the stack is swapped out in order to prevent having to dedicate new areas of the virtual address space for each thread's stack.
I don't know about Linux either, but that's not how it works in Windows. Within a single process, all threads' stacks co-exist in the same address space at different locations.
Yeah exactly, if the stack existed at the same location or was inaccessible to the other threads that would mean that the page directory would be different in between the different threads.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

frank wrote:
Colonel Kernel wrote:
Tyler wrote:Well i don't know about Linux, but in My Operating System and i am pretty sure Windows, the stack is swapped out in order to prevent having to dedicate new areas of the virtual address space for each thread's stack.
I don't know about Linux either, but that's not how it works in Windows. Within a single process, all threads' stacks co-exist in the same address space at different locations.
Yeah exactly, if the stack existed at the same location or was inaccessible to the other threads that would mean that the page directory would be different in between the different threads.
That's the basic idea...
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

It would also mean that you couldn't use a local stack variable for a synchronous inter-thread call. Heap/global vars would have to be used, which is dangerous in the case of globals and maybe slow in the case of heap vars. Seems an unnecessary policy to me...
Crazed123
Member
Member
Posts: 248
Joined: Thu Oct 21, 2004 11:00 pm

Post by Crazed123 »

And it's better to reference the stack of a running thread when sending a message to another running thread?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

It's commonly done with synchronous cross-thread calls. Whether it's safe or not is another matter, the fact is muchos code uses it :P
Crazed123
Member
Member
Posts: 248
Joined: Thu Oct 21, 2004 11:00 pm

Post by Crazed123 »

JamesM wrote:It's commonly done with synchronous cross-thread calls. Whether it's safe or not is another matter, the fact is muchos code uses it :P
What, exactly, are synchronous cross-thread calls? I thought the point of a thread was that it ran its own instruction stream, and therefore couldn't "called" into or out of.

But this stuff sounds interesting and right up my alley (I'm into portal-based IPC.), so please do explain.
Avarok
Member
Member
Posts: 102
Joined: Thu Aug 30, 2007 9:09 pm

Post by Avarok »

Heh...

Threads are supposed to be parallel transformations on a data set, however that would require that data not be shared at all between them. Then you have processes. Threads are *supposed* to share the data they transform.

With that understood, often the data needs to be in a certain state for the a given thread to continue execution. This will typically rely on the output of another thread or external event. The thread *could* just infinitely loop until the result came, or we can have it give up it's execution time to something else. The desireable thing often appears to be to cede it's time to the thread it's waiting for.

So yeah, again, the difference between threads and processes is that threads aren't isolated from one another in memory, and so can share memory without any additional abstraction. This is better in all cases except when one thread cannot trust another thread (the bad thread could change my stack, or overwrite my code - :evil: )
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.
- C. A. R. Hoare
Post Reply