context switches posix api
play

context switches/POSIX API 1 Changelog Changes made in this - PowerPoint PPT Presentation

context switches/POSIX API 1 Changelog Changes made in this version not seen in fjrst lecture: 24 Jan 2019: wait: return value for WNOHANG when process not done is 0, not -1 1 last time system call fmow in xv6 other exceptions in xv6


  1. 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 8

  2. 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 8

  3. 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 9

  4. 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 9 %esp →

  5. 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 9 %esp →

  6. 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 9 ← %esp

  7. 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 9 ← %esp

  8. 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) 9 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

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

  10. 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 11 // 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;

  11. 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; ... 11 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;

  12. 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; ... 11 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;

  13. 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; ... 11 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;

  14. 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; ... 11 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;

  15. 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; ... 11 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;

  16. 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 saved esi 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) movl %edx, %esp # 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 12 ← %esp

  17. kernel-space context switch summary swtch function saves registers on current kernel stack switches to new kernel stack and restores its registers initial setup — manually construct stack values 13

  18. 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 14

  19. 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 initial user registers created manually on stack (as if saved by system call) other code (not shown) handles setting address space 15

  20. 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 initial user registers created manually on stack (as if saved by system call) other code (not shown) handles setting address space 15

  21. where things go in context switch ‘to’ kernel stack caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi fjrst %esp value trap return addr. for ‘to’ process (argument to swtch ) main ’s return addr. main ’s vars … ‘to’ user stack %esp value after return-from-exception … saved user registers saved user registers saved edi trap return addr. … caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx saved esi ‘from’ kernel stack just before exception last %esp value for ‘from’ process (saved by swtch ) main ’s return addr. main ’s vars … ‘from’ user stack %esp value 16

  22. where things go in context switch ‘to’ kernel stack caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx saved esi saved edi fjrst %esp value trap return addr. for ‘to’ process (argument to swtch ) main ’s return addr. main ’s vars … ‘to’ user stack %esp value after return-from-exception … saved user registers saved user registers saved edi trap return addr. … caller-saved registers swtch arguments swtch return addr. saved ebp saved ebx saved esi ‘from’ kernel stack just before exception last %esp value for ‘from’ process (saved by swtch ) main ’s return addr. main ’s vars … ‘from’ user stack %esp value 16

  23. xv6 context switch and saving user mode kernel mode start trap handler save A’s user regs to kernel stack swtch() — switch kernel stacks/kernel registers exit trap handler restore B’s user regs from kernel stack 17 running A running B

  24. exceptions in exceptions alltraps: nesting might be need to know how deep how much stack space?? already in kernel mode) (same stack pointer if switching user to kernel mode x86 rule: switch stacks when call trap pushl %esp ... current kernel stack alltraps: … ecx from trap() 1st time eax from trap() 1st time ret ... movl ..., %eax call trap pushl %esp ... 18 /* in trap(): */

  25. exceptions in exceptions current kernel stack nesting might be need to know how deep how much stack space?? already in kernel mode) (same stack pointer if switching user to kernel mode x86 rule: switch stacks when call trap pushl %esp ... alltraps: … alltraps: ecx from trap() 2nd time eax from trap() 2nd time … ecx from trap() 1st time eax from trap() 1st time ret ... movl ..., %eax call trap pushl %esp ... 18 /* in trap(): */

  26. exceptions in exceptions current kernel stack nesting might be need to know how deep how much stack space?? already in kernel mode) (same stack pointer if switching user to kernel mode x86 rule: switch stacks when call trap pushl %esp ... alltraps: … alltraps: ecx from trap() 2nd time eax from trap() 2nd time … ecx from trap() 1st time eax from trap() 1st time ret ... movl ..., %eax call trap pushl %esp ... 18 /* in trap(): */

  27. exceptions in exceptions current kernel stack nesting might be need to know how deep how much stack space?? already in kernel mode) (same stack pointer if switching user to kernel mode x86 rule: switch stacks when call trap pushl %esp ... alltraps: … alltraps: ecx from trap() 2nd time eax from trap() 2nd time … ecx from trap() 1st time eax from trap() 1st time ret ... movl ..., %eax call trap pushl %esp ... 18 /* in trap(): */

  28. interrupt disabling CPU supports disabling (most) interrupts CPU has extra state: are interrupts enabled? 19 interrupts will wait until it is reenabled

  29. xv6 interrupt disabling xv6 policy: interrupts disabled in non-syscall traps this policy makes xv6 easier to code… disadvantages? slow kernel code makes system hang? gaurenteeing minimum reaction time to keypress? 20

  30. xv6 interrupt disabling xv6 policy: interrupts disabled in non-syscall traps this policy makes xv6 easier to code… disadvantages? slow kernel code makes system hang? gaurenteeing minimum reaction time to keypress? 20

  31. xv6 interrupt disabling xv6 policy: interrupts disabled in non-syscall traps this policy makes xv6 easier to code… disadvantages? slow kernel code makes system hang? gaurenteeing minimum reaction time to keypress? 20

  32. 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: 21 // eax ← 0 // eax ← eax + 1

  33. 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 … …and they always call the trap() function 22 xv6 sets all the table entries

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

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

  36. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  37. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 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;

  38. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  39. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  40. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  41. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  42. xv6: struct proc if waiting, }; current registers/PC of process (user and kernel) stored on (pointer to) 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 24 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  43. process control blocks generally contains process’s context(s) (registers, PC, …) if context is not on a CPU (in xv6: pointers to these, actual location: process’s kernel stack) process’s status — running, waiting, etc. information for system calls, etc. open fjles memory allocations process IDs related processes 25

  44. xv6 myproc xv6 function: myproc() retrieves pointer to currently running struct proc 26

  45. myproc: using a global variable struct cpu cpus[NCPU]; struct proc* myproc( void ) { ... c = mycpu(); using special "ID" register ... return p; } 27 struct cpu *c; /* finds entry of cpus array as array index */ p = c − >proc;

  46. this class: focus on Unix Unix-like OSes will be our focus we have source code used to from 2150, etc.? have been around for a while xv6 imitates Unix 28

  47. Unix history 29 1969 Unnamed PDP-7 operating system 1969 Open Source Unix 1971 to 1973 1971 to 1973 Mixed/Shared Source Version 1 to 4 1974 to 1975 Unix 1974 to 1975 Closed Source PWB/Unix Version 5 to 6 1978 No future releases 1978 BSD 1.0 to 2.0 Unix 1979 1979 Version 7 Unix/32V 1980 1980 BSD 3.0 to 4.1 Xenix 1981 System III 1981 1.0 to 2.3 1982 1982 Xenix 3.0 BSD 4.2 1983 SunOS 1983 System V 1 to 1.1 R1 to R2 1984 SCO Xenix 1984 Unix 1985 1985 Version 8 SCO Xenix System V AIX V/286 BSD 4.3 1986 1.0 R3 1986 HP-UX SunOS 1.0 to 1.2 SCO Xenix 1.2 to 3.0 Unix 1987 V/386 1987 9 and 10 HP-UX (last versions 1988 BSD 4.3 System V 1988 2.0 to 3.0 from T ahoe R4 1989 Bell Labs) SCO Xenix 1989 V/386 BSD 4.3 1990 1990 Reno BSD NET/2 1991 1991 Linux 0.0.1 SunOS 4 Minix 386BSD 1.x NexTSTEP/ 1992 OPENSTEP 1992 HP-UX 1.0 to 4.0 6 to 11 NetBSD 0.8 to 1.0 Linux SCO Unix UnixWare 1993 BSD 1993 0.95 to 1.2.x 3.2.4 1.x to 2.x 4.4 to FreeBSD 1994 4.4 lite2 (System V 1994 1.0 to R4.2) 1995 2.2.x NetBSD 1995 OpenBSD OpenServer 1.1 to 1.2 Solaris 1.0 to 2.2 5.0 to 5.04 1996 2.1 to 9 1996 1997 1997 NetBSD 1.3 1998 FreeBSD 1998 3.0 to 3.2 Minix OpenServer 1999 Mac OS X 1999 AIX 5.0.5 to 5.0.7 2.x Server 3.0-7.2 2000 2000 2001 to 2004 2001 to 2004 Linux 2005 2005 2.x UnixWare 7.x 2006 to 2007 2006 to 2007 (System V OpenBSD R5) 2.3-6.1 Solaris 2008 2008 Mac OS X, FreeBSD NetBSD 10 OS X, 3.3-11.x 1.3-7.1 OpenServer HP-UX 2009 macOS 2009 6.x 11i+ DragonFly 10.0 to 10.12 Minix BSD 2010 (Darwin 2010 3.1.0-3.4.0 1.0 to 4.8 1.2.1 to 17) OpenSolaris 2011 2011 & derivatives Linux 2012 to 2015 (illumos, etc.) 2012 to 2015 3.x Solaris 11.0-11.3 2016 2016 Linux 4.x OpenServer 2017 2017 10.x

  48. POSIX: standardized Unix Portable Operating System Interface (POSIX) “standard for Unix” current version online: http://pubs.opengroup.org/onlinepubs/9699919799/ (almost) followed by most current Unix-like OSes …but OSes add extra features …but doesn’t specify everything 30

  49. what POSIX defjnes POSIX specifjes the library and shell interface source code compatibility doesn’t care what is/is not a system call… doesn’t specify binary formats… idea: write applications for POSIX, recompile and run on all implementations this was a very important goal in the 80s/90s at the time, Linux was very immature 31

  50. POSIX process management essential operations process information: getpid process creation: fork running programs: exec* also posix_spawn (not widely supported), … waiting for processes to fjnish: waitpid (or wait ) process destruction, ‘signaling’: exit , kill 32

  51. POSIX process management essential operations process information: getpid process creation: fork running programs: exec* also posix_spawn (not widely supported), … waiting for processes to fjnish: waitpid (or wait ) process destruction, ‘signaling’: exit , kill 33

  52. getpid pid_t my_pid = getpid(); printf("my pid is %ld\n", ( long ) my_pid); 34 ␣ ␣ ␣

  53. process ids in ps cr4bd@machine:~$ ps PID TTY TIME CMD 14777 pts/3 00:00:00 bash 14798 pts/3 00:00:00 ps 35

  54. POSIX process management essential operations process information: getpid running programs: exec* also posix_spawn (not widely supported), … waiting for processes to fjnish: waitpid (or wait ) process destruction, ‘signaling’: exit , kill 36 process creation: fork

  55. fork pid_t fork() — copy the current process returns twice: in parent (original process): pid of new child process in child (new process): 0 everything (but pid) duplicated in parent, child: memory fjle descriptors (later) registers 37

  56. fork and PCBs eax=42, copy copy child process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … user regs user regs memory parent process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … eax=42, 38

  57. fork and PCBs eax=42, copy copy child process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … user regs user regs memory parent process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … eax=42, 38

  58. fork and PCBs eax=42, copy copy child process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … user regs user regs memory parent process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … eax=42, 38

  59. fork and PCBs eax=42, copy copy child process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … user regs user regs memory parent process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … eax=42, 38

  60. fork and PCBs eax=420, copy copy child process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … user regs user regs memory parent process control block … … fd 1: … fd 0: … open fjles user memory kernel stack ecx=133, … eax=42 child (new) pid , 38

  61. fork example cast in case pid_t isn’t int printf("[%d] child\n", ( int ) my_pid); perror("Fork failed"); } return 0; } getpid — returns current process pid POSIX doesn’t specify (some systems it is, some not…) #include <stdlib.h> (not necessary if you were using C++’s cout, etc.) prints out Fork failed: error message (example error message : “Resource temporarily unavailable”) from error number stored in special global variable errno Example output: Parent pid: 100 [100] parent of [432] [432] child pid_t my_pid = getpid(); 39 [%d]\n", ( int ) my_pid, ( int ) child_pid); pid_t my_pid = getpid(); #include <stdio.h> #include <unistd.h> #include <sys/types.h> pid_t pid = getpid(); printf("Parent pid: %d\n", ( int ) pid); pid_t child_pid = fork(); if (child_pid > 0) { parent printf("[%d] of int main( int argc, char *argv[]) { ␣ ␣ /* Parent Process */ ␣ ␣ ␣ } else if (child_pid == 0) { /* Child Process */ ␣ } else { ␣

  62. fork example getpid — returns current process pid printf("[%d] child\n", ( int ) my_pid); } else { perror("Fork failed"); } return 0; } cast in case pid_t isn’t int #include <stdlib.h> POSIX doesn’t specify (some systems it is, some not…) (not necessary if you were using C++’s cout, etc.) prints out Fork failed: error message (example error message : “Resource temporarily unavailable”) from error number stored in special global variable errno Example output: Parent pid: 100 [100] parent of [432] [432] child pid_t my_pid = getpid(); 39 if (child_pid > 0) { printf("[%d] printf("Parent pid: %d\n", ( int ) pid); pid_t child_pid = fork(); [%d]\n", ( int ) my_pid, ( int ) child_pid); #include <sys/types.h> pid_t my_pid = getpid(); #include <unistd.h> parent #include <stdio.h> of int main( int argc, char *argv[]) { pid_t pid = getpid(); ␣ ␣ /* Parent Process */ ␣ ␣ ␣ } else if (child_pid == 0) { /* Child Process */ ␣ ␣

  63. fork example cast in case pid_t isn’t int printf("[%d] child\n", ( int ) my_pid); perror("Fork failed"); } return 0; } getpid — returns current process pid POSIX doesn’t specify (some systems it is, some not…) #include <stdlib.h> (not necessary if you were using C++’s cout, etc.) prints out Fork failed: error message (example error message : “Resource temporarily unavailable”) from error number stored in special global variable errno Example output: Parent pid: 100 [100] parent of [432] [432] child pid_t my_pid = getpid(); 39 [%d]\n", ( int ) my_pid, ( int ) child_pid); pid_t my_pid = getpid(); #include <stdio.h> #include <unistd.h> #include <sys/types.h> pid_t pid = getpid(); printf("Parent pid: %d\n", ( int ) pid); pid_t child_pid = fork(); if (child_pid > 0) { parent printf("[%d] of int main( int argc, char *argv[]) { ␣ ␣ /* Parent Process */ ␣ ␣ ␣ } else if (child_pid == 0) { /* Child Process */ ␣ } else { ␣

  64. fork example cast in case pid_t isn’t int printf("[%d] child\n", ( int ) my_pid); perror("Fork failed"); } return 0; } getpid — returns current process pid POSIX doesn’t specify (some systems it is, some not…) #include <stdlib.h> (not necessary if you were using C++’s cout, etc.) prints out Fork failed: error message (example error message : “Resource temporarily unavailable”) from error number stored in special global variable errno Example output: Parent pid: 100 [100] parent of [432] [432] child pid_t my_pid = getpid(); 39 [%d]\n", ( int ) my_pid, ( int ) child_pid); pid_t my_pid = getpid(); #include <stdio.h> #include <unistd.h> #include <sys/types.h> pid_t pid = getpid(); printf("Parent pid: %d\n", ( int ) pid); pid_t child_pid = fork(); if (child_pid > 0) { parent printf("[%d] of int main( int argc, char *argv[]) { ␣ ␣ /* Parent Process */ ␣ ␣ ␣ } else if (child_pid == 0) { /* Child Process */ ␣ } else { ␣

  65. fork example cast in case pid_t isn’t int printf("[%d] child\n", ( int ) my_pid); perror("Fork failed"); } return 0; } getpid — returns current process pid POSIX doesn’t specify (some systems it is, some not…) #include <stdlib.h> (not necessary if you were using C++’s cout, etc.) prints out Fork failed: error message (example error message : “Resource temporarily unavailable”) from error number stored in special global variable errno Example output: Parent pid: 100 [100] parent of [432] [432] child pid_t my_pid = getpid(); 39 [%d]\n", ( int ) my_pid, ( int ) child_pid); pid_t my_pid = getpid(); #include <stdio.h> #include <unistd.h> #include <sys/types.h> pid_t pid = getpid(); printf("Parent pid: %d\n", ( int ) pid); pid_t child_pid = fork(); if (child_pid > 0) { parent printf("[%d] of int main( int argc, char *argv[]) { ␣ ␣ /* Parent Process */ ␣ ␣ ␣ } else if (child_pid == 0) { /* Child Process */ ␣ } else { ␣

  66. a fork question child Done! Child 100 Done! In child parent child parent Done! Done! In child Child 100 parent child parent parent int main() { 100 . Give two possible outputs. (Assume no crashes, etc.) Exercise: Suppose the pid of the parent process is 99 and child is } printf("Done!\n"); } %d\n", pid); printf("Child child\n"); printf("In if (pid == 0) { pid_t pid = fork(); 40 ␣ } else { ␣

  67. a fork question child Done! Child 100 Done! In child parent child parent Done! Done! In child Child 100 parent child parent parent int main() { 100 . Give two possible outputs. (Assume no crashes, etc.) Exercise: Suppose the pid of the parent process is 99 and child is } printf("Done!\n"); } %d\n", pid); printf("Child child\n"); printf("In if (pid == 0) { pid_t pid = fork(); 40 ␣ } else { ␣

  68. POSIX process management essential operations process information: getpid process creation: fork also posix_spawn (not widely supported), … waiting for processes to fjnish: waitpid (or wait ) process destruction, ‘signaling’: exit , kill 41 running programs: exec*

  69. exec* exec* — replace current program with new program * — multiple variants same pid, new process image int execv(const char *path, const char **argv) path: new program to run argv: array of arguments, termianted by null pointer 42

  70. execv example ... (but probably should match it) need not match fjrst argument fjlename of program to run used to compute argv, argc } ... } else if (child_pid > 0) { exit(1); perror("execv"); execv("/bin/ls", args); if (child_pid == 0) { child_pid = fork(); 43 /* child process */ char *args[] = {"ls", "-l", NULL}; /* execv doesn't return when it works. So, if we got here, it failed. */ /* parent process */

  71. execv example ... (but probably should match it) need not match fjrst argument fjlename of program to run used to compute argv, argc } ... } else if (child_pid > 0) { exit(1); perror("execv"); execv("/bin/ls", args); if (child_pid == 0) { child_pid = fork(); 43 /* child process */ char *args[] = {"ls", "-l", NULL}; /* execv doesn't return when it works. So, if we got here, it failed. */ /* parent process */

  72. execv example ... (but probably should match it) need not match fjrst argument fjlename of program to run used to compute argv, argc } ... } else if (child_pid > 0) { exit(1); perror("execv"); execv("/bin/ls", args); if (child_pid == 0) { child_pid = fork(); 43 /* child process */ char *args[] = {"ls", "-l", NULL}; /* execv doesn't return when it works. So, if we got here, it failed. */ /* parent process */

  73. exec and PCBs memory discarded old memory (more on this later) not changed! copy arguments new stack, heap, … executable fjle loaded from the process control block user regs … … fd 1: … fd 0: (terminal …) open fjles user memory kernel stack ecx=133 init. val. , … eax=42 init. val. , 44

  74. exec and PCBs memory discarded old memory (more on this later) not changed! copy arguments new stack, heap, … executable fjle loaded from the process control block user regs … … fd 1: … fd 0: (terminal …) open fjles user memory kernel stack ecx=133 init. val. , … eax=42 init. val. , 44

  75. exec and PCBs memory discarded old memory (more on this later) not changed! copy arguments new stack, heap, … executable fjle loaded from the process control block user regs … … fd 1: … fd 0: (terminal …) open fjles user memory kernel stack ecx=133 init. val. , … eax=42 init. val. , 44

  76. exec and PCBs memory discarded old memory (more on this later) not changed! copy arguments new stack, heap, … executable fjle loaded from the process control block user regs … … fd 1: … fd 0: (terminal …) open fjles user memory kernel stack ecx=133 init. val. , … eax=42 init. val. , 44

  77. exec and PCBs memory discarded old memory (more on this later) not changed! copy arguments new stack, heap, … executable fjle loaded from the process control block user regs … … fd 1: … fd 0: (terminal …) open fjles user memory kernel stack ecx=133 init. val. , … eax=42 init. val. , 44

  78. execv and const argv is a pointer to constant pointer to char probably should be a pointer to constant pointer to constant char …this causes some awkwardness: execv(path, array); // ERROR solution: cast 45 int execv( const char *path, char * const *argv); const char *array[] = { /* ... */ }; const char *array[] = { /* ... */ }; execv(path, ( char **) array); // or (char * const *)

Recommend


More recommend