300 likes | 318 Views
Context Switch Animation. Another one by Anastasia . User Mode Stack of process P1, bottom of the stack, low addresses (0xFF) here. Stack grows down ↓. Process P1 is running in user mode. Doing some userFunction(…) Lets assume that userFunction() was called by main()
E N D
Context Switch Animation Another one by Anastasia
User Mode Stack of process P1, bottom of the stack, low addresses (0xFF) here. Stack grows down ↓. Process P1 is running in user mode. Doing some userFunction(…) Lets assume that userFunction() was called by main() When P1 does so, it is not aware about task descriptor, kernel stack, etc. Nothing related to OS functionality . . . eax, ecx, edx saved by caller main() main() responsibility parameters of userFunction() pushed on the stack by main() hardware responsibility, call instruction, called by main() the return address (the next instruction of main() to perform) old (main()’s) ebp esi, edi, ebx saved by callee userFunction() userFunction() responsibility local variables of userFunction() . . .
. . . eax, ecx, edx saved by caller main() parameters of userFunction() pushed on the stack by main() the return address (the next instruction of main() to perform) old (main()’s) ebp esi, edi, ebx saved by callee userFunction() local variables of userFunction() . . .
Now, inside the code of userFunction() there is a call for operation system interface – wait() wait() is a wrapper function and is called as regular function eax, ecx, edx saved by caller main() parameters of userFunction() pushed on the stack by main() the return address (the next instruction of main() to perform) old (main()’s) ebp esi, edi, ebx saved by callee userFunction() local variables of userFunction() . . .
Now, inside the code of userFunction() there is a need to call operation system interface – wait() wait() is a wrapper function and is called as regular function eax, ecx, edx saved by caller main() parameters of userFunction() pushed on the stack by main() the return address (the next instruction of main() to perform) old (main()’s) ebp esi, edi, ebx saved by callee userFunction() local variables of userFunction() userFunction() responsibility eax, ecx, edx saved by caller userFunction() if needed (no parameters needed to wait()) save return address here old (userFunction()’s) ebp esi, edi, ebx saved by callee wait() if needed wait() responsibility local variables of wait() if they exist . . .
wait() is going to invoke an operation system programmable intercept – system call wait() puts the relevant value of the system call number to the eax register wait() invokes the assembly language instruction: int $0x80 Because of this hardware operation we leave the User Mode and we get to Kernel Mode! eax, ecx, edx saved by caller main() parameters of userFunction() pushed on the stack by main() the return address (the next instruction of main() to perform) old (main()’s) ebp esi, edi, ebx saved by callee userFunction() local variables of userFunction() eax, ecx, edx saved by caller userFunction() if needed (no parameters needed to wait()) save return address here old (userFunction()’s) ebp esi, edi, ebx saved by callee wait() if needed local variables of wait() if they exist . . . !!!
Kernel Mode Stack of process P1, bottom of the stack Process P1 is now running in Kernel Mode Registers ss, esp, eflags, cs, eip are getting new values after their old values were saved on the Kernel stack – this is all is done by one single assembler instruction int . . . Hardware responsibility, int $0x80 assembler instruction called by wait() old (user mode’s) ss, esp old eflags, cs, eip Here we already run the code of system call handler interrupt, which is one for all system calls. . . .
Kernel Mode Stack of process P1, bottom of the stack . . . User Stack Hardware responsibility, int $0x80 assembler instruction called by wait() old (user mode’s) ss, esp Kernel Code old eflags, cs, eip Here we already run the code of system call handler interrupt, which is one for all system calls. User Code System call handler responsibility old eax (system call number) gg old es, ds, eax, ebp, edi, esi, edx, ecx, ebx saved by SAVE_ALL macro No need in caller-save registers, no parameters needed to sys_wait(). Return address. Task descriptor of process P1 esp eip . . . call *sys_call_table(0, %eax, 4)
Now assume inside sys_wait() we need to do a context switch. Thus, function schedule() have to be called. . . . User Stack old (user mode’s) ss, esp Kernel Code old eflags, cs, eip System call handler responsibility old eax (system call number) User Code old es, ds, eax, ebp, edi, esi, edx, ecx, ebx saved by SAVE_ALL macro gg No need in caller-save registers, no parameters needed to sys_wait(). Return address. Task descriptor of process P1 old ebp esi, edi saved if needed esp OS function sys_wait() responsibility local variables of sys_wait() eip . . . ebp
Context switch process • In Kernel Mode function schedule() is called.
. . . old (user mode’s) ss, esp Kernel Code old eflags, cs, eip old eax (system call number) old es, ds, eax, ebp, edi, esi, edx, ecx, ebx saved by SAVE_ALL macro gg No need in caller-save registers, no parameters needed to sys_wait(). Return address. Task descriptor of process P1 old ebp esi, edi saved if needed esp local variables of sys_wait() eip . . . ebp Call for function schedule() is a regular function call.
old (user mode’s) ss, esp Kernel Code old eflags, cs, eip old eax (system call number) old es, ds, eax, ebp, edi, esi, edx, ecx, ebx saved by SAVE_ALL macro gg No need in caller-save registers, no parameters needed to sys_wait(). Return address. Task descriptor of process P1 old ebp esi, edi saved if needed esp OS function sys_wait() responsibility local variables of sys_wait() eip save eax, ecx, edx if needed return adress to sys_wait() ebp old ebp old esi, edi if needed local variables of schedule() prev next OS function schedule() responsibility • Function schedule() chooses process P2 to get the CPU after P1. • Then calls for: inline task_t* context_switch(task_t *prev, task_t *next) . . .
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses the process to switch to, then inline function context_switch(…) is called.
old (user mode’s) ss, esp Kernel Code esp old eflags, cs, eip old eax (system call number) eip old es, ds, eax, ebp, edi, esi, edx, ecx, ebx saved by SAVE_ALL macro ebp gg No need in caller-save registers, no parameters needed to sys_wait(). Return address. Task descriptor of process P1 old ebp esi, edi saved if needed local variables of sys_wait() save eax, ecx, edx if needed return adress to sys_wait() Task descriptor of process P2 old ebp old esi, edi if needed OS function schedule() responsibility local variables of schedule() prev next . . .
old ebp Kernel Code esp esi, edi saved if needed local variables of sys_wait() eip save eax, ecx, edx if needed ebp return adress to sys_wait() gg old ebp Task descriptor of process P1 old esi, edi if needed local variables of schedule() prev next . . . Task descriptor of process P2 Function context_switch() is a inline function! inline task_t *context_switch(task_t *prev, task_t *next) { switch_mm.... switch_to(prev, next, prev); //MACRO return prev; }
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses process to switch to, then inline function context_switch(…) is called. • Mainly context_switch(…) is calling switch_to(…) macro
old ebp Kernel Code esp esi, edi saved if needed, ebx is not going to be chnaged eip local variables of sys_wait() save eax, ecx, edx if needed ebp gg return adress to sys_wait() eax old ebp Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp Task descriptor of process P2 . . . thread field: esp0 eip esp … • Here is what switch_to macro does: • Saves the values of prev and next in the eax and edx respectively • Saves esi, edi and ebp on the stack • Saves esp in prevthread.esp
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses process to switch to, then inline function context_switch(…) is called. • Mainly context_switch(…) is calling switch_to(…) macro • switch_to(…) first saves the registers of the previous process in the stack and task descriptor.
N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) old esi, edi if needed local variables of schedule() prev next esi, edi and ebp . . . P R E V P R O C E S S S T A C K old ebp Kernel Code esp esi, edi saved if needed local variables of sys_wait() eip save eax, ecx, edx if needed ebp gg return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed local variables of schedule() prev next edx thread field: esp0 eip esp … esi, edi and ebp . . . Task descriptor of process P2 The next step that switch_macro does is to load nextthread.esp in esp Context switch !!! Kernel mode stack of process P2, which was chosen to be next thread field: esp0 eip esp …
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses process to switch to, then inline function context_switch(…) is called. • Mainly context_switch(…) is calling switch_to(…) macro • switch_to(…) first saves the registers of the previous process in the stack and task descriptor. • Then switch_to moves esp register to point to the next processes kernel stack (stack switch = context switch)
N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) old esi, edi if needed local variables of schedule() prev next esi, edi and ebp . . . P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . Part of switch_to macro’s code: movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip jmp __switch_to 1: popl %ebp popl %edi popl %esi Task descriptor of process P2 Kernel mode stack of process P2, which was chosen to be next thread field: esp0 eip esp …
P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() Part of switch_to macro’s code: movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip jmp __switch_to 1: popl %ebp popl %edi popl %esi Task descriptor of process P2 save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) Kernel mode stack of process P2, which was chosen to be next old esi, edi if needed local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp the address of “popl %ebp” instruction
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses process to switch to, then inline function context_switch(…) is called. • Mainly context_switch(…) is calling switch_to(…) macro • switch_to(…) first saves the registers of the previous process in the stack and task descriptor. • Then switch_to moves esp register to point to the next processes kernel stack (stack switch = context switch) • eip of the previous process is saved in task descriptor as pointing to label $1, eip of the next process is loaded on the stack
P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() Part of switch_to macro’s code: movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip jmp __switch_to 1: popl %ebp popl %edi popl %esi Task descriptor of process P2 save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) Kernel mode stack of process P2, which was chosen to be next old esi, edi if needed local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp the address of “popl %ebp” instruction
P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed Part of __switch_to function’s code: /* save special registers */ tss->esp0 = next->esp0; … movl %fs, prev->fs (movl %gs, prev->gs) if (prev->fs | prev->gs | next->fs | next->gs) { .. movl next->fs, %fs (movl next->gs, %gs) .. } /* load debug registers, load IO permission bitmap */ return; local variables of sys_wait() Task descriptor of process P2 save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) Kernel mode stack of process P2, which was chosen to be next old esi, edi if needed local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp the address of “popl %ebp” instruction
P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() Part of switch_to macro’s code: movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip jmp __switch_to 1: popl %ebp popl %edi popl %esi Task descriptor of process P2 save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) Kernel mode stack of process P2, which was chosen to be next old esi, edi if needed local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp the address of “popl %ebp” instruction
Context switch process • In Kernel Mode function schedule() is called. • schedule() chooses process to switch to, then inline function context_switch(…) is called. • Mainly context_switch(…) is calling switch_to(…) macro • switch_to(…) first saves the registers of the previous process in the stack and task descriptor • Then switch_to moves esp register to point to the next processes kernel stack (stack switch = context switch) • eip of the previous process is saved in task descriptor as pointing to label 1, eip of the next process is loaded on the stack • By jumping and returning from __switch_to function we load the address of label 1 into processor’s eip register
P R E V P R O C E S S S T A C K old ebp esp Kernel Code esi, edi saved if needed eip local variables of sys_wait() movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip save eax, ecx, edx if needed ebp return adress to sys_wait() old ebp eax Task descriptor of process P1 old esi, edi if needed edx local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp . . . N E X T P R O C E S S S T A C K … ↓ esi, edi saved if needed local variables of sys_wait() Part of switch_to macro’s code: movl %esp, prev->thread.esp movl next->thread.esp, %esp movl $1f, prev->thread.eip pushl next->thread.eip jmp __switch_to 1: popl %ebp popl %edi popl %esi Task descriptor of process P2 save eax, ecx, edx if needed return adress to sys_wait() old ebp (saved by schedule()) Kernel mode stack of process P2, which was chosen to be next old esi, edi if needed local variables of schedule() prev next thread field: esp0 eip esp … esi, edi and ebp the address of “popl %ebp” instruction