Search code examples
operating-systemkernelvirtual-memorystack-memorycontext-switch

Question about Context Switching in xv6 OS x86


I'm reading OSTEP for my Operating Systems course, and I have a question from Chp.6.3:

Note that there are two types of register saves/restores that happen during this protocol. The first is when the timer interrupt occurs; in this case, the user registers of the running process are implicitly saved by the hardware, using the kernel stack of that process. The second is when the OS decides to switch from A to B; in this case, the kernel registers are explicitly saved by the software (i.e., the OS), but this time into memory in the process structure of the process. The latter action moves the system from running as if it just trapped into the kernel from A to as if it just trapped into the kernel from B.

I don't really understand what's going on in this following text-segment and the associated diagram:

Diagram

Specifically:

  • What is going on with the registers during the context switch? It mentions that there are "kernel registers" and "user registers"? Why are both saved, and why are each of them saved where they are? In class, I learned that the user registers are saved into the associated PCB whenever a context-switch occurs, but this seems to suggest that the user registers are saved into the k-stack and the kernel registers onto the PCB? What is the difference between these and what exactly is going on here? Details are appreciated. I really enjoy this stuff.

To be clear: to me, it seems like there is redundancy here, as from the diagram alone, it seems that user registers are being saved into PCB and k-stack and being restored from both.


Solution

  • It's best to think of this as two completely unrelated types of context switches that have nothing to do with each other:

    a) A switch from "user-space context" to "kernel context" (or from "kernel context" back to "user-space context") within the same task. This occurs when any interrupt (IRQ, exception, ...) occurs and when the kernel's API is being called by user-space (or when an interrupt or system call handler returns). In this case (especially for interrupts) there's are no calling conventions, and nothing to say "these registers may be trashed by the callee and the caller is responsible for saving them before making the call if it cares". Worse, for interrupts the "caller" can't even predict when they might occur and simply cannot prepare for them (by saving "known trashed" registers beforehand) at all.

    b) A switch from one task's context (that was running kernel code) to another task's context (that will also be running kernel code). In this case there is a calling convention involved; most (all?) calling conventions have "callee preserved" and "callee trashed" registers; and the function that does the actual task switch does not have to save/restore any of the "callee trashed" registers.

    Note that an IRQ might cause a switch from "user-space context" to "kernel context", and then immediately after that the kernel's IRQ handler might (or might not) decide to cause a switch from one task's context to another task's context.

    Also note that in practice it's necessary to treat these as different and independent things; because (e.g.) an IRQ (including a timer IRQ) can occur that does not cause a task switch, and a task switch can be caused by other code that has nothing to do with the timer IRQ (e.g. a task needing to wait for a mutex to be released, causing kernel to switch to some other task while the previous task waits for the mutex).

    What is going on with the registers during the context switch? It mentions that there are "kernel registers" and "user registers"?

    During a switch from "user-space context" to "kernel context" the user-space registers must be stored (so that its possible to restore the "user-space context" when switching from "kernel-space context" back to "user-space context" later).

    During a switch from one task's (kernel) context to to another task's (kernel) context the previous task's state (registers, etc) must be stored (so that its possible to restore the previous task's context when switching back to the previous task later).

    In class, I learned that the user registers are saved into the associated PCB whenever a context-switch occurs, but this seems to suggest that the user registers are saved into the k-stack and the kernel registers onto the PCB?

    For the switch from "user-space context" to "kernel context", the user-space registers are typically saved on the kernel stack (after the CPU switches from "user-stack" to "kernel-stack"). During the switch from one task's context to another task's context, both tasks' user-space registers are still on each tasks' kernel stack. For where the kernel's registers are saved/restored during a switch from one task to another, it's up to the OS designer - most registers can be saved on the task's kernel stack (and not saved in a PCB structure) and most operating systems do that, but some registers (e.g. stack pointer) can't be saved on the kernel stack (or more specifically, can't be restored from the stack later) and must be stored in some kind of PCB structure; and some operating systems just save everything in a PCB structure.

    The problem is that your teachers are probably making the (common) mistake of conflating one type of context switch with a completely different type of context switch; and from that perspective the "one context switch that does everything combined" needs to save both the user-space registers (from one type of context switch) and the kernel-space registers (from the other type of context switch).