<<

Carnegie Mellon Carnegie Mellon Today

Exceptional Control Flow:  Exceptional Control Flow Exceptions and Processes  Processes

15‐213 / 18‐213: Introduction to Computer Systems 13th Lecture, Feb 26, 2013

Instructors: Seth Copen Goldstein, Anthony Rowe, and Greg Kesden

1 2

Carnegie Mellon Carnegie Mellon Control Flow Altering the Control Flow

 Processors do only one thing:  Up to now: two mechanisms for changing control flow: . From startup to shutdown, a CPU simply reads and executes . Jumps and branches (interprets) a sequence of instructions, one at a time . Call and return . This sequence is the CPU’s control flow (or flow of control) Both react to changes in program state

Physical control flow  Insufficient for a useful system: Difficult to react to changes in system state . data arrives from a disk or a network adapter inst1 . instruction divides by zero inst Time 2 . user hits Ctrl‐ at the keyboard inst3 . System timer expires …

instn  System needs mechanisms for “exceptional control flow”

3 4 Carnegie Mellon Carnegie Mellon Exceptional Control Flow ECF Exists at All Levels of a System

 Exists at all levels of a computer system  Exceptions  Low level mechanisms . Hardware and kernel software . Exceptions  This Lecture . change in control flow in response to a system event . Hardware timer and kernel software (i.e., change in system state)  Signals . Combination of hardware and OS software . Kernel software and application software  Higher level mechanisms  Nonlocal jumps . Process context switch Next Lecture . Application code . Signals What about . Nonlocal jumps: setjmp()/longjmp() try/catch/throw? . Implemented by either: . OS software (context switch and signals) . C language runtime library (nonlocal jumps)

5 6

Carnegie Mellon Carnegie Mellon

Exceptions Exception Tables

 An exception is a transfer of control to the OS in response to Exception some event (i.e., change in processor state) numbers

code for  Each type of event has a User Process OS exception handler 0 unique exception number k

Exception code for event I_current exception Table exception handler 1  k = index into exception table I_next exception processing 0 (a.k.a. vector) by exception handler 1 code for almost 2 • return to I_current ... exception handler 2  Handler k is called each time • return to I_next n-1 ... exception k occurs • abort

code for exception handler n‐1

 Examples: div by 0, arithmetic overflow, , I/O request completes, Ctrl‐C

7 8 Carnegie Mellon Carnegie Mellon

Asynchronous Exceptions () Synchronous Exceptions

 Caused by events that occur as a result of executing an  Caused by events external to the processor instruction: . Indicated by setting the processor’s interrupt pin . Traps . Handler returns to “next” instruction . Intentional . Examples: system calls, breakpoint traps, special instructions  Examples: . Returns control to “next” instruction . I/O interrupts . Faults . hitting Ctrl‐C at the keyboard . Unintentional but possibly recoverable . arrival of a packet from a network . Examples: page faults (recoverable), protection faults . arrival of data from a disk (unrecoverable), floating point exceptions . Hard reset interrupt . Either re‐executes faulting (“current”) instruction or aborts . hitting the reset button . Aborts . Soft reset interrupt . unintentional and unrecoverable . hitting Ctrl‐Alt‐Delete on a PC . Examples: parity error, machine check . Aborts current program 9 10

Carnegie Mellon Carnegie Mellon

Trap Example: Opening File Fault Example: Page Fault int a[1000];  User calls: open(filename, options)  User writes to memory location main () {  Function open executes instruction int  That portion (page) of user’s memory a[500] = 13; 0804d070 <__libc_open>: is currently on disk } . . . 804d082: cd 80 int $0x80 80483b7: c7 05 10 9d 04 08 0d movl $0xd,0x8049d10 804d084: 5b pop %ebx . . . User Process OS

User Process OS exception: page fault movl exception Create page and int returns pop Different privilege load into memory open file levels! returns  Page handler must load page into physical memory  Returns to faulting instruction Different privilege  OS must find or create file, get it ready for reading or writing  Successful on second try levels!  Returns integer file descriptor 11 12 Carnegie Mellon Carnegie Mellon Fault Example: Invalid Memory Reference Exception Table IA32 (Excerpt) int a[1000]; main () { Exception Number Description Exception Class a[5000] = 13; 0 Divide error Fault } 13 Fault 80483b7: c7 05 60 e3 04 08 0d movl $0xd,0x804e360 14 Page fault Fault 18 Machine check Abort User Process OS 32‐127 OS‐defined Interrupt or exception: page fault 128 (0x80) System call Trap movl 129‐255 OS‐defined Interrupt or trap detect invalid address process

Check Table 6‐1:  Page handler detects invalid address http://download.intel.com/design/processor/manuals/253665.pdf  Sends SIGSEGV signal to user process Different privilege  User process exits with “” levels! 13 14

Carnegie Mellon Carnegie Mellon Altering the Control Flow (revisited) Today

 Up to now: two mechanisms for changing control flow:  Exceptional Control Flow . Jumps and branches Is there an alternative?  Processes . Call and return - For general purpose processors? Both react to changes in program state - For embedded processors?

 Insufficient for a useful system: Difficult to react to changes in system state . data arrives from a disk or a network adapter . instruction divides by zero . user hits Ctrl‐C at the keyboard . System timer expires

 System needs mechanisms for “exceptional control flow”

15 16 Carnegie Mellon Carnegie Mellon Processes Concurrent Processes

 Definition: A process is an instance of a running program.  Two processes run concurrently (are concurrent) if their . One of the most profound ideas in computer science flows overlap in time . Not the same as “program” or “processor”  Otherwise, they are sequential  Examples (running on single core):  Process provides each program with two key abstractions: . Concurrent: A & B, A & C . Logical control flow . Sequential: B & C . Each program seems to have exclusive use of the CPU . Private virtual address space Process AProcess BProcess C . Each program seems to have exclusive use of main memory

Time  How are these Illusions maintained? . Process executions interleaved (multitasking) or run on separate cores . Address spaces managed by system . we’ll talk about this in a couple of weeks 17 18

Carnegie Mellon Carnegie Mellon User View of Concurrent Processes Context Switching  Processes are managed by a shared chunk of OS code  Control flows for concurrent processes are physically called the kernel disjoint in time . Important: the kernel is not a separate process, but rather runs as part of some user process  However, we can think of concurrent processes as  Control flow passes from one process to another via a running in parallel with each other context switch

Process AProcess B Process AProcess BProcess C

Time user code kernel code context switch

Time user code

kernel code context switch

user code

19 20 Carnegie Mellon Carnegie Mellon fork: Creating New Processes Understanding fork Process n Child Process m  int fork(void) pid_t pid = fork(); pid_t pid = fork(); . creates a new process (child process) that is identical to the calling if (pid == 0) { if (pid == 0) { process (parent process) printf("hello from child\n"); printf("hello from child\n"); } else { } else { . returns 0 to the child process printf("hello from parent\n"); printf("hello from parent\n"); } } . returns child’s pid (process id) to the parent process pid_t pid = fork(); pid_t pid = fork(); pid_t pid = fork(); if (pid == 0) { if (pid == 0) { if (pid == 0) { pid = m printf("hello from child\n"); pid = 0 printf("hello from child\n"); printf("hello from child\n"); } else { } else { printf("hello from parent\n"); printf("hello from parent\n"); } else { } } printf("hello from parent\n"); } pid_t pid = fork(); pid_t pid = fork(); if (pid == 0) { if (pid == 0) { printf("hello from child\n"); printf("hello from child\n");  Fork is interesting (and often confusing) because } else { } else { printf("hello from parent\n"); printf("hello from parent\n"); it is called once but returns twice } }

hello from parent Which one is first? hello from child 21 22

Carnegie Mellon Carnegie Mellon Fork Example #1 Fork Example #2

 Parent and child both run same code  Two consecutive forks . Distinguish parent from child by return value from fork void fork2()  Start with same state, but each has private copy { . Including shared output file descriptor printf("L0\n"); Bye . Relative ordering of their print statements undefined fork(); L1 Bye printf("L1\n"); Bye fork(); void fork1() L0 L1 Bye { printf("Bye\n"); int x = 1; } pid_t pid = fork(); if (pid == 0) { printf("Child has x = %d\n", ++x); } else { printf("Parent has x = %d\n", --x); } printf("Bye from process %d with x = %d\n", getpid(), x); }

23 24 Carnegie Mellon Carnegie Mellon Fork Example #3 Fork Example #4

 Three consecutive forks  Nested forks in parent

void fork3() Bye void fork4() { { L2 Bye printf("L0\n"); printf("L0\n"); fork(); Bye if (fork() != 0) { printf("L1\n"); L1 L2 Bye printf("L1\n"); Bye fork(); Bye if (fork() != 0) { printf("L2\n"); L2 Bye printf("L2\n"); Bye fork(); fork(); Bye printf("Bye\n"); Bye } L0 L1 L2 Bye } L0 L1 L2 Bye } printf("Bye\n"); }

25 26

Carnegie Mellon Carnegie Mellon Fork Example #5 exit: Ending a process

 Nested forks in children  void exit(int status) . exits a process void fork5() . Normally return with status 0 { printf("L0\n"); . atexit() registers functions to be executed upon exit if (fork() == 0) { printf("L1\n"); Bye if (fork() == 0) { L2 Bye void cleanup(void) { printf("L2\n"); printf("cleaning up\n"); L1 Bye fork(); } } L0 Bye } void fork6() { printf("Bye\n“); atexit(cleanup); } fork(); exit(0); }

27 28 Carnegie Mellon Carnegie Mellon

void fork7() Zombies Zombie { if (fork() == 0) {  Idea /* Child */ Example printf("Terminating Child, PID = %d\n", . When process terminates, still consumes system resources getpid()); exit(0); . Various tables maintained by OS } else { . Called a “zombie” printf("Running Parent, PID = %d\n", > ./forks 7 & getpid()); . Living corpse, half alive and half dead [1] 6639 while (1) Running Parent, PID = 6639 ; /* */  Reaping Terminating Child, PID = 6640 } } . Performed by parent on terminated child (using wait or waitpid) linux> ps PID TTY TIME CMD . Parent is given exit status information 6585 ttyp9 00:00:00 tcsh  ps shows child process as . Kernel discards process 6639 ttyp9 00:00:03 forks 6640 ttyp9 00:00:00 forks “defunct”  What if parent doesn’t reap? 6641 ttyp9 00:00:00 ps . If any parent terminates without reaping a child, then child will be linux> reaped by init process (pid == 1) . So, only need explicit reaping in long‐running processes . e.g., shells and servers

29 30

Carnegie Mellon Carnegie Mellon

void fork8() void fork7() { Zombie { Nonterminating if (fork() == 0) { if (fork() == 0) { /* Child */ Example /* Child */ printf("Running Child, PID = %d\n", printf("Terminating Child, PID = %d\n", Child Example getpid()); getpid()); while (1) exit(0); ; /* Infinite loop */ } else { } else { printf("Running Parent, PID = %d\n", printf("Terminating Parent, PID = %d\n", linux> ./forks 7 & getpid()); getpid()); [1] 6639 while (1) exit(0); Running Parent, PID = 6639 ; /* Infinite loop */ } Terminating Child, PID = 6640 } } } linux> ps linux> ./forks 8 PID TTY TIME CMD Terminating Parent, PID = 6675 6585 ttyp9 00:00:00 tcsh Running Child, PID = 6676  Child process still active even  6639 ttyp9 00:00:03 forks ps shows child process as linux> ps though parent has terminated 6640 ttyp9 00:00:00 forks “defunct” PID TTY TIME CMD 6641 ttyp9 00:00:00 ps 6585 ttyp9 00:00:00 tcsh linux> 6639 6676 ttyp9 00:00:06 forks [1] Terminated  Killing parent allows child to be 6677 ttyp9 00:00:00 ps linux> ps reaped by init linux> PID TTY TIME CMD 6585 ttyp9 00:00:00 tcsh 6642 ttyp9 00:00:00 ps

31 32 Carnegie Mellon Carnegie Mellon

void fork8() { Nonterminating if (fork() == 0) { wait: Synchronizing with Children /* Child */ printf("Running Child, PID = %d\n", Child Example getpid());  Parent reaps child by calling the wait function while (1) ; /* Infinite loop */ } else { printf("Terminating Parent, PID = %d\n",  int wait(int *child_status) getpid()); exit(0); . suspends current process until one of its children terminates } . return value is the pid of the child process that terminated } linux> ./forks 8 . if child_status != NULL, then the object it points to will be set Terminating Parent, PID = 6675 to a status indicating why the child process terminated Running Child, PID = 6676  Child process still active even linux> ps though parent has terminated PID TTY TIME CMD 6585 ttyp9 00:00:00 tcsh 6676 ttyp9 00:00:06 forks  Must kill explicitly, or else will keep 6677 ttyp9 00:00:00 ps running indefinitely linux> kill 6676 linux> ps PID TTY TIME CMD 6585 ttyp9 00:00:00 tcsh 6678 ttyp9 00:00:00 ps 33 34

Carnegie Mellon Carnegie Mellon wait: Synchronizing with Children wait() Example  If multiple children completed, will take in arbitrary order void fork9() {  Can use macros WIFEXITED and WEXITSTATUS to get information about int child_status; exit status if (fork() == 0) { void fork10() printf("HC: hello from child\n"); HC Bye { } pid_t pid[N]; else { int i; printf("HP: hello from parent\n"); HP CT Bye int child_status; wait(&child_status); for (i = 0; i < N; i++) printf("CT: child has terminated\n"); if ((pid[i] = fork()) == 0) } exit(100+i); /* Child */ printf("Bye\n"); for (i = 0; i < N; i++) { exit(); pid_t wpid = wait(&child_status); } if (WIFEXITED(child_status)) printf("Child %d terminated with exit status %d\n", wpid, WEXITSTATUS(child_status)); else printf("Child %d terminate abnormally\n", wpid); } }

35 36 Carnegie Mellon Carnegie Mellon waitpid(): Waiting for a Specific Process execve: Loading and Running Programs Stack bottom Null‐terminated  int execve(  env var strings waitpid(pid, &status, options) char *filename, . suspends current process until specific process terminates Null‐terminated char *argv[], cmd line arg strings . various options (see textbook) char *envp[] unused void fork11() ) envp[n] == NULL {  envp[n‐1] pid_t pid[N]; Loads and runs in current process: … int i; . filename envp[0] int child_status; . With argument list argv environ for (i = 0; i < N; i++) argv[argc] == NULL if ((pid[i] = fork()) == 0) . And environment variable list envp argv[argc‐1] exit(100+i); /* Child */ for (i = N-1; i >= 0; i--) {  Does not return (unless error) … pid_t wpid = waitpid(pid[i], &child_status, 0); argv[0]  if (WIFEXITED(child_status)) Overwrites code, data, and stack Linker vars printf("Child %d terminated with exit status %d\n", . keeps pid, open files and signal context envp wpid, WEXITSTATUS(child_status)); else  Environment variables: argv argc printf("Child %d terminated abnormally\n", wpid); . “name=value” strings } Stack frame for } . getenv and putenv 37 main Stack top 38

Carnegie Mellon Carnegie Mellon execve Example Summary if ((pid = Fork()) == 0) { /* Child runs user job */  Exceptions if (execve(argv[0], argv, environ) < 0) { . Events that require nonstandard control flow printf("%s: Command not found.\n", argv[0]); . Generated externally (interrupts) or internally (traps and faults) exit(0); } }  Processes . At any given time, system has multiple active processes argv[argc] = NULL . Only one can execute at a time on a single core, though argv[argc‐1] “/usr/include” . … “-lt” Each process appears to have total control of processor + private memory space argv[0] “ls” argv

envp[n] = NULL envp[n‐1] “PWD=/usr/droh” … “PRINTER=iron” envp[0] “USER=droh” environ 39 40 Carnegie Mellon Summary (cont.)

 Spawning processes . Call fork . One call, two returns  Process completion . Call exit . One call, no return  Reaping and waiting for processes . Call wait or waitpid  Loading and running programs . Call execve (or variant) . One call, (normally) no return

41