Exception: Change in control flow in response to a system event (change system state)
hardware and OS software implemented
transfer control from user mode code to OS kernel code
Example Events: divide by 0, arithmetic overflow, page fault, I/O request complete, typing on keyboard
Exception Handler: Kernel code
Exception Handler's Return:
Asynchronous Exception Control Flow: external events
Indicated by setting the processor’s interrupt pin
Handler returns to next instruction
Examples:
Synchronous Exception Control Flow: Caused by events that occur as a result of executing an instruction
Traps: Intentional
Faults: Unintentional, may Recoverable
Aborts: Unintentional, Unrecoverable
syscall
: calling kernel with different set of privileges
Time: take 100-1000 cycles
%rax
: syscall number
%rdi, %rsi, 5rdx, %10, %r8, %r9
: arguments to syscall
%rax
: return (negative value indicate there is an error)
errno
: if %rax
is negative, errno
indicate specific error number
Number | Name | Description |
---|---|---|
0 | read | read file |
1 | write | write file |
2 | open | open file |
3 | closc | close file |
4 | stat | get info about file |
12 | brk | allocate memory |
57 | fork | create process |
59 | execve | execute program |
60 | exit | terminate process |
62 | kill | send signal to process |
Process Context Switch (interrupt)
Signals
Nonlocal Jumps
process: A process is an instance of a running program. abstraction
Logical control flow: Each program seems to have exclusive use of the CPU (provided by context switching)
Private address space: Each program seems to have exclusive use of main memory (provided by virtual memory)
Context Switching (about 2000 cycles)
CPU interleave running process
CPU save register values to physical memory when done
CPU load register values from physical memory when start
Multi-core: multiple CPUs on single chip
share main memory, but not all cache
scheduling done by kernel
concurrent: if any portion of the code from process B runs during the runtime of process A, process A is in concurrent with process B. sequential: not concurrent
On Error, Linux system-level functions typically return -1 and set global variable errno to indicate cause. (not termination, therefore you should check return value of syscall)
pid_t getpid(void)
: Returns PID of current process
pid_t getppid(void)
: Returns PID of parent process
Processes' Status: status of a process in programmer's perspective
Running: executing, or waiting to be executed, will be scheduled by kernel
Stopped: suspended, will not be scheduled until further notice by signals
Terminated: stopped permanently
Terminating Process
received a signal whose default action is to terminate
finish executing main
(implicit call to exit)
calling void exit(int status)
(exit
called once but never return)
Creating Process: parent create running child process by calling int fork(void)
return 0 to child process
return child's PID
to parent process (called once, return twice)
child is identical to parent except different PID
in creating virtual memory
mm_struct
, vm_area_struct
, and page tables.vm_area_struct
in both processes as private copy-on-writeConcurrency in Creating Process:
Behavior of parent-child process combined are not deterministic
stdout
is the same in both parent and child, printing in the same shellprocess graph: partial ordering of statements in a concurrent program
printf
vertices can be labeled with outputtopological sort: Total ordering of vertices where all edges point from left to right
wait(...)
: wait for the first finished child process before running following code
suspend current process
return pid
of child process
if child_status != NULL
, then the pointer you pass into wait
will be set to values indicate exit status (checked using wait.h
macros)
error if process has no children
implemented by syscall
waitpid(...)
: wait for a specific child process before running following code
Zombie Process: finished child process with only exit status, various OS tables waiting for the parent to check. Zombie Process only killed after parent check their children's status by calling wait
.
Orphan Process: a process that is still executing, but whose parent has died. When the parent dies, the orphaned child process is adopted by init
. When orphan processes die, they do not remain as zombie processes; instead, they are waited
on by init
(PID: 0, parent of all process)
int execve(char *filename, char *argv[], char *envp[])
:
filename
: path to executable
argv
: argument vector (by convention, argv[0]==filename
)
envp
: environment variables
Overwrites code, data, and stack, Retains PID, open files and signal context. Therefore, anything below the execve
will not be executed unless there is an error running the execve
command
Called once and never returns
More variation of execve
: Youtube
pstree
can see process hierarchy
systemd is the parent of all programs
Shell
purpose: spawn other application
loop: prompt and wait for user input forever, unless EOF close stdin
stream
has built-in functions
create children (foreground or background)
reap all children
Process Group: one process belongs to exactly one process group
getpgrp()
: return process group of current process
setpgid()
: set process group of a process
usually SIGINT
kills entire process group than a process
kill
: send a signal to a process or process group
kill -9 -24817
sends SIGSTOP
to process group 24817
kill -9 24817
sends SIGSTOP
to process 24817
notice negative indicate process group rather than process
signal: notify a process and start run signal handler (can be sync or async)
exception: from hardware to kernel
signal: from kernel to specific process
cased by:
ID | Name | Default | Event |
---|---|---|---|
2 | SIGINT | Terminate | Ctrl-C |
9 | SIGKILL | Terminate | Kill Program (not overridable) |
11 | SIGSEGV | Terminate | Segmentation (page) violation |
14 | SIGALRM | Terminate | Timer |
17 | SIGCHLD | Terminate | Child stopped or terminated |
Send: kernel store signal in memory
process request kernel to set pending
bit to 1 for a process for a signal
there only can be one signal for each signal id for each process
therefore signals are not queued
Delivers: kernel read stored signal and notify program by executing handler
before scheduling a process, kernel check if a process have any pending
signals that are not blocked
, if so, deliver to process and change pending
to 0.
always before scheduling a process
pnb = pending & ~blocked
choose least non-zero bit in pnb
and force process to receive signal represented by that bit
repeat until pnb == 0
before passing control to process
Pending: a signal is sent but not delivered
Block: a process can block the receipt of signals
process request kernel to change block
bit to 1 for current process for a signal
the signal will remain there, unchanged if it is blocked
Receive: when a process react to the delivery of the signal
Signal handler
can be ran concurrently, but not in parallel
there can be a signal coming in when the handler is running (but not with itself)
normally you should block every signal in signal handler
should be as simple as possible
Async-Safety
stronger requirement than thread safety
printf
, sprintf
, malloc
, exit
are not safe
_exit_
, write
, wait
, waitpid
, sleep
, kill
are safe (write
is the only output that is async-safe)
sio_printf
, sio_error
, sio_put
, sio_putl
are library that are safe
any function that modify global state is not safe
things that are async-safe: man 7 signal-safety
save and restore errno
signal handlers are in the same thread as main program
declare global flag as static volatile
use atomic version of integer like volatile sig_atomic_t
make sure we can't receive a SIGCHILD
signal before we add child to job list
don't use wait()
, use waitpid()
and atomic version of pause()
like sigsuspend()
because while(!pid) pause()
might receive a signal before checking pid
and pause()
and therefore, pause()
cannot detect the signal that just arrived before it.
int sigaction(int signum, const struct sigaction *sa, struct sigaction *old_sa)
for install handlers (BUT DO NOT USE THIS FUNCTION)
if old_sa
not NULL, old sa is stored (good for revert back)
sigprocmask
: manipulate signal mask in process control block of kernel
sigemptyset
: create empty set that blocks no signals
sigfillset
: create filled set that blocks all signals
sigaddset
: add signal to blocking set
sigdelset
: delete signal from blocking set (unblock)
pause()
: stop(suspend) process until any signal arrives
waitpid()
: wait for child process with pid to terminate / stop / continue
wait()
: wait for any children to terminate
using handlers
Table of Content