Okay, in my last blog post we discussed the theory of stacks in general, mostly referring to user stacks. In this blog post, I’m going to explain the different kinds of stacks available to the operating system.
A thread consists of a user stack and a kernel stack. Firstly, we’ll talk about user stacks, since these are more simple, and we’ve focused on these more in the last blog post.
We know when a thread is created, the Memory Manager reserves about 1MB of memory to the stack. We should also know that the stack isn’t committed straight away, that is, the stack grows when local variables and function calls are created or called within the program. Only around 64KB of memory is initially committed to the stack. When the stack grows, and touches the Guard Page it expands, and part of the reserved region of the stack is committed to the stack. This should be a simple remainder of stacks discussed in my last blog post. Remember that user stacks will not shrink back like kernel stacks.
We can gather the stack reserved and stack committed sizes with the !dh extension, when specifying the name of a module. User stacks are only used to storage user-mode information.
The Kernel Stack is used to store kernel-mode related information, and can only be accessed if given the privilege by the kernel, otherwise this is a protected data structure. A kernel stack is usually much smaller size, with about 12KB for the stack and 16KB if you count the size of the Guard Page, since it’s excepted to have much less recursive function calls (which can cause stack overflows) and better management of local variables. Again, kernel stacks are allocated from virtual memory, and although quite uncommon can be paged out onto the disk. The stacks which are paged out, tend to be inactive stacks.
Kernel Stacks can also grow and strink in size unlike User Stacks due to a size limitation. Another oddity with kernel stacks, is that they can move in both directions essentially. When data has been removed from the stack, then the stack moves upwards, and when data is added to the stack, the stack is able to move downwards in memory.
This is assigned to a per-processor basis, and this stack is used when DPCs are executed. DPC stacks are also used to separate kernel code from DPC code, and a switch may be performed to the DPC stack when the kernel stack doesn’t have enough space.
The DPC Stack can be found within the _KPRCB data structure, and the DPC Queue can be found with the !pcr extension.
Interrupt stacks are associated on a per processor basis like DPC stacks, and are only used while the kernel is currently using that particular CPU. When a interrupt (external) happens, this can be I/O from the mouse and keyboard, then the kernel switches to the the interrupt stack, since it saves creating more space on the kernel stack with the associated thread.
Interrupt Stacks are also used to load new known good stacks from the Interrupt Stack Table (IST) when serious exceptions occur like a NMI. There is currently 7 IST entries per a CPU, and the IST uses the TSS to point to these good stacks.
Intel Developers Manual – Section 6.2