Date post: | 17-Jan-2016 |
Category: |
Documents |
Upload: | allison-allen |
View: | 226 times |
Download: | 0 times |
CSCI 3431: OPERATING SYSTEMS
Chapter 6 – Process Synchronisation (Pgs 225 – 267)
Overview
Consider a block of shared memory Process P wants to write data to the
shared memory while Process R wants to read the data
Up until now, we have needed to use a synchronous system call to cause the reader to wait until the write is complete
The approach works because the synchronous system calls provide mutual exclusion
Mutual Exclusion
When a resource (i.e., memory) can be accessed by only one process at a time, we say that the resource is mutually exclusive
The code in each process to access the mutually exclusive resource is known as a critical section
Only one processes critical section may run at a time (and hence only one process can access the resource at a time)
Race Conditions
Pre-emption means that, essentially, the statements of any two processes can be interleaved to create any ordering of their parts
If the ordering of the parts affects the outcome of computation, we say that a race condition exists
To prevent race conditions, we use mutual exclusion and critical sections
Example
x++ increments x R1 mem[x]; R1 R1 + 1; mem[x] R1; What if P1 does x++ and P2 does x--? End result
is that x should never change regardless of the ordering
Assume x starts at 4 ..R1 mem[x]; R1 R1 + 1; *Pre-empt* R2 mem[x]; R2 R2 - 1 ; *Pre-empt*mem[x] R1; *Pre-empt*mem[x] R2; .... x is now 3, not 4 ....
Idea: x++ must fully execute before x– (or reverse)
Critical Section
Must provide the following1. Mutual Exclusion: Only one process at a
time can be in their critical section2. Progress: Processes must be allowed to
make progress and be allowed (eventually) to enter their critical section
3. Bounded Waiting: There is a limit on how many times other processes can perform their critical sections and thus block any other process from entering theirs
Peterson's Solution
Basic software solution for processes i and jdo { flag[i] = TRUE; turn = j; while (flag[j] && turn==j); /* Critical Section */ flag[i] = FALSE; /* Remainder */} while (TRUE);
Highly concurrent (threaded) CPUs can invalidate this solution
Locks
Use a lock to protect critical section Process must acquire the lock before
entering and release it after leaving Both hardware and software
solutions are possible Simple with hardware (disable
interrupts when shared variable – lock – is being modified), but inefficient and potentially dangerous
Lock Example
boolean TestAndSet (bool *t) {
// If False: return false, set to true
// If True: return true, set to true
bool rv = *t;
*t = TRUE;
return rv;
}
Then ...do {
// Loop while lock is set
while (TestAndSet(&lock));
/* Critical section */
lock = FALSE;
/* remainder */
}
Works if TestAndSet is Atomic and cannot be interrupted
Problems
Hardware solutions are difficult to use since they only provide mutual exclusion
Bounded waiting requires considerable and complex "extra" code (see Fig. 6.8)
More complex for n processes than for 2
Difficult for engineers to implement as they require memory access
Semaphores
Integer variable accessed using on the operations wait and signal
wait(s) {
while (s <= 0) ;
s--;
}
signal(s) {
s++;
}
wait and signal must be atomic in their manipulation of s!
Using a Semaphoredo { wait(mutex) ; /* critical section */ signal(mutex)} while (TRUE);
Main problem is "busy waiting" – constant checking of mutex in wait requires CPU instructions (being busy) to do nothing except wait
Semaphores++
Semaphores with busy waiting are often called "spinlocks"
Can be generalised for bounded resources usable by more than one process
Simple to program with and provide bounded waiting
Can remove busy waiting if we tell the kernel to use a queue for the semaphore and block all processes that are waiting
Signal would wake up a waiting process
Deadlock + Starvation When two processes are each waiting for a
resource held by the other process "Deadlock" can occur
E.g., 4 cars approach an interesection, each blocking one of the 4 roads out, each waits for the others to move and clear a road forward
Starvation occurs from infinite waiting E.g., process that is supposed to do the
wakeup fails and the wakeup is never sent
Priority Inversion
Requires more than 2 priorities Higher priority process blocked from
critical section by lower priority processes
Solution: Priority Inheritance Process inherits the priority of the
highest process that is blocked
Classic Problems Bounded Buffer (Producer/Consumer) Readers-Writers: Simultaneous reads,
writes block everything Dining Philosophers
Monitors
Semaphores only work when used properly, by all processes
Solution is to pre-code the critical sections A Monitor is an ADT Only one thread at a time can be in a
monitor Usually provides a way for a thread to yield
the monitor (or block) so another thread can run
Many different models of monitors (See Buhr et al. for details)
Posix Semaphores
Provided by semaphore.h: sem_init() – creates and initialises it sem_open() – opens it sem_close() – closes it after we are
done sem_post() – releases the semaphore sem_wait() – obtains the semaphore sem_trywait() – asynchronous wait
To Do:
Work on Lab 4 Read Chapter 6 (pgs 225-267; this
lecture) Read Chapter 7 (pgs 283-306; next
lecture)