Lecture6 Synchronization
Lecture6 Synchronization
Lecture 6
Process Synchronization
(chapter 6)
• The slides here are adapted/modified from the textbook and its slides:
Operating System Concepts, Silberschatz et al., 7th & 8th editions,
Wiley.
REFERENCES
• Operating System Concepts, 7th and 8th editions, Silberschatz et al.
Wiley.
• Modern Operating Systems, Andrew S. Tanenbaum, 3rd edition, 2009.
• Background
• The Critical-Section Problem
• Peterson’s Solution
• Synchronization Hardware
• Semaphores
• Monitors
Shared Data
count
Producer Consumer
Shared Buffer
Producer ConSUMER
or
Producer Consumer
register1 Count
PRODUCER (count++)
6
5 5
4
6
register1 = count
register1 = register1 + 1
register2 count = register1
5
4
CONSUMER (count--)
register2 = count
register2 = register2 – 1
CPU count = register2
6
Main Memory
CS342 Operating Systems 11 İbrahim Körpeoğlu, Bilkent University
Interleaved Execution sequence
Change X
Change X
Change Y
Change Y
Change Y
Change X
• Considering a process:
– It may be executing critical section code from time to time
– It may be executing non critical section code (remainder section)
other times.
do {
do {
entry section
critical section
critical section
remainder section
exit section
Entry section will allow only one process to enter and execute critical section code.
CS342 Operating Systems 15 İbrahim Körpeoğlu, Bilkent University
Solution to Critical-Section Problem
• While a process is running in user mode, it may call a system call s().
Then kernel starts running function s(). CPU is executing in kernel
mode now. We say the process is now running in kernel mode (even
though kernel code is running).
do {
flag[i] = TRUE;
turn = j; entry section
} while (1)
PROCESS 0 PROCESS 1
do { do {
flag[0] = TRUE; flag[1] = TRUE;
turn = 1; turn = 0;
while (flag[1] && turn == while (flag[0] && turn ==
1); 0);
critical section critical section
flag[0] = FALSE; flag[1] = FALSE;
remainder section remainder section
} while (1) } while (1)
0 1
flag
Shared Variables
turn
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
Only one process can acquire lock. Others has to wait (or busy loop)
• Is a machine/assembly instruction.
• Need to program in assembly to use. Hence Entry section code should be
programmed in assembly
• But here we provide definition of it using a high level language code.
Solution:
do {
while ( TestAndSet (&lock ))
entry section
; // do nothing
// critical section
// remainder section
} while (TRUE);
entry_section:
TestAndSet REGISTER, LOCK; entry section code
CMP REGISTER, #0
JNE entry_section;
RET
exit_section:
move LOCK, #0 exit section code
RET
main:
..
call entry_section;
execute criticial region;
call exit_section;
• Shared Boolean variable lock initialized to FALSE; Each process has a local
Boolean variable key
Solution: do {
key = TRUE;
while ( key == TRUE)
Swap (&lock, &key );
// critical section
lock = FALSE;
// remainder section
} while (TRUE);
// critical section
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
exit section code
lock = FALSE;
else
waiting[j] = FALSE;
// remainder section
} while (TRUE);
CS342 Operating Systems 32 İbrahim Körpeoğlu, Bilkent University
Semaphore
• Less complicated entry and exit sections when semaphores are used
do {
wait (mutex);
// Critical Section
signal (mutex);
// remainder section
} while (TRUE);
Process 0 Process 1
do {
do {
wait (mutex);
wait (mutex);
// Critical Section
// Critical Section
signal (mutex);
signal (mutex);
// remainder section
// remainder section
} while (TRUE);
} while (TRUE);
semaphore x = 0; // initialized to 0
P0 P1
… …
S1; wait (x);
Solution: signal (x); S2;
…. ….
Producer Consumer
do { do {
// produce item wait (Full_Cells);
… ….
put item into buffer remove item from buffer
..
.. …
signal (Full_Cells); } while (TRUE);
} while (TRUE);
wait() {…} signal() {…}
Kernel
Semaphore Full_Cells = 0; // initialized to 0
Full_Cells
BUF_SIZE
Producer
Sleeps
0 time
Consumer
Sleeps
CS342 Operating Systems 40 İbrahim Körpeoğlu, Bilkent University
Ensured by synchronization mechanisms: * Red is always less than Blue
Pt – Ct <= BUF_SIZE * (Blue – Red) can never be
Pt – Ct >= 0 greater than BUF_SIZE
BUF_SIZE
times
all items consumed (Ct)
wait (x);
…
….use one instance Each process has to be
of the resource… coded in this manner.
…
signal (x);
• Must guarantee that no two processes can execute wait () and signal
() on the same semaphore at the same time.
• Kernel can guarantee this.
typedef struct {
int value;
struct process *list;
} semaphore;
}
} Implementation of signal:
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup the process;
}
}
CS342 Operating Systems 45 İbrahim Körpeoğlu, Bilkent University
Kernel Implementing wait and signal
• The wait and signal operations must be atomic. The integer value is updated.
No two process should update at the same time.
How can the kernel ensure that? It can NOT use semaphores to implement
semaphores.
• Deadlock – two or more processes are waiting indefinitely for an event that can
be caused by only one of the waiting processes
• Let S and Q be two semaphores initialized to 1
P0 P1
wait (S); wait (Q);
wait (Q); wait (S);
. .
. .
. .
signal (S); signal (Q);
signal (Q); signal (S);
• Starvation – indefinite blocking. A process may never be removed from the
semaphore queue in which it is suspended
• Priority Inversion - Scheduling problem when lower-priority process holds a
lock needed by higher-priority process
• Bounded-Buffer Problem
• Readers and Writers Problem
• Dining-Philosophers Problem
buffer
prod cons
full = 4
empty = 6
The structure of the producer process The structure of the consumer process
do { do {
// produce an item in nextp wait (full);
wait (mutex);
wait (empty);
wait (mutex); // remove an item from
// buffer to nextc
// add the item to the buffer
signal (mutex);
signal (mutex); signal (empty);
signal (full);
// consume the item in nextc
} while (TRUE);
} while (TRUE);
• Problem – allow multiple readers to read at the same time. Only one single
writer can access the shared data at the same time
reader
writer
reader
Data Set
reader
writer
writer reader
• Shared Data
– Data set
– Integer readcount initialized to 0
• Number of readers reading the data at the moment
– Semaphore mutex initialized to 1
• Protects the readcount variable
(multiple readers may try to modify it)
– Semaphore wrt initialized to 1
• Protects the data set
(either writer or reader(s) should access data at a time)
a resource
a process
Assume a philosopher needs two forks to eat. Forks are like resources.
While a philosopher is holding a fork, another one can not have it.
• But lots of real resource allocation problems look like this. If we can
solve this problem effectively and efficiently, we can also solve the real
problems.
do {
wait ( chopstick[i] );
wait ( chopStick[ (i + 1) %
5] );
// eat
signal ( chopstick[i] );
signal (chopstick[ (i + 1)
% 5] );
// think
} while (TRUE);
This solution provides concurrency but may result in deadlock.
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
…
procedure Pn (…) {……}
• condition x, y;
• Condition variables are not semaphores. They are different even though they
look similar.
monitor DP {
enum { THINKING;
HUNGRY, EATING) state [5] ;
void test (int i) {
condition cond [5];
if ( (state[(i + 4) % 5] != EATING) &&
(state[(i + 1) % 5] != EATING) &&
void pickup (int i) {
(state[i] == HUNGRY)) {
state[i] = HUNGRY;
state[i] = EATING ;
test(i);
cond[i].signal ();
if (state[i] != EATING)
}
cond[i].wait;
}
}
void putdown (int i) {
initialization_code() {
state[i] = THINKING;
for (int i = 0; i < 5; i++)
// test left and right neighbors
state[i] = THINKING;
test((i + 4) % 5)
}
test((i + 1) % 5);
}
} /* end of monitor */
CS342 Operating Systems 63 İbrahim Körpeoğlu, Bilkent University
Solution to Dining Philosophers (cont)
DiningPhilosophters.pickup (i);
DiningPhilosophers.putdown (i);
THINK…
}
Test(i)
Test(i+4 %5) Test(i+1 %5)
• Assume the following strategy is implemented regarding who will run after a
signal() is issued on a condition variable:
– “The process that calls signal() on a condition variable is blocked. It can
not be waken up if there is somebody running inside the monitor”.
• We would like to apply a priority based allocation. The process that will use the
resource for the shortest amount of time will get the resource first if there are
other processes that want the resource.
Processes or Threads
…. that want to use the resource
Resource
Each process should use resource between acquire() and release() calls.
• Assume we have a process A running in CPU 1 and holding a spin lock and
executing the critical region touching to some shared data.
• Assume at the same, another process B running in CPU 2 would like run a
critical region touching to the same shared data.
• B can wait on a semaphore, but this will cause B to sleep (a context switch is
needed; costly operation). However, critical section of A is short; It would be
better if B would busy wait for a while; then the lock would be available.
• Spin Locks are doing this. B can use a Spin Lock to wait (busy wait) until A will
leave the critical region and releases the Spin Lock. Since critical region is
short, B will not wait much.
f1() {… f2() {…
acquire_spin_lock_(X); acquire_spin_lock_(X);
…//critical region…. …//critical region….
…touch to SD (shared data); …touch to SD (shared data);
release_spin_lock(X); release_spin_lock(X); …
} }
CPU 1 CPU 2
• While process A is in the critical region, executing on CPU 1 and having the
lock (X set to 1), process A may be spinning on a while loop on CPU 2, waiting
for the lock to be become available (i.e. waiting X to become 0). As soon as
process A releases the lock (sets X to 0), process B can get the lock (test and
set X), and enter the critical region.
• Solaris
• Windows XP
• Linux
• Pthreads
• Linux:
– Prior to kernel Version 2.6, disables interrupts to implement short
critical sections
– Version 2.6 and later, fully preemptive
• Linux provides:
– semaphores
– spin locks