Hi,
GLneo wrote:i'm going for software task switching, but what do you mean 1 place to store all those redgisters???
Ahh - with software task switching you don't store
all the registers in the TSS.
Forget about the TSS for now, and think of a normal function or sub-routine that modifies all of the CPUs registers - what does it do? It just pushes everything on the stack. For software task switching, we do the same thing.
Conceptually, at the start of the task switch you push everything on the stack, then you change stacks (to a different task's stack), and then you pop everything off of the new tasks stack.
Of course nothing is this simple in real life. First, you need somewhere to store each task's ESP, so that you can switch from one stack to another. It's not the only information the OS needs to keep for each task, so it's best to have some form of "Task Structure" that describes the task, and store ESP in that.
Next, some (or maybe all) tasks have different address spaces, which means that you need to store CR3 somewhere. Usually the task's stack can only be accessed when the task's address space is in use, so you couldn't store CR3 on the tasks stack (you'd need CR3 to get CR3 off of the stack again). This is easy to fix - put CR3 in that "Task Structure", just like ESP.
Then, for reasons probably best left for later, it's a good idea to store the CPU's FPU/MMX/SSE/SSE2 state in the "Task Structure" instead of on the stack, because this allows the FPU/MMX/SSE/SSE2 state saving/loading to be postponed or even skipped if some tasks don't use FPU/MMX/SSE/SSE2 instructions. I recommend that you ignore the FPU/MMX/SSE/SSE2 state for now - it just confuses things and it's easy to add to a working scheduler later.
Combining all of this (but ignoring FPU/MMX/SSE/SSE2 state), you end up with something like:
Code: Select all
switch_tasks:
pushad
pushfd
cli
<push segment registers if necessary>
<store ESP in the previous task's Task Structure>
<get new task's CR3 from it's Task Structure>
<change address spaces if necessary>
<get new task's ESP from it's Task Structure>
<switch to the new task's stack>
<pop segment registers if necessary>
popfd
popad
ret
You may have noticed that saving and restoring the segment registers on the stack is optional. Because of all the protection checks loading a segment register is slow, which is one of the reasons why most people use a flat memory model (so data segment registers never need to be changed). Even if you do allow the segment registers to be changed you must remember that the task switching code is within the kernel. If CPL=3 code uses different segment registers they are often changed to segments that the kernel expects when CPL=3 code enters the CPL=0 kernel, which is what happens automatically for the CPL=3 CS and SS anyway. For these reasons, for almost all OS's, none of the segment registers need to be saved/restored during a task switch.
[continued]