other exceptions context switches
play

other exceptions / context switches 1 Changelog 16 January 2020: - PowerPoint PPT Presentation

other exceptions / context switches 1 Changelog 16 January 2020: fjx location of stack pointer indicator on swtch summary 22 January 2020: expand a little on I/O interrupts to separate them from I/O-requesting system calls 1 last time


  1. xv6: interrupt table setup ... for ( int i = 0; i < 256; i++) SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0); SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); ... trap.c (run on boot) set every entry of interrupt (descriptor) table to assembly function vectors[i] that saves registers, then calls trap() 20 lidt(idt, sizeof (idt));

  2. non-system call exceptions xv6 handles many kinds of exceptions other than system calls recall: our orignal examples of why hardware had exceptions timer interrupt — ‘tick’ from constantly running timer make sure infjnite loop doesn’t hog CPU check for programs waiting for time to pass faults — e.g. access invalid memory, divide by zero xv6’s action: kill the program I/O — I/O device indicates that it requires OS action communicate with I/O device that now has data ready possibly wake up waiting programs 21

  3. xv6: faults void more featureful OS would lookup the name for you can lookup in traps.h prints out trap number assume it screwed up print message and kill running program (example: invalid memory access, divide-by-zero) exception not otherwise handled } } 22 "eip 0x%x addr 0x%x--kill proc\n", cprintf("pid %d %s: trap %d err %d on cpu %d " default : ... ... { trap( struct trapframe *tf) switch (tf − >trapno) { ... // (not shown here: similar code for errors in kernel itself) myproc() − >pid, myproc() − >name, tf − >trapno, tf − >err, cpuid(), tf − >eip, rcr2()); myproc() − >killed = 1;

  4. xv6: faults void more featureful OS would lookup the name for you can lookup in traps.h prints out trap number assume it screwed up print message and kill running program (example: invalid memory access, divide-by-zero) exception not otherwise handled } } 22 "eip 0x%x addr 0x%x--kill proc\n", cprintf("pid %d %s: trap %d err %d on cpu %d " default : ... ... { trap( struct trapframe *tf) switch (tf − >trapno) { ... // (not shown here: similar code for errors in kernel itself) myproc() − >pid, myproc() − >name, tf − >trapno, tf − >err, cpuid(), tf − >eip, rcr2()); myproc() − >killed = 1;

  5. non-system call exceptions xv6 handles many kinds of exceptions other than system calls recall: our orignal examples of why hardware had exceptions timer interrupt — ‘tick’ from constantly running timer make sure infjnite loop doesn’t hog CPU check for programs waiting for time to pass faults — e.g. access invalid memory, divide by zero xv6’s action: kill the program I/O — I/O device indicates that it requires OS action communicate with I/O device that now has data ready possibly wake up waiting programs 23

  6. xv6: I/O lapiceoi(); to appropriate application(s) handlers arrange for data to be sent exception indicates: data now ready uart = serial port (external terminal) kbd = keyboard ide = disk interface break ; lapiceoi(); uartintr(); case T_IRQ0 + IRQ_COM1: break ; kbdintr(); void case T_IRQ0 + IRQ_KBD: ... break ; lapiceoi(); ideintr(); case T_IRQ0 + IRQ_IDE: ... ... { 24 trap( struct trapframe *tf) switch (tf − >trapno) {

  7. non-system call exceptions xv6 handles many kinds of exceptions other than system calls recall: our orignal examples of why hardware had exceptions timer interrupt — ‘tick’ from constantly running timer make sure infjnite loop doesn’t hog CPU check for programs waiting for time to pass faults — e.g. access invalid memory, divide by zero xv6’s action: kill the program I/O — I/O device indicates that it requires OS action communicate with I/O device that now has data ready possibly wake up waiting programs 25

  8. xv6: timer interrupt void acquire/release — related to synchronization (later) (needed for all interrupts from ‘external’ devices) lapiceoi — tell hardware we have handled this interrupt ( sleep system call) certain amount of time wakeup — handle waiting processes on timer interrupt: yield = maybe switch to difgerent program if a process is running (trigger periodically by external timer): on timer interrupt } ... yield(); ... // Force process to give up CPU on clock tick. wakeup(&ticks); { ... case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); ticks++; release(&tickslock); } lapiceoi(); break ; ... 26 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  9. xv6: timer interrupt void acquire/release — related to synchronization (later) (needed for all interrupts from ‘external’ devices) lapiceoi — tell hardware we have handled this interrupt ( sleep system call) certain amount of time wakeup — handle waiting processes on timer interrupt: yield = maybe switch to difgerent program if a process is running (trigger periodically by external timer): on timer interrupt } ... yield(); ... // Force process to give up CPU on clock tick. wakeup(&ticks); { ... case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); ticks++; release(&tickslock); } lapiceoi(); break ; ... 26 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  10. xv6: timer interrupt void acquire/release — related to synchronization (later) (needed for all interrupts from ‘external’ devices) lapiceoi — tell hardware we have handled this interrupt ( sleep system call) certain amount of time wakeup — handle waiting processes on timer interrupt: yield = maybe switch to difgerent program if a process is running (trigger periodically by external timer): on timer interrupt } ... yield(); ... // Force process to give up CPU on clock tick. wakeup(&ticks); { ... case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); ticks++; release(&tickslock); } lapiceoi(); break ; ... 26 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  11. xv6: timer interrupt void acquire/release — related to synchronization (later) (needed for all interrupts from ‘external’ devices) lapiceoi — tell hardware we have handled this interrupt ( sleep system call) certain amount of time wakeup — handle waiting processes on timer interrupt: yield = maybe switch to difgerent program if a process is running (trigger periodically by external timer): on timer interrupt } ... yield(); ... // Force process to give up CPU on clock tick. wakeup(&ticks); { ... case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); ticks++; release(&tickslock); } lapiceoi(); break ; ... 26 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  12. xv6: timer interrupt void acquire/release — related to synchronization (later) (needed for all interrupts from ‘external’ devices) lapiceoi — tell hardware we have handled this interrupt ( sleep system call) certain amount of time wakeup — handle waiting processes on timer interrupt: yield = maybe switch to difgerent program if a process is running (trigger periodically by external timer): on timer interrupt } ... yield(); ... // Force process to give up CPU on clock tick. wakeup(&ticks); { ... case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); ticks++; release(&tickslock); } lapiceoi(); break ; ... 26 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  13. time multiplexing loop.exe ssh.exe firefox.exe loop.exe ssh.exe = operating system exception happens return from exception 27

  14. time multiplexing loop.exe ssh.exe firefox.exe loop.exe ssh.exe = operating system exception happens return from exception 27

  15. OS and time multiplexing starts running instead of normal program via exception saves old program counter, registers somewhere sets new registers, jumps to new program counter called context switch saved information called context 28

  16. context all registers values condition codes program counter address space = page table base pointer 29 %rax %rbx , …, %rsp , …

  17. contexts (A running) Process B memory: in Memory … … %rcxPC %rbxZF %raxSF OS memory: code, stack, etc. code, stack, etc. %rax Process A memory: in CPU PC ZF SF … %rsp %rcx %rbx 30

  18. contexts (B running) OS memory: on A’s kernel stack into “trapframe” exception handler xv6: A’s registers saved by in Memory … … %rcxPC %rbxZF %raxSF code, stack, etc. %rax Process B memory: code, stack, etc. Process A memory: in CPU PC ZF SF … %rsp %rcx %rbx 31

  19. contexts (B running) OS memory: on A’s kernel stack into “trapframe” exception handler xv6: A’s registers saved by in Memory … … %rcxPC %rbxZF %raxSF code, stack, etc. %rax Process B memory: code, stack, etc. Process A memory: in CPU PC ZF SF … %rsp %rcx %rbx 31

  20. exercise: counting context switches two active processes: A: running infjnite loop B: described below process B asks to read from from the keyboard after input is available, B reads from a fjle then, B does a computation and writes the result to the screen how many system calls do we expect? how many context switches do we expect? your answers can be ranges 32

  21. counting system calls (no system calls from A) B: read from keyboard maybe more than one — lots to read? B: read from fjle maybe more than one — opening fjle + lots to read? B: write to screen maybe more than one — lots to write? (3 or more from B) 33

  22. counting context switches B makes system call to read from keyboard (1) switch to A while B waits keyboard input: B can run (2) switch to B to handle input B makes system call to read from fjle (3?) switch to A while waiting for disk? if data from fjle not available right away (4) switch to B to do computation + write system call + maybe switch between A + B while both are computing? 34

  23. xv6 context switch and saving restore B’s user regs call swtch() in A; return from swtch() in B what if no space left? what if stack pointer invalid? use kernel stack to avoid disrupting user stack haven’t decided whether to context switch when saving user registers here… from kernel stack exit trap handler user mode swtch() — switch kernel stacks/kernel registers to kernel stack save A’s user regs start trap handler kernel mode 35 running A running B

  24. xv6 context switch and saving restore B’s user regs call swtch() in A; return from swtch() in B what if no space left? what if stack pointer invalid? use kernel stack to avoid disrupting user stack haven’t decided whether to context switch when saving user registers here… from kernel stack exit trap handler user mode swtch() — switch kernel stacks/kernel registers to kernel stack save A’s user regs start trap handler kernel mode 36 running A running B

  25. xv6 context switch and saving restore B’s user regs call swtch() in A; return from swtch() in B what if no space left? what if stack pointer invalid? use kernel stack to avoid disrupting user stack haven’t decided whether to context switch when saving user registers here… from kernel stack exit trap handler user mode swtch() — switch kernel stacks/kernel registers to kernel stack save A’s user regs start trap handler kernel mode 37 running A running B

  26. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 39

  27. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 39

  28. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 39

  29. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 39

  30. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 40

  31. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 40

  32. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 40

  33. xv6: where the context is save/restore B kernel stack pointer … ‘B’ process control block save/restore on trap() entry/exit on swtch() B’s saved kernel registers args to swtch() memory used to run process A memory accessable when running process A (= address space) ‘B’ kernel stack … ‘A’ process A’s saved user registers address space ‘B’ process address space kernel-only memory … ‘A’ user stack … B’s saved user registers A’s saved kernel registers ‘A’ kernel stack A’s kernel stack pointer … ‘A’ process control block … ‘B’ user stack 40

  34. swtch prototype save current context into *old start running context from new trick: struct context* = thread’s stack pointer top of stack contains saved registers, etc. 41 void swtch( struct context **old, struct context *new);

  35. swtch prototype save current context into *old start running context from new trick: struct context* = thread’s stack pointer top of stack contains saved registers, etc. 41 void swtch( struct context **old, struct context *new);

  36. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  37. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  38. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  39. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  40. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  41. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 42 /* switch from A to B */ swtch(&(a − >context), b − >context); /* returns to (2) */ /* later on switch back to A */ swtch(&(b − >context), a − >context) /* returns to (4) */

  42. thread switching in xv6: how? SP new (B) stack SP SP SP struct context (saved into A arg) SP swtch return addr. SP SP saved user regs old (A) stack saved user regs new (B) stack callee-saved registers swtch arguments swtch(A, B) pseudocode: read+use swtch return address from stack save caller-saved registers to stack write swtch return address to stack write all callee-saved registers to stack save old stack pointer into arg A read B arg as new stack pointer read all callee-saved registers from stack restore caller-saved registers from stack caller-saved registers … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack … 43

  43. old (A) stack new (B) stack thread switching in xv6: how? (saved into A arg) callee-saved registers SP SP SP struct context SP SP swtch(A, B) pseudocode: SP SP saved user regs saved user regs swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A read+use swtch return address from stack (x86 ret ) … restore caller-saved registers from stack caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 save caller-saved registers to stack write all callee-saved registers to stack new (B) stack read B arg as new stack pointer read all callee-saved registers from stack

  44. old (A) stack new (B) stack thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP SP (saved into A arg) struct context SP SP callee-saved registers swtch return addr. swtch arguments caller-saved registers … save caller-saved registers to stack write swtch return address to stack (x86 call ) save old stack pointer into arg A read+use swtch return address from stack (x86 ret ) restore caller-saved registers from stack … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 SP → write all callee-saved registers to stack new (B) stack read B arg as new stack pointer read all callee-saved registers from stack

  45. old (A) stack new (B) stack thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP SP (saved into A arg) struct context SP SP callee-saved registers swtch return addr. swtch arguments caller-saved registers caller-saved registers write swtch return address to stack (x86 call ) save old stack pointer into arg A read+use swtch return address from stack (x86 ret ) restore caller-saved registers from stack … … swtch arguments swtch return addr. callee-saved registers old (A) stack 43 SP → save caller-saved registers to stack write all callee-saved registers to stack new (B) stack read B arg as new stack pointer read all callee-saved registers from stack

  46. old (A) stack new (B) stack thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP SP (saved into A arg) struct context SP SP callee-saved registers swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) write all callee-saved registers to stack save old stack pointer into arg A read+use swtch return address from stack (x86 ret ) restore caller-saved registers from stack … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 SP → save caller-saved registers to stack new (B) stack read B arg as new stack pointer read all callee-saved registers from stack

  47. old (A) stack new (B) stack thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP SP (saved into A arg) struct context SP SP callee-saved registers swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A read B arg as new stack pointer read+use swtch return address from stack (x86 ret ) restore caller-saved registers from stack … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 SP → save caller-saved registers to stack write all callee-saved registers to stack new (B) stack read all callee-saved registers from stack

  48. old (A) stack new (B) stack thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP (saved into A arg) struct context SP SP SP callee-saved registers swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A read B arg as new stack pointer read all callee-saved registers from stack read+use swtch return address from stack (x86 ret ) restore caller-saved registers from stack … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 save caller-saved registers to stack write all callee-saved registers to stack new (B) stack SP →

  49. old (A) stack new (B) stack thread switching in xv6: how? struct context callee-saved registers new (B) stack SP SP SP SP (saved into A arg) swtch(A, B) pseudocode: SP SP saved user regs saved user regs swtch return addr. swtch arguments caller-saved registers restore caller-saved registers from stack write swtch return address to stack (x86 call ) save old stack pointer into arg A read all callee-saved registers from stack … read+use swtch return address from stack (x86 ret ) … caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 save caller-saved registers to stack write all callee-saved registers to stack read B arg as new stack pointer SP →

  50. old (A) stack new (B) stack thread switching in xv6: how? struct context callee-saved registers new (B) stack SP SP SP SP (saved into A arg) swtch(A, B) pseudocode: SP SP saved user regs saved user regs swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A … restore caller-saved registers from stack read+use swtch return address from stack (x86 ret ) caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 save caller-saved registers to stack write all callee-saved registers to stack read B arg as new stack pointer SP → read all callee-saved registers from stack

  51. old (A) stack new (B) stack thread switching in xv6: how? struct context callee-saved registers new (B) stack SP SP SP SP (saved into A arg) swtch(A, B) pseudocode: SP SP saved user regs saved user regs swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A … restore caller-saved registers from stack read+use swtch return address from stack (x86 ret ) caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 save caller-saved registers to stack write all callee-saved registers to stack SP → read B arg as new stack pointer read all callee-saved registers from stack

  52. thread switching in xv6: how? swtch(A, B) pseudocode: saved user regs saved user regs SP SP SP SP (saved into A arg) struct context SP SP SP new (B) stack callee-saved registers swtch return addr. swtch arguments caller-saved registers … write swtch return address to stack (x86 call ) save old stack pointer into arg A … restore caller-saved registers from stack read+use swtch return address from stack (x86 ret ) caller-saved registers swtch arguments swtch return addr. callee-saved registers old (A) stack 43 old (A) stack save caller-saved registers to stack write all callee-saved registers to stack new (B) stack read B arg as new stack pointer read all callee-saved registers from stack

  53. struct context **from_context struct context *to_context thread switching in xv6: assembly = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: saved by call of swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: .globl swtch pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks ret movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp 44

  54. thread switching in xv6: assembly esp: same as address of context = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller program counter: saved by call of swtch .globl swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: ret pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks popl %ebp movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx 44 struct context **from_context struct context *to_context

  55. struct context **from_context struct context *to_context thread switching in xv6: assembly = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: saved by call of swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: .globl swtch pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks ret movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp 44

  56. struct context **from_context struct context *to_context thread switching in xv6: assembly = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: saved by call of swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: .globl swtch pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks ret movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp 44

  57. struct context **from_context struct context *to_context thread switching in xv6: assembly = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: saved by call of swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: .globl swtch pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks ret movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp 44

  58. struct context **from_context struct context *to_context thread switching in xv6: assembly = where to fjnd new context context stored on thread’s stack context address = top of stack callee-saved registers: ebp, ebx, esi, edi other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: saved by call of swtch save stack pointer to fjrst argument (stack pointer now has all info) restore stack pointer from second argument restore program counter (and other saved registers) from stack of new thread = where to save current context two arguments: .globl swtch pushl %edi swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi # Switch stacks ret movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp 44

  59. the userspace part? user registers stored in ‘trapframe’ struct created on kernel stack when interrupt/trap happens restored before using iret to switch to user mode other code (not shown) handles setting address space 45

  60. the userspace part? user registers stored in ‘trapframe’ struct created on kernel stack when interrupt/trap happens restored before using iret to switch to user mode other code (not shown) handles setting address space 45

  61. xv6 context switch and saving restore B’s user regs call swtch() in A; return from swtch() in B what if no space left? what if stack pointer invalid? use kernel stack to avoid disrupting user stack haven’t decided whether to context switch when saving user registers here… from kernel stack exit trap handler user mode swtch() — switch kernel stacks/kernel registers to kernel stack save A’s user regs start trap handler kernel mode 46 running A running B

  62. missing pieces showed how we change kernel registers, stacks, program counter not everything: trap handler saving/restoring registers: before swtch: saving user registers before calling trap() after swtch: restoring user registers after returning from trap() changing address spaces: switchuvm changes address translation mapping changes stack pointer for HW to use for exceptions still missing: starting new thread? 47

  63. missing pieces showed how we change kernel registers, stacks, program counter not everything: trap handler saving/restoring registers: before swtch: saving user registers before calling trap() after swtch: restoring user registers after returning from trap() changing address spaces: switchuvm changes address translation mapping changes stack pointer for HW to use for exceptions still missing: starting new thread? 47

  64. exercise loop.exe’s eax stored? D. the kernel stack for the program switched to G. elsewhere C. the user stack of the program switched to F. a special register B. loop.exe’s kernel stack E. loop.exe’s heap A. loop.exe’s user stack when xv6 switches away from this program, where is the value of suppose xv6 is running this loop.exe : // goto start_loop jmp start_loop add $1, %eax start_loop: mov $0, %eax main: 48 // eax ← 0 // eax ← eax + 1

  65. exercise (alternative) loop.exe’s program counter had when it was last running in user D. the kernel stack for the program switched to G. elsewhere C. the user stack of the program switched to F. a special register B. loop.exe’s kernel stack E. loop.exe’s heap A. loop.exe’s user stack mode stored? when xv6 switches away from this program, where is the value suppose xv6 is running this loop.exe : // goto start_loop jmp start_loop add $1, %eax start_loop: mov $0, %eax main: 49 // eax ← 0 // eax ← eax + 1

  66. what about switching to a new thread? fjrst call to swtch? one thread calls swtch and …return from another thread’s call to swtch …using information on that thread’s stack trick: setup stack as if in the middle of swtch write saved registers + return address onto stack avoids special code to swtch to new thread (in exchange for special code to create thread) 50

  67. fjrst call to swtch? one thread calls swtch and …return from another thread’s call to swtch …using information on that thread’s stack trick: setup stack as if in the middle of swtch write saved registers + return address onto stack avoids special code to swtch to new thread (in exchange for special code to create thread) 50 what about switching to a new thread?

  68. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. 51 p is new struct proc ... struct proc ≈ process p − >kstack is its new stack sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  69. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  70. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  71. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  72. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  73. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  74. creating a new thread assembly code to return to user mode as if there was an interrupt) return address = trapret (for forkret) return address = forkret (for swtch) saved kernel registers (for swtch) new kernel stack same code as for syscall returns ‘trapframe’ initial code to run when starting a new process ( fork = process creation system call) saved registers (incl. return address) for swtch to pop ofg the stack new stack says: this thread is in middle of calling swtch in the middle of a system call (saved userspace registers (for the kernel only) static struct proc* *(uint*)sp = (uint)trapret; allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. p >kstack is its new stack 51 struct proc ... process p is new struct proc sp = p − >kstack + KSTACKSIZE; sp − = sizeof *p − >tf; p − >tf = ( struct trapframe*)sp; sp − = 4; sp − = sizeof *p − >context; p − >context = ( struct context*)sp; memset(p − >context, 0, sizeof *p − >context); p − >context − >eip = (uint)forkret;

  75. process control block some data structure needed to represent a process xv6: struct proc 52 called Process Control Block

  76. process control block some data structure needed to represent a process xv6: struct proc 52 called Process Control Block

  77. xv6: struct proc if waiting, }; pointers to current registers/PC of process (user and kernel) stored on its kernel stack (if not currently running) thread’s state the kernel stack for this process every process has one kernel stack is process running? or waiting? or fjnished? waiting for what ( chan )? char name[16]; enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; process ID to identify process in system calls information about address space pgdir — used by processor sz — used by OS only information about open fjles, etc. // Process name (debugging) // Current directory struct proc { // Process ID uint sz; // Size of process memory (bytes) // Page table // Bottom of kernel stack for this process enum procstate state; // Process state int pid; // Parent process // Trap frame for current syscall // swtch() here to run process // If non-zero, sleeping on chan int killed; // If non-zero, have been killed // Open files 53 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  78. xv6: struct proc if waiting, }; pointers to current registers/PC of process (user and kernel) stored on its kernel stack (if not currently running) the kernel stack for this process every process has one kernel stack is process running? or waiting? or fjnished? waiting for what ( chan )? char name[16]; enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; process ID to identify process in system calls information about address space pgdir — used by processor sz — used by OS only information about open fjles, etc. // Process name (debugging) // Current directory struct proc { // Trap frame for current syscall uint sz; // Size of process memory (bytes) // Page table // Bottom of kernel stack for this process enum procstate state; // Process state int pid; // Process ID // Parent process // swtch() here to run process // If non-zero, sleeping on chan int killed; // If non-zero, have been killed // Open files 53 pde_t* pgdir; char *kstack; ≈ thread’s state struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  79. xv6: struct proc if waiting, }; pointers to current registers/PC of process (user and kernel) stored on its kernel stack (if not currently running) thread’s state the kernel stack for this process every process has one kernel stack is process running? or waiting? or fjnished? waiting for what ( chan )? char name[16]; enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; process ID to identify process in system calls information about address space pgdir — used by processor sz — used by OS only information about open fjles, etc. // Process name (debugging) // Current directory struct proc { // Process ID uint sz; // Size of process memory (bytes) // Page table // Bottom of kernel stack for this process enum procstate state; // Process state int pid; // Parent process // Trap frame for current syscall // swtch() here to run process // If non-zero, sleeping on chan int killed; // If non-zero, have been killed // Open files 53 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

Recommend


More recommend