Hardware Synchronization

advertisement
Synchronization
Where are we going with synchronization?
Programs
Shared Programs
Higher-level
API
Locks Semaphores Monitors
Hardware
Load/Store Disable Ints Test&Set Comp&Swap
• We are going to implement various higher-level synchronization primitives
using atomic operations
– Everything is pretty painful if only atomic primitives are load and store
– Need to provide primitives useful at user-level
Lock
• Race conditions can be prevented by
requiring that critical sections be protected by
locks. That is, a process must acquire a lock
before entering a critical section; it releases
the lock when it exits the critical section
Synchronization
do {
[acquire lock]
critical section
[release lock]
remainder section
} while (TRUE);
“Too Much Milk” Solution #4
• Suppose we have some sort of implementation of a lock
– Lock.Acquire() – wait until lock is free, then grab
– Lock.Release() – Unlock, waking up anyone waiting
– These must be atomic operations – if two processes are waiting for
the lock and both see it’s free, only one succeeds to grab the lock
• Then, our milk problem is easy:
milklock.Acquire();
if (nomilk)
buy milk;
milklock.Release();
How to implement Locks?
• Lock: prevents someone from doing
something
– Lock before entering critical section and before
accessing shared data
– Unlock when leaving, after accessing shared data
– Wait if locked
• Important idea: all synchronization involves waiting
• Should sleep if waiting for a long time
• Hardware Lock instruction
Synchronization Hardware
• Many systems provide hardware support for critical section
code
• Uniprocessors – could disable interrupts
– Currently running code would execute without preemption
– System’s clock is kept updated by interrupts
Naïve use of Interrupt Enable/Disable
• How can we build multi-instruction atomic operations?
– Recall: dispatcher gets control in two ways
» Internal: Process does something to relinquish the CPU
» External: Interrupts cause dispatcher to take CPU
– On a uniprocessor, can avoid context-switching by:
» Avoiding internal events (although virtual memory tricky)
» Preventing external events by disabling interrupts
• Consequently, naïve implementation of locks:
LockAcquire { disable Ints; }
LockRelease { enable Ints; }
• Problems with this approach:
– Can’t let user do this! Consider following:
LockAcquire();
While(TRUE) {;}
– What happens with I/O or other important events?
» “Reactor about to meltdown. Help?”
Synchronization Hardware
• Modern machines provide special atomic hardware
instructions
• Atomic = non-interruptable
• Atomic instructions that allow us either to test and
modify the content of a word or to swap the contents
of two words
Synchronization Hardware
• Test and modify the content of a word atomically
boolean TestAndSet (boolean &target) {
boolean rv = target;
target = true;
return rv;
}
Mutual Exclusion with Test-and-Set
•
Shared data:
boolean lock = false;
•
Process Pi
do {
while (TestAndSet(lock));
critical section
lock = false;
remainder section
} while (TRUE);
Synchronization Hardware
• Atomically swap two variables
void Swap(boolean &a, boolean &b) {
boolean temp = a;
a = b;
b = temp;
}
Mutual Exclusion with Swap
•
Shared data (initialized to false):
boolean lock;
•
Process Pi
do {
key = true;
// key is a local variable
while (key == true)
Swap(lock,key);
critical section
lock = false;
remainder section
} while (TRUE);
Bounded-waiting Mutual Exclusion with TestandSet()
do {
waiting[i] = TRUE;
key = TRUE;
while (waiting[i] && key)
key = TestAndSet(&lock);
waiting[i] = FALSE;
// critical section
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = FALSE;
else
waiting[j] = FALSE;
// remainder section
} while (TRUE);
Semaphores
•
Dijkstra’s work on semaphores established over 30 years ago the foundation of modern techniques
for accomplishing synchronization
•
A semaphore, S, is a integer variable that is changed or tested only by one of the two following
indivisible operations
P(S):
while (S 
0) do no-op;
S--;
V(S):
S++;
•
In Dijkstra’s original paper, the P operation was an abbreviation for the Dutch word Proberen,
meaning “to test” and the V operation was an abbreviation for the word Verhogen, meaning “to
•
increment”
Now, P() and V() is normally called wait() and signal()
Critical Section for n Processes
•
Shared data:
semaphore mutex; // initially mutex = 1
•
Process Pi
do {
P(mutex);
critical section
V(mutex);
remainder section
} while (TRUE);
Semaphore Implementation
• Must guarantee that no two processes can execute wait () and signal
() on the same semaphore at the same time
• Thus, implementation becomes the critical section problem where
the wait and signal code are placed in the crtical section.
– Could now have busy waiting in critical section implementation
• But implementation code is short
• Little busy waiting if critical section rarely occupied
• Note that applications may spend lots of time in critical sections and
therefore this is not a good solution.
Semaphore Implementation with no Busy waiting
• With each semaphore there is an associated waiting queue.
Each entry in a waiting queue has two data items:
– value (of type integer)
– pointer to next record in the list
typedef struct {
int value;
struct process *L;
} semaphore;
• Two operations:
– block – place the process invoking the operation on the
appropriate waiting queue.
– wakeup – remove one of processes in the waiting queue
and place it in the ready queue.
Semaphore Implementation
•
Semaphore operations now defined as
P(S):
S.value--;
if (S.value < 0) {
add this process to S.L;
block();
}
V(S):
S.value++;
if (S.value <= 0) {
remove a process Pi from S.L;
wakeup(Pi);
}
Semaphore as a General Synchronization Tool
• Execute <B> in Pj only after <A> executed in Pi
• Use semaphore flag initialized to 0
Pi
Pj
<A>
P(flag)
V(flag)
<B>
Deadlock and Starvation
• Deadlock – two or more processes are waiting indefinitely for an event
that can be caused by only one of the waiting processes
• Starvation – indefinite blocking. A process may never be removed from
the semaphore queue in which it is suspended
• Let S and Q be two semaphores initialized to 1
Pi
Pj
P(S)
P(Q)
P(Q)
P(S)
V(S)
V(Q)
V(Q)
V(S)
Download