allow task to make their own stacks
- jerryleecooper
- Member
- Posts: 233
- Joined: Mon Aug 06, 2007 6:32 pm
- Location: Canada
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.
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.
*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: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.
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.bewing wrote: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.use a seperate stack in kernel space for each userspace thread
cheers
Joe
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 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...)
Yeah, I'm thinking about just making it 48 bytes, and sticking it on the end of each ring3 job table entry.JoeKayzA wrote:you could make the kernel stack even smaller, of course.
Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.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.
But (usually) they can still legally access data on the stack of another thread.Tyler wrote:Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.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.
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.urxae wrote:But (usually) they can still legally access data on the stack of another thread.Tyler wrote:Thread's need their own stack... or else they wouldn't be able to use Automatic Storage Variables.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.
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
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.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.
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
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.Colonel Kernel wrote: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.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.
That's the basic idea...frank wrote: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.Colonel Kernel wrote: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.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.
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.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
But this stuff sounds interesting and right up my alley (I'm into portal-based IPC.), so please do explain.
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 - )
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 - )
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
- C. A. R. Hoare