Page 1 of 1

Paging and Process Stacks

Posted: Sat Sep 11, 2010 5:05 pm
by thegamefreak0134
So I recently got completely hacktastic context switching (multi-tasking, yay!!!) working in my kernel, and I cheated; I did so without virtual memory of any kind. I'm about to make the big giant leap of faith and enable paging and proper process loading (from ELF format) all at once, and while I was thinking about my spawn process function, something occured to me.

Processes each have their own stack, and naturally that stack would start out as a single 4k page at the end of the virtual address space, and then be allowed to grow downwards as needed, right? But, when the stack needs to grow past the page, wouldn't that result in a page fault? And doesn't a page fault (and any interrupt really) need to throw stuff on the stack? How does that work exactly?

I was looking at http://wiki.osdev.org/Context_Switching and it seems to suggest something about a TSS entry. Does this mean that all virtual processes will need to run in not-ring 3, and that their interrupt information will then end up on the kernel's stack? This seems logical, although I would need to rewrite some things, as presently I'm pushing my processes state info onto its own stack and just having the kernel hold on to the stack pointer for each process.

Or, is it more normal for the process stack to be a fixed size? I mean, I want there to be a limit on stack size, but I was hoping for it to grow dynamically so the limit could be rather large if needed / could possibly be set by the program as part of its header.

Thanks in advance. ^_^

Re: Paging and Process Stacks

Posted: Sat Sep 11, 2010 7:01 pm
by gerryg400
thegamefreak0134 wrote:So I recently got completely hacktastic context switching (multi-tasking, yay!!!) working in my kernel, and I cheated; I did so without virtual memory of any kind. I'm about to make the big giant leap of faith and enable paging and proper process loading (from ELF format) all at once, and while I was thinking about my spawn process function, something occured to me.

Processes each have their own stack, and naturally that stack would start out as a single 4k page at the end of the virtual address space, and then be allowed to grow downwards as needed, right? But, when the stack needs to grow past the page, wouldn't that result in a page fault? And doesn't a page fault (and any interrupt really) need to throw stuff on the stack? How does that work exactly?

I was looking at http://wiki.osdev.org/Context_Switching and it seems to suggest something about a TSS entry. Does this mean that all virtual processes will need to run in not-ring 3, and that their interrupt information will then end up on the kernel's stack? This seems logical, although I would need to rewrite some things, as presently I'm pushing my processes state info onto its own stack and just having the kernel hold on to the stack pointer for each process.

Or, is it more normal for the process stack to be a fixed size? I mean, I want there to be a limit on stack size, but I was hoping for it to grow dynamically so the limit could be rather large if needed / could possibly be set by the program as part of its header.

Thanks in advance. ^_^
Normally each process has more than one stack. The TSS contains information to tell the processor about the stacks and the processor switches stacks automatically when transitioning from one ring to another.

There are a number of possible arrangements, though they have some things in common. In most implementations, processes run at ring 3 and the kernel is at ring 0. When and interrupt occurs (hardware or software), a stack change automatically occurs. The stack change is controlled by the TSS. This guarantees that the kernel or interrupt has a nice stack to run on. Here are some ways to do it.

1. Each process has a ring3 stack and a ring0 stack. The ring0 stack is used to process system calls. This arrangement allows a process to be pre-empted and later continued while in a system call. Linux is like this I think.

2. Each process has a ring3 stack and each core has a ring0 stack. This method is mainly used in microkernels (or hobby monolithic kernels). In some implementations like this there is also a small ring0 stack for each process that is just large enough to hold the register set of the process. The disadvantage is that generally a system call must be completed before the next system call can be started.

3. As a variation of the first 2, sometimes each interrupt has its own stack. This allows interrupts to be processed in priority order easily and allows for interrupt processing to be suspended and to be moved from one core to another depending on priority and core availability.


The arrangement that you choose will depend on the goals for your OS.