Chapter 7 -- Synchronization
Note: less emphasis on 7.2.2, stuff of 7.7 and beyond
Software solutions
- Critical sections
- Defined to be areas of code where only one process/thread can
execute at a time.
- Example: Any code that messes with the task_struct will need to
lock out other processess from looking at the same task_struct.
- Example: any code managinb buffers for the producer/consumer
problem must have locks.
- These code areas not always contigious.
- Goals (simplified version).
- Mutual exclusion -- only one process per time.
- Progress -- If no one else is waiting, then I can get in.
- Bounded Wait -- No starvation. But what if the
scheduler keeps a process from running?
- Types
- Busy waiting (spin lock) -- keep CPU while waiting.
- Wastes CPU
- Avoids context switch.
- Real dumb if lock will not be released until another
process runs.
- Useful on multi-CPU systems.
- Blocking wait -- turn CPU over if thwarted.
- Uses a context switch.
- Makes sure other process can get CPU.
- Application Solutions
- Normally cannot disable interupts.
- Single lines of code can generate multiple asm instructions.
- We assume that each single asm instruction is atomic.
- Page 195 ("algorithm 3") offers a solution that works for two
processes without disabling interrupts.
- 6.2.2 Offers a solution that works for multiple processes
without disabling interrupts.
- Some operating systems offer lock as a system call.
- Operating System Solutions.
- Can disable interrupts. (Give example of the cli() in pur /proc
assigment).
- Hurts interrupt latency.
- Bad for multi-CPU systems.
- Instructions just for locks.
- test-and-set
- define ...
- Use is (also complicated version that has bounded wait).
while (test-and-set(lock))
;
CRITICAL SECTION
lock = false;
REMAINDER SECTION
- swap (also complicated version that has bounded wait).
- define...
- Useis
key = true;
repeat swap(key, lock) until key == false;
CRITICAL SECTION
lock = false;
REMIANDER SECTION
Semaphores
- Define wait(lock) and signal(lock) (spin-lock version).
wait(lock): while lock <= 0 do no-op;
lock -= 1;
signal(lock): lock++;
- Define wait(lock) and signal(lock) (blocking wait version)
class semaphore { int value; list-of-processes L; }
wait(semaphore) : S.value--;
if (S.value < 0)
add self to S.L;
schedule();
signal(semaphore): S.value++;
if (S.value <= 0)
remove process P from S.L;
wakeup(P);
When is spin-lock better? When is blocking lock better?
- More versitile than critical sections.
- Can impose an ordering.
FIRST STUFF
signal(lock);
----------------
wait(lock);
SECOND STUFF
- Can allow 'n' processes in critical section (by init'ing the
lock to 'n').
Example Using Semaphores
- Producer/Consumer problem
// init
empty = SIZE; full = 0;
// Producer
repeat
produce an item
wait(empty);
wait(mutex);
add item to buffer
signal(mutex);
signal(full);
until done
// Consumer
repeat
wait(full);
wait(mutex);
consume an item from buffer
signal(mutex);
signal(empty);
until done
Chinese Philosphers
while (1) {
wait(chopstick(N));
wait(chopstick((N+1) mod 5);
eat ....
signal(chopstick((N+1) mod 5);
signal(chopstick(N);
}
But, what about deadlock? How can deadlock be solved?
What happens if you accedentilly switch wait() and signal()?
Deadlocks
- Requirements for deadlocks
- Exclusive use
- No preemption
- Indefinite wait
- Cyclic wait.
- Deadlock can be described by a wait-for graph
- elements are processes and resources.
- Bipartite graph
- cycle means deadlock.
- Easy ways to fix avoid deadlock
- Can only request resource if have no resource-- no wait cycle
- Works sometimes (like for kernel data structures)
- Doesn't work other times (like for devices).
- Must allow for multiple simultainious requests.
- Death if request cannot be satisfied.
- Can request second resource. If cannot immediately satisfy,
then must release resource.
- Can cause process termination.
- Not suitable for kernel.
- Order resources -- no wait cycle
- Can cause unnecessary resource hogging.
- Simple and effective