CS 208 s22 — The Process Model and Exceptional Control Flow
1 Intro
1.1 Normal control flow
- control flow is the successive transfer of control from one instruction to the next
- each control transfer within this flow involves going from executing \(I_k\) at address \(a_k\) to instruction \(I_{k+1}\) at address \(a_{k+1}\)
- \(I_k\) may be adjacent to \(I_{k+1}\) in memory, but instructions like jumps, calls, and returns can redirect to different regions of stored instructions
1.2 Exceptional control flow (ECF)
- jumps, calls, and returns only react to internal program state, ECF allows all levels of the system to react to abrupt external changes
- underlies mechanisms for I/O, processes, virtual memory
- involved in how application interacts with the OS (system calls)
- an important part of writing event-driven programs like Unix shells and web servers
- basic building block for concurrency
1.3 The Operating System Kernel
- the kernel is the core part of the operating system
- it is trusted and has unrestricted access to the hardware (e.g., is free to perform whatever operations in memory and on disk it wants)
- this trusted state is called kernel-mode
- all other programs run in user-mode, which restricts access to the hardware
2 Exceptions
- partly hardware, partly software
- thus, details vary by system (general ideas the same)
- exception is abrupt change in control flow in response to change in processor state
- change in state called an event
- Java/Python exceptions are not the same, they react to program state and don't involve the hardware
- indirect jump to exception handler procedure through exception table
2.1 Exception Handling
- each possible exception assigned unique exception number
- either by the processor designers or system kernel, depending on the type of exception
- exception table initialized at boot
- address store in special exception table base register
- differences from a normal procedure call
- return address pushed on the stack can be current or next instruction, depending on exception
- other processor state pushed onto stack to facilitate restoring pre-expception context
- may use the kernel's stack instead of user's
- run in kernel mode, given full access to system resources
2.2 Classes of Exceptions
Class | Cause | Async/sync | Return behavior |
---|---|---|---|
interrupt | signal from I/O device | async | always return to next instruction |
trap | intentional exception | sync | always return to next instruction |
fault | potentially recoverable error | sync | might return to current instruction |
abort | nonrecoverable error | sync | never returns |
2.2.1 Interrupts
- asynchronous since exception arises from I/O rather than specific instruction
- hitting ctrl-C, clicking the mouse, data arrives from the network, timer interrupt
2.2.2 Traps and System Calls
- interface between user programs and kernel: the system call
- request for services like reading a file, creating a new process, exiting current process
- from the programmer's perspective appears like any other function call, implementation very different
- returns to next instruction
2.2.3 Faults
- page faults, segment protection faults, divide-by-zero
// page fault example int a[1000]; int main() { a[500] = 13; // this portion of memory is currently only on disk }
mov
instruction interrupted while page fault handler loads corresponding page into memorymov
restarted afterwards
// seg fault example int a[1000]; int main() { a[5000] = 13; // this refers to an invalid address }
mov
instruction interrupted while page fault handler detects invalid address- sends
SIGSEGV
signal to use process, causing it to exit in a seg fault
- handler tries to correct, returns to current instruction if it can
- otherwise returns to
abort
routine
2.2.4 Aborts
- fatal errors, application terminated
- hardware failure
2.3 Exceptions on Linux/x86-64 Systems
- divide error: divide by 0 or result too big for destination, Unix opts to abort ("floating exceptions")
- general protection fault: access to undefined area of virtual memory or attempt to write to read-only segment, no recovery
- notorious "segmentation faults"
- page fault: segment of memory must be loaded from disk into memory, instruction restarted
- machine check: fatal hardware error, always aborts
- C standard library provides wrappers for system calls (system level functions)
3 Processes
- an instance of a program in execution
- each process has the illusion of exclusive use of the processor and memory
3.1 Logical Control Flow
- processes take turns using the processor
- a process is temporarily suspended or preempted when other processes take their turns
- this switching is hidden from the processes
- only detectable through precise measurment of time between instructions
3.2 Concurrent Flows
- process flows overlap
- switching between concurrent processes is called multitasking or time slicing
- independent of whether there are multiple processors
- two processes running simultaneously on different processors are said to be running in parallel
3.3 Private Address Space
- illusion of uniform layout of exclusive memory for each process
3.4 User and Kernel Modes
- typically control via mode bit in some special register
- kernel mode can execute any instruction, access any memory
- user mode prevented from executing privilaged instructions or accessing the kernel area of memory
- instead must operate indirectly through system calls
- exception handlers run in kernel mode
3.5 Context Switches
- kernel maintains the context for each process
- everything needed to restart a preempted process
- register values, instruction pointer, user's stack, and other data structures
- kernel switches from one process to another via context switch: saving current process context and loading in a previously saved one for a different process
- the decisions for doing these switches are collectively called scheduling
- contexting switching often occurs when a process is waiting for I/O (e.g., disk read) or when an interrupt occurs
4 System Call Error Handling
- good practice to always check for errors after making a system call
- possible to define convenient wrapper functions that abstract the error checking
5 Process Control
5.1 Obtaining Process IDs
- each process has a unique id (PID)
getpid
returns PID of calling process,getppid
returns PID of parent of calling process (i.e., process that created calling process)
5.2 Creating and Terminating Processes
- a process is either running, stopped, or terminated
- a stopped process is suspended until it receives a SIGCONT signal and resumes
- a process is terminated by a signal, by returning from
main
, or by callingexit
exit
takes anint
determines the exit status of the process- exit status also set by the return value from
main
- exit status used to indicate whether a process completed successfully or ended in some kind of error state
- a parent process creates a new child process by calling
fork
- child process get a separate copy of the parent's private address space and access to any file descriptors open when
fork
was called (differeny PIDs)- including code, child runs parent's code from the point of the
fork
call
- including code, child runs parent's code from the point of the
fork
is weird: called once, returns twice (in the parent and in the child)- returns child's PID to parent, returns 0 to child (providing a way for the code to check which process it is)
- kernel free to interleave the execution of parent and child, meaning we can't assume anything about which will run first, etc.
- multiple calls to
fork
can get very difficult to reason about
- child process get a separate copy of the parent's private address space and access to any file descriptors open when
5.3 Reaping Child Processes
- a terminated process persists until it is reaped by its parent
- reaping passes the child's exit status to the parent
- an un-reaped process is called a zombie
- when a parent terminates, it's non-terminated child processes become children of the
init
processinti
reaps any zombie children- necessary for long-running processes like shells to reap children, as zombies still take up memory
- a process waits for a child to terminate or stop by calling
waitpid
- by default it suspends calling process until a child process in its wait set terminates
- returns immediately if a child has already terminated
5.3.1 Wait Set
waitpid
takespid
arugment- if
pid > 0
, wait set is single process with that PID - if
pid = -1
, wait set is all child processes
- if
5.3.2 Modifying Default Behavior
- set
options
arugment to combinations (i.e.,or
-ing) of:- WNOHANG: return immediately with value 0 if no child process has terminated yet
- WUNTRACED: wait for child process to terminate or stop
- WCONTINUED: wait for child process to terminate or continue via SIGCONT signal
5.3.3 Checking the Exit Status of a Reaped Child
- provide non-null pointer
status
argument,waitpid
will store child's exit status as the value pointed to bystatus
wait.h
provides macros for interpreting exit status
5.3.4 Error Conditions
waitpid
returns -1 if there are no children or if it was interrupted
5.4 Putting Processes to Sleep
sleep
stops process for a given number of secondspause
stops process until it receives a signal
5.5 Loading and Running Programs
- function
execve
runs and executable with an argument list and environment variable list- does not return unless there's an error
- global variable
environ
also references environment variables - functions
getenv
,setenv
, andunsetenv
manipulate environment array
5.6 Using fork
and execve
- neat mini-shell example, p. 754-56