OS——Process Synchronization
1.Background
- Shared-memory solution to bounded-buffer problem allows at most n – 1 items in buffer at the same time
A solution, we modify the producer-consumer code
• adding a integer variable counter to keep track of the number of full buffers, initialized to 0. - Race condition: The situation where several processes access and manipulate the shared data concurrently and the final value of the shared data depends upon the particular order in which the access takes place.
- To prevent race conditions, concurrent processes must be synchronized
2 The Critical-Section Problem
- n processes all competing to use some shared data
- Each process has a code segment, called critical section, in which the shared
data is accessed. - Problem – ensure that when one process is executing in its critical section, no other process is to be allowed to execute in its critical section.
- Need to design a protocol that the processes can use to cooperate.
- – Each process must request permission to enter its critical section.
- – The section of code implementing this request is the entry section.
- – The critical section may be followed by an exit section.
- – The remaining code is the remainder section
- A Solution to Critical-Section Problem must satisfy the following three requirements:
- Mutual Exclusion
- Progress
- Bounded Waiting
Two approaches depending on if kernel is preemptive or non-preemptive.
– Preemptive – allows preemption of process when running in kernel mode.
– Non-preemptive – runs until it exits kernel mode, blocks, or voluntarily yields CPU. - Types of solution for critical section
Software-based: programming
Hardware instructions
– TestAndSet
– Swap
Semaphores
3. Peterson’s Solution
- Only 2 processes, P0 and P1
- Mutual exclusion?
Progress?
Bounded-waiting?
do {
flag [i]= true;
turn = j;
while (flag [j] and turn == j) ;
critical section
flag [i] = false;
remainder section
} while (true);
4.Bakery Algorithm
do {
choosing[i] = true; //choose number
number[i] = max(number[0], number[1], …, number [n–1])+1;
choosing[i] = false;
for (j = 0; j < n; j++)
{
while (choosing[j]) ;
while ((number[j] != 0) && ((number[j],j) < (number[i],i)) ;
}
critical section
number[i] = 0;
remainder section
} while (1);
5.Synchronization Hardware
- All solutions below based on idea of locking
Uniprocessors – could disable interrupts
– Currently running code would execute without preemption
– Generally too inefficient on multiprocessor systems - Modern machines provide special atomic hardware instructions
• Atomic = non-interruptable
– test memory word and set value
– swap contents of two memory words
Testandset()
boolean TestAndSet ( boolean *target ) {
boolean rv = *target;
*target = TRUE;
return rv;
}
Process Pi
do {
while (TestAndSet(&lock)) ;
critical section
lock = FALSE;
remainder section
} while(TRUE)
Swap Instruction
void Swap(boolean *a, boolean *b) {
boolean temp = *a;
*a =*b;
*b = temp;
}
Shared data (initialized to false):
boolean lock;
Process Pi
do {
key = true;
while (key == TRUE)
Swap (&lock, &key);
critical section
lock = FALSE;
remainder section
} while(TRUE)
Bounded-waiting Mutual Exclusion with test_and_set
Shared data (initialized to false):
boolean lock;
boolean waiting[n];
Process Pi
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);
Advantages
– Applicable to any number of processes on either a single processor or multiple processors sharing main memory.
– It is simple and therefore easy to verify.
– It can be used to support multiple critical sections.
Disadvantages
– Busy-waiting consumes processor time.
– Starvation is possible when a process leaves a critical section and more than one process is waiting.
– Deadlock
6.Semaphores(信号量)
- does not require busy waiting.
- can only be accessed via two indivisible (atomic) operations
- Wait() and signal() operations cannot be interrupted. - Counting semaphore
– integer value can range over an unrestricted domain.
Binary semaphore
– integer value can range only between 0 and 1.
– Same as a mutex lock
Could now have busy waiting in critical section
Process Pi:
do {
wait(mutex);
critical section
signal(mutex);
remainder section
} while (true);
- semaphore “synch”:Consider P1 and P2 ,require Action1 to happen before Action2
- Implementing S as a Binary semaphore
binary-semaphore S1=1, S2=0;
int C= initial value of semaphore S;
s1 is responsible for mutex while s2 is for Synchronization,(when and only when s2 is produced by signal (s),Wait(S) can escape from wait(s2) )
Wait(S) operation
wait (S1);
C--;
if (C < 0) {
signal (S1);
wait (S2);
}
else
signal (S1); //release
Signal(S) operation
wait (S1);
C ++;
if (C <= 0)
signal(S2); //escape wait when c<0
signal(S1);
- Semaphore Implementation with no Busy waiting
typedef struct {
int value;
struct process *List;
} semaphore;
two operations:
– Block(), place the process invoking the operation on the appropriate waiting queue.
– wakeup( P ), remove one of processes in the waiting queue and place it in the ready queue.
wait (semaphore S) {
S->value--;
if (S->value < 0) {
add this process to S->List;
block();
}
}
signal (semaphore S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->List;
wakeup(P);
}
}
- Critical-Section Problem of Semaphore
Uniprocessor environment(禁止中断)
– Inhibit interrupts during the time that wait() and signal() operations are executing.
multiprocessor environment
– inhibiting interrupts does not work.(every processor)
– Special hardware instructions
– Software solution for the critical-section problem, where the critical section consists of the wait() and signal() procedures.
Busy waiting problem
– have removed busy waiting from the entry to the critical sections of application programs.
– have limited busy waiting to only the critical sections of the wait() and signal() operations, these sections are short.
7. Problems: 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. ( a LIFO queue )
Priority Inversion
– Scheduling problem when lower-priority process holds a lock needed by higher-priority process.
Solved via priority-inheritance protocol.
– all processes that are accessing resources needed by a higher-priority process inherit the higher priority until they are finished with the resources in question.
– When they are finished, their priorities revert to their original values.
8. Classical Problems of Synchronization
- The Bounded-Buffer Problem
- The Readers-Writers Problem
- The Dining-Philosophers Problem
The Bounded-Buffer Problem
Initially:
full = 0
empty = n
mutex = 1 binary semaphore, guaranteeing mutual exclusively operating on the buffer, i.e. only one process is allowed to operate on the buffer
Producer Process
do {
…
produce an item in nextp
…
wait(empty); /*是否有空单元*/
wait(mutex); /*是否可向buffer/空单元写,或:是否有其它进程在操作buffer*/ 和上一句互换易死锁
…
add nextp to buffer
…
signal(mutex); /*允许其它进程访问*/
signal(full); /*满单元数加1,唤醒被阻塞的消费者*/
} while (1);
Consumer Process
do {
wait(full) /*是否有满单元*/
wait(mutex); /*是否可从buffer/满单元读*/
…
remove an item from buffer to nextc
…
signal(mutex); /*允许其他进程访问*/
signal(empty); /*空单元数加1,唤醒阻塞的生产者*/
…
consume the item in nextc
…
} while (1);
The Readers-Writers Problem
A data set is shared among a number of concurrent processes
– Readers: only read the content of the shared object.
– Writers: may update the content of the shared object.
Problem: allow multiple readers to read at the same time,Only one single writer can access the shared data at the same time.
– The writers have exclusive access to the shared object.
the first readers-writers problem: reader first
– no reader will be kept waiting unless a writer has already obtained permission to use the shared object.
– Writers may starve.
the second readers-writers problem: writer first
– once a writer is ready, that writer performs its write as soon as possible.
– Readers may starve.
the first readers-writers problem: reader first
-
Data structures
semaphore wrt /*共享数据写操作的互斥信号量 readcount: /*正在读的reader数目 semaphore mutex /*访问共享变量readcount的互斥信号量
-
Initially wrt = 1, mutex =1, readcount = 0
Reader Process:
wait(mutex); /*互斥访问readcount*/
readcount++; /*读者数加1*/
if (readcount == 1) /*如果是第1个读者,判断是否存在读写冲突,或互斥/禁止写操作*/
wait(wrt);
signal(mutex); /*恢复对readcount访问*/
…
reading is performed
…
wait(mutex); /*互斥访问readcount*/
readcount--; /*读者数减1*/
if (readcount == 0) /*如果已经无readers,
signal(wrt); 恢复写操作许可*/
signal(mutex); /*恢复对readcount访问*/
Writer Process:
wait(wrt);
…
writing is performed
…
signal(wrt);
if a writer is in its critical section while n readers are waiting, the first reader is queued on wrt, and the other n-1 readers are queued on mutex
Dining-Philosophers Problem
guarantee that no two neighbors are eating simultaneously
deadlock occurs, e.g., when each philosopher picks up her left chopsticks
Solution 1: allow at most 4 philosophers to be setting simultaneously at the table
Solution 2: an odd philosopher picks up first her left and then right chopstick, whereas an even philosopher picks up her right and then left chopstick
9.Monitors (管程)
Why the monitor needed ?
process synchronization on the basis of semaphores is somewhat difficult for the application programmers
— disorders of the wait and signal operations in application programs may result in deadlock
High-level synchronization construct that allows the safe sharing of an abstract data type
among concurrent processes.
Monitor is a software module
Chief characteristics
– the local variables of a monitor can be accessed by only the local functions.
– Process enters monitor by invoking one of its functions.
– Only one process may be executing in the monitor at a time
x.wait() means that the process invoking this operation is suspended until another process invokes x.signal()
the x.signal operation resumes exactly one suspended process. If no process is suspended, then the signal operation has no effect.
Monitor Solution to Dining Philosophers problem
void test(int i) {
if ( (state[(i + 4) % 5] != eating) &&
(state[i] == hungry) &&
(state[(i + 1) % 5] != eating)) {
state[i] = eating;
self[i].signal();
}
}
Monitor Solution to Bounded Producer-Consumer Problem