Lecture 6 – Interrupts, Signals, and Indivisibility
By Anubhav Saggi and Frank Chen
Apache Speedups
Interrupts & Signals
E.g. What happens when you unplug your computer.
Desire: Save your state automatically (Use transactions)
- Use a UPS.
o Has following property: Detects power out, has 20 extra minutes.
o Run program: Kernel saves machine state to disk. After power is resumed, all state is restored.
§ Pros: Don’t need to change apps.
§ Cons: Apps don’t know a power outage occured
- Apps should know what to do
o
End-to-end principle
Proposal
§ A “file”: /dev/power as a monitor for power.
·
When you read from this device, you get a byte:
0 => OK
1-255 => N seconds before hard power failure
· Con: Apps must continually check. Constant polling
o Inefficient, wastes CPU cycle
o Hassle (primary concern). Lots of modifications to pre-existing code. Almost any loop will require a check. Extensive analysis and modification
§ Signal – a “magical” spontaneous call inside application to a function specified by the application
§
In between these two: Make a thread to read intermittently from
/dev/power
read(fd,&c,1)
Hangs, until you’re about to run out
return // you have C seconds
Signals are a change to our abstract machine.
We can no longer use the program counter to predict what will happen.
Has instructions. Arrives between any pair of abstract machine instructions
Application x86 instructions + sys calls
At the C level
#include <signal.h>
Typdef void (*handler_t) (int);
//(below) Returns Previous handler Signal Number (small set)
// When power fails, invoke the following handler
handler_t Signal(int,handler_t);
Handlers you can pass:
SIG_IGN (Ignore this
handler)
SIG_DFL (Take ”normal” action aka default. Three usual actions ignore, exit, or dump core & exit)
Other handlers
SIGINT ^C (Default: exit)
SIGPWR (Power failure)
SIGHUP (Hangup)
SIGFPE, SEGV, BUS (Internal error)
SIGPIPE (Piping to nowhere)
SIGTERM (Sent when ‘kill 9286’)
SIGKILL (#9) (Sent when ‘kill -9 9286’. You cannot ignore it)
SIGALRM (Real time clock expires)
SIGVTALRM (CPU clock time expires)
SIGXCPU (Too much CPU usage)
SIGFSZ (Too much disk space usage)
SIGTSTP ^Z (Stopped)
SIGSTOP (Cannot be ignored)
// Assuming cleanup and do_real_work are functions. Also, the exit status is some arbitrary but useful exit number
#include <signal.h>
static void omigosh(int sig){ cleanup(); _exit(126);}
int main(void){
signal(SIGPUR,omigosh);
do_real_work();
}
Uses of signals:
^C
- kills off unwanted computation
- deals with unusual events
- Timeouts
Atomicity: We try to build a reliable machine in what instructions are executed as atoms
Utilization: Useful work / total work
Any resource: time ( CPU / real ), network, power.
We want to spend as little time as possible to maintain the operating system. When OS time goes up, utilization goes down.
E.g. 4 processes on a 4 CPU machine, accessing a shared variable. 90% reads, 10% writes.
Assume single global lock. Assume 0 cost to get / release the lock. (Totally unfair).
- 25% Utilization.
Change the following: The lock is now a read / write lock.
- Multiple reads possible, single writer
- 90% reads
- 30% Someone has a write lock
- 70% Get lock right away
- 10 % writes
- 25% utilization
- 65% utilization
Use this type of process to speed up Apache. Assuming 0 cost to change locks is unreasonable. This is usually costs a lot. Thus, we need to make forking faster.
Forking
What it does
Clones the following:
Address space
File descriptor table
Signal handler table
Working directory (cd chdir) // Child can change its directory
Root directory // Can change theoretically. Root directory
Mount Table // Visible file systems
Stack // Each process has a stack
Registers
State (runnable, blocked, zombie, …)
Process ID
Errno // Why a process failed
Signal Mask // Which signals has temporarily been blocked out
We will come up with a lightweight fork that grabs essentials.
Parent and process share more stuff.
+ Faster
+ We want sharing. Sometimes parent & child want the same address space. Fast sharing
- We lost isolation. Tricky sharing. Child processes are no longer isolated
The following CAN shared between children and parents. Per process, all of the following can be shared. The rest can be shared per thread.
Address space
File descriptor table
Signal handler table
Working directory (cd chdir)
Root directory
Mount Table
C code:
# include <pthread.h> // POSIX threads
// Similar to fork
int pthread_create(pthread_t * thread, pthread_attr_t const * attr,
void (*start) (void*), void *arg /*inintial argument for 'start'*/);
// Similar to exit
void pthread_exit(void * value /* Thread to do next */);
// Similar wait_pid
void pthread_join (pthread_t thread /*Wait for this thread*/,
void ** value /*Where to put the returned value*/);
Now, we go back to Apache Speedups (very top). We add number 3. The shared space is enormous.
Problem: Context switch overhead.
Results from stopping execution of one thread and transitioning to another.
All you need to do is load a few registers, however, ~ 200 instructions. Overhead is costly.
Solution is Non-Blocking I/O.
E.g.
open(file, O_RDWR | O_NONBLOCK) // Causes reads and writes to behave different.
read ( … ) // Always returns immediately. Never blocks. If it would block, will return an error. Errno = EWOULDBLOCK.
write( … ) // Same way
Makes it application’s responsibility to find useful work for a thread. Webserver (on this mode) will create a 100 threads, and each will be a slot of resources.
We want to avoid context switching by limiting the number of threads to an approximate amount. All system calls that would block would no longer do it. Usually talking about network. Thread never waits on doing system calls.
Select( … ); // Master thread calls select to hand off work
Poll ( … ); // Waits