unix api 1
play

Unix API 1 1 Changelog swtch() of animation 9 September 2019: - PowerPoint PPT Presentation

Unix API 1 1 Changelog swtch() of animation 9 September 2019: exec and PCBs: remove init. val. from fjrst frame with arrows 4 September 2019: xv6: where the context is: mark where pointers point identifying what an address space is to


  1. 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) 10 what about switching to a new thread?

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

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

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

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

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

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

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

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

  10. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

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

  12. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  13. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  14. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  15. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

  16. 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 13 pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;

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

  18. xv6 myproc xv6 function: myproc() retrieves pointer to currently running struct proc 15

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

  20. 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 17

  21. Unix history 18 1969 Unnamed PDP-7 operating system 1969 Open Source 1971 to 1973 Unix 1971 to 1973 Mixed/Shared Source Version 1 to 4 1974 to 1975 Unix 1974 to 1975 Closed Source Version 5 to 6 PWB/Unix 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 1983 BSD 4.2 1983 SunOS 1 to 1.1 System V R1 to R2 1984 SCO Xenix 1984 Unix 1985 1985 Version 8 SCO Xenix AIX System V V/286 BSD 4.3 1986 1.0 R3 HP-UX 1986 SunOS 1.0 to 1.2 SCO Xenix Unix 1.2 to 3.0 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 Bell Labs) 1989 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 NetBSD 6 to 11 0.8 to 1.0 Linux SCO Unix UnixWare 1993 BSD 1993 0.95 to 1.2.x 4.4 to 3.2.4 1.x to 2.x 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 1.0 to 2.2 Solaris 1996 5.0 to 5.04 1996 2.1 to 9 1997 1997 NetBSD 1.3 FreeBSD 1998 1998 3.0 to 3.2 OpenServer Minix 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 2.x 2005 UnixWare 7.x 2006 to 2007 (System V 2006 to 2007 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 6.x 2009 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

  22. 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 …and POSIX doesn’t specify everything 19

  23. 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 20

  24. 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 21

  25. POSIX process management essential operations 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 22 process information: getpid

  26. getpid pid_t my_pid = getpid(); printf("my pid is %ld\n", ( long ) my_pid); 23

  27. 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 24

  28. 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 25 process creation: fork

  29. 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 26

  30. 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, 27

  31. 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, 27

  32. 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, 27

  33. 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, 27

  34. 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 , 27

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

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

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

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

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

  40. a fork question child Done! Child 100 Done! In child parent child parent Done! Done! In child Child 100 parent parent int main() { child parent 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"); } printf("Child %d\n", pid); printf("In child\n"); if (pid == 0) { pid_t pid = fork(); 29 } else {

  41. a fork question child Done! Child 100 Done! In child parent child parent Done! Done! In child Child 100 parent parent int main() { child parent 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"); } printf("Child %d\n", pid); printf("In child\n"); if (pid == 0) { pid_t pid = fork(); 29 } else {

  42. 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 30 running programs: exec*

  43. 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 31

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

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

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

  47. 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, … eax=42, 33

  48. 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. , 33

  49. 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. , 33

  50. 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. , 33

  51. 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. , 33

  52. 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 34 int execv( const char *path, char * const *argv); const char *array[] = { /* ... */ }; const char *array[] = { /* ... */ }; execv(path, ( char **) array); // or (char * const *)

  53. aside: environment variables (1) SSH_TTY=/dev/pts/0 _=/usr/bin/printenv … KDEDIRS=/usr LOADEDMODULES= MODULEPATH=/sw/centos/Modules/modulefiles:/sw/linux-any/Modules/modulefiles LANG=en_US.UTF-8 PWD=/zf14/cr4bd PATH=/zf14/cr4bd/.cargo/bin:/zf14/cr4bd/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/puppetlabs/bin:/usr/cs/contrib/bin:. MAIL=/var/spool/mail/cr4bd MODULE_VERSION=3.2.10 LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36: USER=cr4bd QT_GRAPHICSSYSTEM_CHECKED=1 QTINC=/usr/lib64/qt-3.3/include key=value pairs associated with every process: OLDPWD=/zf14/cr4bd QTDIR=/usr/lib64/qt-3.3 SELINUX_USE_CURRENT_RANGE= SSH_CLIENT=128.143.67.91 58432 22 HISTSIZE=1000 SHELL=/bin/bash TERM=screen SELINUX_ROLE_REQUESTED= HOSTNAME=labsrv01 XDG_SESSION_ID=754 MANPATH=:/opt/puppetlabs/puppet/share/man MODULE_VERSION_STACK=3.2.10 $ printenv 35

  54. aside: environment variables (2) environment variable library functions: putenv("KEY= value ") (sets KEY to value ) setenv("KEY", "value") (sets KEY to value ) int execve(char *path, char **argv, char **envp) execve("/path/to/somecommand", argv, envp); normal exec versions — keep same environment variables 36 getenv("KEY") → value char *envp[] = { "KEY1=value1", "KEY2=value2", NULL }; char *argv[] = { "somecommand", "some arg", NULL };

  55. aside: environment variables (3) interpretation up to programs, but common ones… PATH=/bin:/usr/bin to run a program ‘foo’, look for an executable in /bin/foo , then /usr/bin/foo HOME=/zf14/cr4bd current user’s home directory is ‘/zf14/cr4bd’ TERM=screen-256color your output goes to a ‘screen-256color’-style terminal … 37

  56. why fork/exec? could just have a function to spawn a new program Windows CreateProcess() ; POSIX’s (rarely used) posix_spawn some other OSs do this (e.g. Windows) needs to include API to set new program’s state open fjles, current directory, environment variables, … with fork: just use ‘normal’ API before fork but allows OS to avoid ‘copy everything’ code probably makes OS implementation easier 38

  57. posix_spawn pid_t new_pid; } if (error_code == 0) { ); argv, 39 "/bin/ls", &new_pid, int error_code = posix_spawn( const char argv[] { "/bin/ls", "-l", NULL }; NULL /* null = copy current process's open files; if not null, do something else */ NULL /* null = no special settings for new process */ , NULL /* null = copy current process's env. vars; if not null, do something else */ /* handle error */

  58. some opinions (via HotOS ’19) 40

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

  60. wait/waitpid pid_t waitpid(pid_t pid, int *status, int options) wait for a child process (with pid= pid ) to fjnish sets *status to its “status information” options? see manual page (command man waitpid ) 0 — no options WNOHANG — return 0 rather than hanging if process not yet done 42 pid=-1 → wait for any child process instead

  61. wait/waitpid pid_t waitpid(pid_t pid, int *status, int options) wait for a child process (with pid= pid ) to fjnish sets *status to its “status information” options? see manual page (command man waitpid ) 0 — no options WNOHANG — return 0 rather than hanging if process not yet done 42 pid=-1 → wait for any child process instead

  62. exit statuses int main() { return 0; */ } 43 /* or exit(0);

  63. waitpid example #include <sys/wait.h> ... child_pid = fork(); if (child_pid > 0) { int status; waitpid(child_pid, &status, 0); ... 44 /* Parent process */ } else if (child_pid == 0) { /* Child process */

  64. the status #include <sys/wait.h> ... waitpid(child_pid, &status, 0); if (WIFEXITED(status)) { printf("main returned or exit called with %d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("killed by signal %d (control-C causes signal %d)\n", WTERMSIG(status), SIGINT); ... } W* macros to decode it 45 } else { “status code” encodes both return value and if exit was abnormal

  65. the status #include <sys/wait.h> ... waitpid(child_pid, &status, 0); if (WIFEXITED(status)) { printf("main returned or exit called with %d\n", WEXITSTATUS(status)); printf("killed by signal %d (control-C causes signal %d)\n", WTERMSIG(status), SIGINT); ... } W* macros to decode it 45 } else if (WIFSIGNALED(status)) { } else { “status code” encodes both return value and if exit was abnormal

  66. aside: signals signals are a way of communicating between processes they are also how abnormal termination happens wait’s status will tell you when and what signal killed a program constants in signal.h SIGINT — control-C SIGTERM — kill command (by default) SIGSEGV — segmentation fault SIGBUS — bus error SIGABRT — abort() library function … 46

  67. waiting for all children #include <sys/wait.h> ... while ( true ) { if (errno == ECHILD) { break ; } } } 47 pid_t child_pid = waitpid( − 1, &status, 0); if (child_pid == (pid_t) − 1) { /* no child process to wait for */ } else { /* some other error */ /* handle child_pid exiting */

  68. ‘waiting’ without waiting #include <sys/wait.h> ... pid_t return_value = waitpid(child_pid, &status, WNOHANG); if (return_value == (pid_t) 0) { } else { } 48 /* child process not done yet */ } else if (child_pid == (pid_t) − 1) { /* error */ /* handle child_pid exiting */

  69. typical pattern parent fork waitpid child process exec exit() 49

  70. typical pattern (detail) } } … main() { … } … waitpid(pid,…); } else if (pid > 0) { … exec…(…); if (pid == 0) { pid = fork(); … … pid = fork(); waitpid(pid,…); } else if (pid > 0) { … exec…(…); if (pid == 0) { pid = fork(); … } … waitpid(pid,…); } else if (pid > 0) { … exec…(…); if (pid == 0) { 50

  71. multiple processes? while (...) { pid = fork(); if (pid == 0) { exec ... } else if (pid > 0) { pids.push_back(pid); } } for (pid_t pid : pids) { waitpid(pid, ...); ... } 51 /* retrieve exit statuses in order */

  72. multiple processes? while (...) { pid = fork(); if (pid == 0) { exec ... } else if (pid > 0) { pids.push_back(pid); } } handleProcessFinishing(pid); } 52 /* retrieve exit statuses as processes finish */ while ((pid = waitpid( − 1, ...)) != − 1) {

  73. parent and child processes every process (but process id 1) has a parent process ( getppid() ) this is the process that can wait for it creates tree of processes: 53

  74. parent and child questions… what if parent process exits before child? child’s parent process becomes process id 1 (typically called init ) what if parent process never waitpid()/wait() s for child? child process stays around as a “zombie” can’t reuse pid in case parent wants to use waitpid() what if non-parent tries to waitpid() for child? waitpid fails 54

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

  76. 56

  77. backup slides 57

  78. context switch in xv6 will mostly talk about kernel thread switch : xv6 function: swtch() save kernel registers for A, restore for B in xv6: separate from saving/restoring user registers one of many possible OS design choices additional process switch pieces: ( switchuvm() ) changing address space (page tables) telling processor new stack pointer for exceptions 58

  79. 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; 59 void swtch( struct context **old, struct context * new );

  80. 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; 59 void swtch( struct context **old, struct context * new );

  81. 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; 59 void swtch( struct context **old, struct context * new );

  82. 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; 59 void swtch( struct context **old, struct context * new );

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