system calls excpetions context switches
play

system calls/excpetions/context switches 1 Changelog Changes made - PowerPoint PPT Presentation

system calls/excpetions/context switches 1 Changelog Changes made in this version not seen in fjrst lecture: contexts (B running) adjust remark about where values are saved to note that As user regs are saved on As kernel stack rather


  1. trap( struct trapframe *tf) write syscall in xv6: interrupt table setup jmp alltraps 1: do not disable interrupts during syscalls for other types of exceptions (e.g. I/O), disable interrupts (to make OS code that handles I/O, timers, etc. much simpler) vectors[T_SYSCALL] — OS function for processor to run set to pointer to assembly function vector64 trap returns to alltraps alltraps restores registers from tf , then returns to user-mode vector64: pushl $0 pushl $64 vectors.S ... meaning: run in kernel mode alltraps: ... call trap ... iret trapasm.S void { ... trap.c (yes, code segments specifjes more than that — nothing we care about) set it to use the kernel “code segment” ... (otherwise: triggers fault like privileged instruction) lidt(idt, sizeof (idt)); ... ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type (from mmu.h): // Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap gate, 0 for an interrupt gate. // interrupt gate clears FL_IF, trap gate leaves FL_IF alone // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ set the T_SYSCALL (= 0x40 ) interrupt to be callable from user mode via int instruction 7 SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);

  2. trap( struct trapframe *tf) write syscall in xv6: interrupt table setup jmp alltraps 1: do not disable interrupts during syscalls for other types of exceptions (e.g. I/O), disable interrupts (to make OS code that handles I/O, timers, etc. much simpler) vectors[T_SYSCALL] — OS function for processor to run set to pointer to assembly function vector64 trap returns to alltraps alltraps restores registers from tf , then returns to user-mode vector64: pushl $0 pushl $64 vectors.S ... meaning: run in kernel mode alltraps: ... call trap ... iret trapasm.S void { ... trap.c (yes, code segments specifjes more than that — nothing we care about) set it to use the kernel “code segment” ... (otherwise: triggers fault like privileged instruction) lidt(idt, sizeof (idt)); ... ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type (from mmu.h): // Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap gate, 0 for an interrupt gate. // interrupt gate clears FL_IF, trap gate leaves FL_IF alone // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ set the T_SYSCALL (= 0x40 ) interrupt to be callable from user mode via int instruction 7 SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);

  3. trap( struct trapframe *tf) write syscall in xv6: interrupt table setup jmp alltraps 1: do not disable interrupts during syscalls for other types of exceptions (e.g. I/O), disable interrupts (to make OS code that handles I/O, timers, etc. much simpler) vectors[T_SYSCALL] — OS function for processor to run set to pointer to assembly function vector64 trap returns to alltraps alltraps restores registers from tf , then returns to user-mode vector64: pushl $0 pushl $64 vectors.S ... meaning: run in kernel mode alltraps: ... call trap ... iret trapasm.S void { ... trap.c (yes, code segments specifjes more than that — nothing we care about) set it to use the kernel “code segment” ... (otherwise: triggers fault like privileged instruction) lidt(idt, sizeof (idt)); ... ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type (from mmu.h): // Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap gate, 0 for an interrupt gate. // interrupt gate clears FL_IF, trap gate leaves FL_IF alone // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ set the T_SYSCALL (= 0x40 ) interrupt to be callable from user mode via int instruction 7 SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);

  4. write syscall in xv6: interrupt table setup jmp alltraps (yes, code segments specifjes more than that — nothing we care about) 1: do not disable interrupts during syscalls for other types of exceptions (e.g. I/O), disable interrupts (to make OS code that handles I/O, timers, etc. much simpler) vectors[T_SYSCALL] — OS function for processor to run set to pointer to assembly function vector64 trap returns to alltraps alltraps restores registers from tf , then returns to user-mode vector64: pushl $0 pushl $64 ... ... vectors.S alltraps: ... call trap ... iret trapasm.S void { ... trap.c meaning: run in kernel mode set it to use the kernel “code segment” (otherwise: triggers fault like privileged instruction) be callable from user mode via int instruction lidt(idt, sizeof (idt)); ... SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type (from mmu.h): // Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap gate, 0 for an interrupt gate. // interrupt gate clears FL_IF, trap gate leaves FL_IF alone // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ set the T_SYSCALL (= 0x40 ) interrupt to 7 trap( struct trapframe *tf)

  5. interrupt type, application registers, … write syscall in xv6: the trap function } what operation to do for program uses myproc()->tf to determine syscall() — actual implementations much more on this later in semester represents currently running process myproc() — pseudo-global variable example: tf >eax = old value of eax struct trapframe — set by assembly trap.c ... void } return ; exit(); syscall(); exit(); { 8 trap( struct trapframe *tf) if (tf − >trapno == T_SYSCALL){ if (myproc() − >killed) myproc() − >tf = tf; if (myproc() − >killed)

  6. write syscall in xv6: the trap function void what operation to do for program uses myproc()->tf to determine syscall() — actual implementations much more on this later in semester represents currently running process myproc() — pseudo-global variable struct trapframe — set by assembly trap.c } ... } return ; exit(); syscall(); exit(); { 8 trap( struct trapframe *tf) interrupt type, application registers, … if (tf − >trapno == T_SYSCALL){ example: tf − >eax = old value of eax if (myproc() − >killed) myproc() − >tf = tf; if (myproc() − >killed)

  7. interrupt type, application registers, … write syscall in xv6: the trap function } what operation to do for program uses myproc()->tf to determine syscall() — actual implementations much more on this later in semester represents currently running process myproc() — pseudo-global variable example: tf >eax = old value of eax struct trapframe — set by assembly trap.c ... void } return ; exit(); syscall(); exit(); { 8 trap( struct trapframe *tf) if (tf − >trapno == T_SYSCALL){ if (myproc() − >killed) myproc() − >tf = tf; if (myproc() − >killed)

  8. interrupt type, application registers, … write syscall in xv6: the trap function } what operation to do for program uses myproc()->tf to determine syscall() — actual implementations much more on this later in semester represents currently running process myproc() — pseudo-global variable example: tf >eax = old value of eax struct trapframe — set by assembly trap.c ... void } return ; exit(); syscall(); exit(); { 8 trap( struct trapframe *tf) if (tf − >trapno == T_SYSCALL){ if (myproc() − >killed) myproc() − >tf = tf; if (myproc() − >killed)

  9. write syscall in xv6: the syscall function } else { copies tf >eax into %eax ) (assembly code this returns to result assigned to eax store result in user’s eax register call sys_…function from table (if system call number in range) ‘ [number] value ’: syscalls[number] = value array of functions — one for syscall syscall.c ... if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { static int (*syscalls[])( void ) = { ... { syscall( void ) void ... }; ... sys_write, [SYS_write] ... 9 num = curproc − >tf − >eax; curproc − >tf − >eax = syscalls[num]();

  10. write syscall in xv6: the syscall function } else { copies tf >eax into %eax ) (assembly code this returns to result assigned to eax store result in user’s eax register call sys_…function from table (if system call number in range) ‘ [number] value ’: syscalls[number] = value array of functions — one for syscall syscall.c ... if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { static int (*syscalls[])( void ) = { ... { syscall( void ) void ... }; ... sys_write, [SYS_write] ... 9 num = curproc − >tf − >eax; curproc − >tf − >eax = syscalls[num]();

  11. write syscall in xv6: the syscall function } else { copies tf >eax into %eax ) (assembly code this returns to result assigned to eax store result in user’s eax register call sys_…function from table (if system call number in range) ‘ [number] value ’: syscalls[number] = value array of functions — one for syscall syscall.c ... if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { static int (*syscalls[])( void ) = { ... { syscall( void ) void ... }; ... sys_write, [SYS_write] ... 9 num = curproc − >tf − >eax; curproc − >tf − >eax = syscalls[num]();

  12. write syscall in xv6: the syscall function static int (*syscalls[])( void ) = { (assembly code this returns to result assigned to eax store result in user’s eax register call sys_…function from table (if system call number in range) ‘ [number] value ’: syscalls[number] = value array of functions — one for syscall syscall.c ... } else { if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { ... { syscall( void ) void ... }; ... sys_write, [SYS_write] ... 9 copies tf − >eax into %eax ) num = curproc − >tf − >eax; curproc − >tf − >eax = syscalls[num]();

  13. write syscall in xv6: sys_write } (the terminal counts as a fjle) actual internal function that implements writing to a fjle (note: 32-bit x86 calling convention puts all args on stack) (more on this later) returns -1 on error (e.g. stack pointer invalid) utility functions that read arguments from user’s stack sysfjle.c return filewrite(f, p, n); int if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) int n; { sys_write( void ) 10 struct file *f; char *p; return − 1;

  14. write syscall in xv6: sys_write } (the terminal counts as a fjle) actual internal function that implements writing to a fjle (note: 32-bit x86 calling convention puts all args on stack) (more on this later) returns -1 on error (e.g. stack pointer invalid) utility functions that read arguments from user’s stack sysfjle.c return filewrite(f, p, n); int if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) int n; { sys_write( void ) 10 struct file *f; char *p; return − 1;

  15. write syscall in xv6: sys_write } (the terminal counts as a fjle) actual internal function that implements writing to a fjle (note: 32-bit x86 calling convention puts all args on stack) (more on this later) returns -1 on error (e.g. stack pointer invalid) utility functions that read arguments from user’s stack sysfjle.c return filewrite(f, p, n); int if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) int n; { sys_write( void ) 10 struct file *f; char *p; return − 1;

  16. write syscall in xv6: interrupt table setup jmp alltraps (yes, code segments specifjes more than that — nothing we care about) 1: do not disable interrupts during syscalls for other types of exceptions (e.g. I/O), disable interrupts (to make OS code that handles I/O, timers, etc. much simpler) vectors[T_SYSCALL] — OS function for processor to run set to pointer to assembly function vector64 trap returns to alltraps alltraps restores registers from tf , then returns to user-mode vector64: pushl $0 pushl $64 ... ... vectors.S alltraps: ... call trap ... iret trapasm.S void { ... trap.c meaning: run in kernel mode set it to use the kernel “code segment” (otherwise: triggers fault like privileged instruction) be callable from user mode via int instruction lidt(idt, sizeof (idt)); ... SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type (from mmu.h): // Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap gate, 0 for an interrupt gate. // interrupt gate clears FL_IF, trap gate leaves FL_IF alone // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ set the T_SYSCALL (= 0x40 ) interrupt to 11 trap( struct trapframe *tf)

  17. write syscall in xv6: summary write function — syscall wrapper uses int $0x40 interrupt table entry setup points to assembly function vector64 (and switches to kernel stack) …which calls trap() with trap number set to 64 ( T_SYSCALL ) (after saving all registers into struct trapframe ) …which checks trap number, then calls syscall() …which checks syscall number (from eax) …and uses it to call sys_write …which reads arguments from the stack and does the write …then registers restored, return to user space 13

  18. write syscall in xv6: summary write function — syscall wrapper uses int $0x40 interrupt table entry setup points to assembly function vector64 (and switches to kernel stack) …which calls trap() with trap number set to 64 ( T_SYSCALL ) (after saving all registers into struct trapframe ) …which checks trap number, then calls syscall() …which checks syscall number (from eax) …and uses it to call sys_write …then registers restored, return to user space 14 …which reads arguments from the stack and does the write

  19. recall: address translation Program B code = kernel-mode only trigger error real memory … OS data Program B data Program A data Program A code Program A (set by OS) mapping (set by OS) mapping addresses Program B addresses 16

  20. xv6 memory layout larger addresses are for kernel is active on a processor as part of switching which processes change which one exceptions use one kernel stack per process in special “task state selector” location of stack stored when execption/interrupt/…happens processor switches stacks kernel stack allocated here smaller addresses are for applications (accessible in kernel mode only ) 17 Virtual 4 Gig Device memory RW- 0xFE000000 Unused if less than 2 Gig of physical memory Free memory RW-- end Kernel data RW- Kernel text R-- + 0x100000 Physical RW- 4 Gig KERNBASE Memory-mapped 32-bit I/O devices PHYSTOP Unused if less than 2 Gig of physical memory Program data & heap At most 2 Gig Extended memory PAGESIZE User stack RWU 0x100000 User data RWU I/O space 640K User text RWU Base memory 0 0

  21. xv6 memory layout larger addresses are for kernel is active on a processor as part of switching which processes change which one exceptions use one kernel stack per process in special “task state selector” location of stack stored when execption/interrupt/…happens processor switches stacks kernel stack allocated here smaller addresses are for applications (accessible in kernel mode only ) 17 Virtual 4 Gig Device memory RW- 0xFE000000 Unused if less than 2 Gig of physical memory Free memory RW-- end Kernel data RW- Kernel text R-- + 0x100000 Physical RW- 4 Gig KERNBASE Memory-mapped 32-bit I/O devices PHYSTOP Unused if less than 2 Gig of physical memory Program data & heap At most 2 Gig Extended memory PAGESIZE User stack RWU 0x100000 User data RWU I/O space 640K User text RWU Base memory 0 0

  22. xv6 memory layout larger addresses are for kernel is active on a processor as part of switching which processes change which one exceptions use one kernel stack per process in special “task state selector” location of stack stored when execption/interrupt/…happens processor switches stacks kernel stack allocated here smaller addresses are for applications (accessible in kernel mode only ) 17 Virtual 4 Gig Device memory RW- 0xFE000000 Unused if less than 2 Gig of physical memory Free memory RW-- end Kernel data RW- Kernel text R-- + 0x100000 Physical RW- 4 Gig KERNBASE Memory-mapped 32-bit I/O devices PHYSTOP Unused if less than 2 Gig of physical memory Program data & heap At most 2 Gig Extended memory PAGESIZE User stack RWU 0x100000 User data RWU I/O space 640K User text RWU Base memory 0 0

  23. xv6 memory layout larger addresses are for kernel is active on a processor as part of switching which processes change which one exceptions use one kernel stack per process in special “task state selector” location of stack stored when execption/interrupt/…happens processor switches stacks kernel stack allocated here smaller addresses are for applications (accessible in kernel mode only ) 17 Virtual 4 Gig Device memory RW- 0xFE000000 Unused if less than 2 Gig of physical memory Free memory RW-- end Kernel data RW- Kernel text R-- + 0x100000 Physical RW- 4 Gig KERNBASE Memory-mapped 32-bit I/O devices PHYSTOP Unused if less than 2 Gig of physical memory Program data & heap At most 2 Gig Extended memory PAGESIZE User stack RWU 0x100000 User data RWU I/O space 640K User text RWU Base memory 0 0

  24. aside: nested exceptions x86 switches to kernel stack on exception… assuming it’s switching to kernel mode system call or timer interrupt in user mode start at top of kernel stack timer interrupt during system call continue using current kernel stack 18

  25. write syscall in xv6: summary write function — syscall wrapper uses int $0x40 interrupt table entry setup points to assembly function vector64 …which calls trap() with trap number set to 64 ( T_SYSCALL ) (after saving all registers into struct trapframe ) …which checks trap number, then calls syscall() …which checks syscall number (from eax) …and uses it to call sys_write …which reads arguments from the stack and does the write …then registers restored, return to user space 19 (and switches to kernel stack)

  26. non-system call exceptions xv6: there are traps other than system calls timer interrupt — every hardware “tick” action: schedule new process faults — e.g. access invalid memory I/O — handle I/O 20

  27. aside: interrupt descriptor table x86’s interrupt descriptor table has an entry for each kind of exception segmentation fault timer expired (“your program ran too long”) divide-by-zero system calls … shown earlier: being set for syscalls — SETGATE macro …and they always call the trap() function xv6 design choice: could have separate functions for each 21 xv6 sets all the table entries

  28. write syscall in xv6: interrupt table setup ... lidt(idt, sizeof (idt)); 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) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type 22

  29. write syscall in xv6: interrupt table setup ... lidt(idt, sizeof (idt)); for ( int i = 0; i < 256; i++) SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0); ... trap.c (run on boot) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type 22 SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);

  30. write syscall in xv6: interrupt table setup ... lidt(idt, sizeof (idt)); 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) lidt — function (in x86.h) wrapping lidt instruction sets the interrupt descriptor table table of handler functions for each interrupt type 22

  31. non-system call exceptions xv6: there are traps other than system calls timer interrupt — every hardware “tick” action: schedule new process faults — e.g. access invalid memory I/O — handle I/O 23

  32. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  33. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  34. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  35. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  36. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  37. xv6: timer interrupt void process was just about to stop running check state == RUNNING in case myproc() retrieves running process 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 processes waiting a yield — maybe context switch } ... yield(); ... // Force process to give up CPU on clock tick. ticks++; { case T_IRQ0 + IRQ_TIMER: if (cpuid() == 0){ acquire(&tickslock); wakeup(&ticks); release(&tickslock); } lapiceoi(); break ; ... 24 trap( struct trapframe *tf) switch (tf − >trapno){ if (myproc() && myproc() − >state == RUNNING && tf − >trapno == T_IRQ0+IRQ_TIMER)

  38. non-system call exceptions xv6: there are traps other than system calls timer interrupt — every hardware “tick” action: schedule new process faults — e.g. access invalid memory I/O — handle I/O 25

  39. xv6: faults 0x%x--kill void %d " "eip 0x%x addr proc\n", %d } } unknown exception print message and kill running program assume it screwed up prints out trap number can lookup in traps.h on cpu err %s: { ... ... default : ... cprintf("pid %d 26 trap %d trap( struct trapframe *tf) switch (tf − >trapno) { ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ myproc() − >pid, myproc() − >name, tf − >trapno, tf − >err, cpuid(), tf − >eip, rcr2()); myproc() − >killed = 1;

  40. xv6: faults 0x%x--kill void %d " "eip 0x%x addr proc\n", %d } } unknown exception print message and kill running program assume it screwed up prints out trap number can lookup in traps.h on cpu err %s: { ... ... default : ... cprintf("pid %d 26 trap %d trap( struct trapframe *tf) switch (tf − >trapno) { ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ myproc() − >pid, myproc() − >name, tf − >trapno, tf − >err, cpuid(), tf − >eip, rcr2()); myproc() − >killed = 1;

  41. non-system call exceptions xv6: there are traps other than system calls timer interrupt — every hardware “tick” action: schedule new process faults — e.g. access invalid memory I/O — handle I/O 27

  42. xv6: I/O kbdintr(); uart = serial port (external terminal) kbd = keyboard ide = disk interface break ; lapiceoi(); uartintr(); case T_IRQ0 + IRQ_COM1: break ; lapiceoi(); case T_IRQ0 + IRQ_KBD: void ... break ; lapiceoi(); ideintr(); case T_IRQ0 + IRQ_IDE: ... ... { 28 trap( struct trapframe *tf) switch (tf − >trapno) {

  43. xv6: keyboard I/O ... (xv6 choice: usually not immediately) make it run soon fjnds process waiting on console } ... wakeup(&input.r); { void void consoleintr(...) ... } consoleintr(kbdgetc); { kbdintr( void ) 29

  44. xv6: keyboard I/O ... (xv6 choice: usually not immediately) make it run soon fjnds process waiting on console } ... wakeup(&input.r); { void void consoleintr(...) ... } consoleintr(kbdgetc); { kbdintr( void ) 29

  45. timing nothing long times[NUM_TIMINGS]; int main( void ) { for ( int i = 0; i < N; ++i) { long start, end; /* do nothing */ end = get_time(); } output_timings(times); } 30 start = get_time(); times[i] = end - start; same instructions — same difgerence each time?

  46. doing nothing on a busy system 31 time for empty loop body 10 8 10 7 10 6 time (ns) 10 5 10 4 10 3 10 2 10 1 0 200000 400000 600000 800000 1000000 sample #

  47. doing nothing on a busy system 32 time for empty loop body 10 8 10 7 10 6 time (ns) 10 5 10 4 10 3 10 2 10 1 0 200000 400000 600000 800000 1000000 sample #

  48. time multiplexing // whatever get_time does ... subq %rbp, %rax // whatever get_time does call get_time million cycle delay movq %rax, %rbp call get_time loop.exe ... time CPU: ssh.exe loop.exe firefox.exe ssh.exe 33

  49. time multiplexing // whatever get_time does ... subq %rbp, %rax // whatever get_time does call get_time million cycle delay movq %rax, %rbp call get_time loop.exe ... time CPU: ssh.exe loop.exe firefox.exe ssh.exe 33

  50. time multiplexing // whatever get_time does ... subq %rbp, %rax // whatever get_time does call get_time million cycle delay movq %rax, %rbp call get_time loop.exe ... time CPU: ssh.exe loop.exe firefox.exe ssh.exe 33

  51. time multiplexing really loop.exe ssh.exe firefox.exe loop.exe ssh.exe = operating system exception happens return from exception 34

  52. time multiplexing really loop.exe ssh.exe firefox.exe loop.exe ssh.exe = operating system exception happens return from exception 34

  53. 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 saved information called context 35 called context switch

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

  55. 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 37

  56. 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 38

  57. 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 38

  58. context switch in xv6 xv6 context switch has two parts switching threads switching user address spaces + kernel stack to use for exception kernel part of address space same for every process (simplifjes address space switching) each process has its own kernel stack 39

  59. context switch in xv6 xv6 context switch has two parts switching threads kernel part of address space same for every process (simplifjes address space switching) each process has its own kernel stack 39 switching user address spaces + kernel stack to use for exception

  60. context switch in xv6 xv6 context switch has two parts switching threads kernel part of address space same for every process (simplifjes address space switching) each process has its own kernel stack 39 switching user address spaces + kernel stack to use for exception

  61. thread switching yes, it looks like we’re missing switch to context new set old to point to it allocate space for context on top of stack function to switch contexts eip = saved program counter some registers we need… structure to save context in struct context { } uint eip; uint ebp; uint ebx; uint esi; uint edi; 40 void swtch( struct context **old, struct context * new );

  62. thread switching yes, it looks like we’re missing switch to context new set old to point to it allocate space for context on top of stack function to switch contexts eip = saved program counter some registers we need… structure to save context in struct context { } uint eip; uint ebp; uint ebx; uint esi; uint edi; 40 void swtch( struct context **old, struct context * new );

  63. thread switching yes, it looks like we’re missing switch to context new set old to point to it allocate space for context on top of stack function to switch contexts eip = saved program counter some registers we need… structure to save context in struct context { } uint eip; uint ebp; uint ebx; uint esi; uint edi; 40 void swtch( struct context **old, struct context * new );

  64. thread switching yes, it looks like we’re missing switch to context new set old to point to it allocate space for context on top of stack function to switch contexts eip = saved program counter some registers we need… structure to save context in struct context { } uint eip; uint ebp; uint ebx; uint esi; uint edi; 40 void swtch( struct context **old, struct context * new );

  65. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  66. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  67. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  68. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  69. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  70. thread switching in xv6: C swtch(...); ... ... // (3) ... ... // (2) in thread A: // (0) -- called earlier in thread B: ... // (4) ... // (1) 41 /* 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) */

  71. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: set 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 new context = 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 42

  72. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller program counter: set 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 new context = 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 42 struct context **from_context struct context *to_context

  73. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: set 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 new context = 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 42

  74. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: set 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 new context = 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 42

  75. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: set 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 new context = 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 42

  76. 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 saved: ebp, ebx, esi, edi what about other parts of context? eax, ecx, …: saved by swtch’s caller esp: same as address of context program counter: set 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 new context = 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 42

  77. juggling stacks %esp swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp %esp caller-saved registers fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack swtch arguments from stack .globl swtch movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) # Load new callee-save registers saved edi popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx saved esi 43

  78. juggling stacks %esp swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp .globl swtch fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack caller-saved registers from stack saved edi movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) # Load new callee-save registers saved esi popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx 43 %esp →

  79. juggling stacks %esp swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp .globl swtch fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack caller-saved registers from stack saved edi movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) # Load new callee-save registers saved esi popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx 43 %esp →

  80. juggling stacks %esp swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp .globl swtch fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack caller-saved registers from stack saved edi movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) # Load new callee-save registers saved esi popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx 43 ← %esp

  81. juggling stacks %esp swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp .globl swtch fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack caller-saved registers from stack saved edi movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) # Load new callee-save registers saved esi popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx 43 ← %esp

  82. juggling stacks %esp swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi to stack %esp %esp %esp %esp .globl swtch fjrst instruction executed by new thread bottom of new kernel stack saved user regs … from stack saved user regs … to stack caller-saved registers from stack saved edi movl %edx, %esp swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) 43 saved esi # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp ret caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx ← %esp

  83. fjrst call to swtch? one thread calls swtch and …return from another thread’s call to swtch 44 what about switching to a new thread?

  84. creating a new thread (for swtch) ‘trapframe’ (saved userspace registers as if there was an interrupt) return address = trapret (for forkret) return address = forkret saved kernel registers static struct proc* (for swtch) new kernel stack assembly code to return to user mode same code as for syscall returns new stack says: this thread is in middle of calling swtch in the middle of a system call (for the kernel only) p is new struct proc 45 // Set up new context to start executing at forkret, allocproc( void ) { ... // Leave room for trap frame. // which returns to trapret. ... *(uint*)sp = (uint)trapret; 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;

  85. creating a new thread (for swtch) (for the kernel only) ‘trapframe’ (saved userspace registers as if there was an interrupt) return address = trapret (for forkret) return address = forkret saved kernel registers p is new struct proc (for swtch) new kernel stack assembly code to return to user mode same code as for syscall returns new stack says: this thread is in middle of calling swtch in the middle of a system call p >kstack is its new stack process static struct proc* struct proc allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. *(uint*)sp = (uint)trapret; ... 45 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;

  86. creating a new thread (for swtch) (for the kernel only) ‘trapframe’ (saved userspace registers as if there was an interrupt) return address = trapret (for forkret) return address = forkret saved kernel registers p is new struct proc (for swtch) new kernel stack assembly code to return to user mode same code as for syscall returns new stack says: this thread is in middle of calling swtch in the middle of a system call p >kstack is its new stack process static struct proc* struct proc allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. *(uint*)sp = (uint)trapret; ... 45 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;

  87. creating a new thread (for swtch) (for the kernel only) ‘trapframe’ (saved userspace registers as if there was an interrupt) return address = trapret (for forkret) return address = forkret saved kernel registers p is new struct proc (for swtch) new kernel stack assembly code to return to user mode same code as for syscall returns new stack says: this thread is in middle of calling swtch in the middle of a system call p >kstack is its new stack process static struct proc* struct proc allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. *(uint*)sp = (uint)trapret; ... 45 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;

  88. creating a new thread (for swtch) (for the kernel only) ‘trapframe’ (saved userspace registers as if there was an interrupt) return address = trapret (for forkret) return address = forkret saved kernel registers p is new struct proc (for swtch) new kernel stack assembly code to return to user mode same code as for syscall returns new stack says: this thread is in middle of calling swtch in the middle of a system call p >kstack is its new stack process static struct proc* struct proc allocproc( void ) { ... // Leave room for trap frame. // Set up new context to start executing at forkret, // which returns to trapret. *(uint*)sp = (uint)trapret; ... 45 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;

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend