0% found this document useful (0 votes)
37 views31 pages

6 Process Synchronization

Uploaded by

Prajwal Kandel
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views31 pages

6 Process Synchronization

Uploaded by

Prajwal Kandel
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 31

Process Synchronization

Race Condition
 When several processes access and manipulate the same data
concurrently and the outcome of the execution depends on the
particular order in which the access takes place, is called a
race condition.
 To guard against the race condition , we need to ensure that
only one process at a time can be manipulating the shared
data.
 To make such a guarantee, we require that the processes be
synchronized in some way.
 Suppose that two processes A and B have access to a
shared variable “Balance”:

PROCESS A: PROCESS B:
Balance = Balance - 100 Balance = Balance – 200

 Further, assume that Process A and Process B are executing


concurrently in a time-shared, multi-programmed system.
Race Condition
 Observe: In a time-shared or multi-processing system the exact
instruction execution order cannot be predicted!
 Scenario 1:
Scenario 2:
A1. LOAD R1, BALANCE
A1. LOAD R1, BALANCE
A2. SUB R1, 100 A2. SUB R1, 100
A3. STORE BALANCE, R1 Context Switch!
Context Switch! B1. LOAD R1, BALANCE

B1. LOAD R1, BALANCE B2. SUB R1, 200


B3. STORE BALANCE, R1
B2. SUB R1, 200
Context Switch!
B3. STORE BALANCE, R1
A3. STORE BALANCE, R1
Balance is effectively decreased
Balance is effectively
by 300!
decreased by 100!
When multiple processes are accessing shared data
without access control the final result depends on the
execution order creating what we call race conditions.
 A serious problem for any concurrent system using
shared variables!
 We need Access Control using code sections that are
executed atomically.
An Atomic operation is one that completes in its entirety
without context switching (i.e. without interruption).
Critical-Section Problem
 Consider a system consisting of n processes {P0, PI, ...,
Pn}. Each process has a segment of code, called a critical
section, in which the process may be changing common
variables, updating a table, writing a file, and so on.
 The important feature of the system is that, when one
process is executing in its critical section, no other
process is to be allowed to execute in its critical section.
 That is, no two processes are executing in their critical
sections at the same time.
 The critical-section problem is to design a protocol that
the processes can use to cooperate. Each process must
request permission to enter its critical section.
Critical-Section Problem

 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.

The general structure of a typical process P, is shown in Figure.


while (1) {
….
entry section
critical section
exit section
remainder section
}
Solution to Critical-Section
Problem

1. Mutual Exclusion - If process Pi is executing in its critical


section, then no other processes can be executing in
their critical sections
2. Progress - If no process is executing in its critical section
and there exist some processes that wish to enter their
critical section, then the selection of the processes that
will enter the critical section next cannot be postponed
indefinitely
3. Bounded Waiting - A bound must exist on the number of
times that other processes are allowed to enter their
critical sections after a process has made a request to
enter its critical section and before that request is
granted
4. No assumptions may be made about the speeds or
number of CPUs
Peterson’s Solution
 We illustrate a classic software-based solution to the
critical-section problem known as Peterson's solution.
 Peterson's solution is restricted to two processes that
alternate execution between their critical sections and
remainder sections.
 Peterson's solution requires two data items to be shared
between the two processes:
 int turn;
 Boolean flag[2]
 The variable turn indicates whose turn it is to enter the
critical section.
 The flag array is used to indicate if a process is ready to
enter the critical section. flag[i] = true implies that
process Pi is ready!
 To enter the critical section, process Pi, first sets flag[i]
to be true and then sets turn to the value j, thereby
asserting that if the other process wishes to enter the
critical section, it can do so.
 If both processes try to enter at the same time, turn will
be set to both i and j at roughly the same time. Only one
of these assignments will last; the other will occur but
will be overwritten immediately.
 The eventual value of turn decides which of the two
processes is allowed to enter its critical section first.
Algorithm for Process Pi

while (true) {
flag[i] = TRUE;
turn = j;
while ( flag[j] && turn == j);

CRITICAL SECTION

flag[i] = FALSE;

REMAINDER SECTION

}
Synchronization Hardware
 we can state that any solution to the critical-section problem
requires a simple tool—a lock. Race conditions are prevented
by requiring that critical regions 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.
 Many systems provide hardware support for critical section
code
 Uniprocessors – could disable interrupts
 Currently running code would execute without preemption
 this solution is not as feasible in a multiprocessor
environment.
 Disabling interrupts on a multiprocessor can be time
consuming, as the Modern machines provide special atomic
hardware instructions message is passed to all the
processors. This message passing delays entry into each
critical section, and system efficiency decreases.
 Atomic = non-interruptable
 Either test memory word and set value
 Or swap contents of two memory words
TestAndSet Instruction

the test-and-set instruction is an instruction used to write


to a memory location and return its old value as a single
atomic (i.e., non-interruptible) operation. If multiple
processes may access the same memory, and if a process
is currently performing a test-and-set, no other process
may begin another test-and-set until the first process is
done
Definition:

boolean TestAndSet (boolean *target)


{
boolean rv = *target;
*target = TRUE;
return rv:
}
Solution using TestAndSet

 Shared boolean variable lock., initialized to false.


 Solution:

while (true) {
while ( TestAndSet (&lock ))
; /* do nothing

// critical section

lock = FALSE;

// remainder section

}
Swap Instruction

 Definition:

void Swap (boolean *a, boolean *b)


{
boolean temp = *a;
*a = *b;
*b = temp:
}
Solution using Swap
 Shared Boolean variable lock initialized to FALSE;
Each process has a local Boolean variable key.
 Solution:
while (true) {
key = TRUE;
while ( key == TRUE)
Swap (&lock, &key );

// critical section

lock = FALSE;

// remainder section

}
Semaphore
 A semaphore S is an integer variable that, apart from
initialization, is accessed only through two standard atomic
operations: wait () and signal ().
 Originally called P() and V()
 The value of the semaphore S is the number of units of the
resource that are currently available. The P operation wastes
time or sleeps until a resource protected by the semaphore
becomes available. The V operation is the inverse: it makes a
resource available again after the process has finished using it.
 wait(): Decrements the value of semaphore variable by 1. If the
value becomes negative, the process executing wait() is
blocked, i.e., added to the semaphore's queue.
 signal(): Increments the value of semaphore variable by 1. After
the increment, if the value is negative, it transfers a blocked
process from the semaphore's queue to the ready queue.
 wait (S) {
while S <= 0
; // no-op
S--; }
Semaphore as General Synchronization
Tool

 Binary Semaphore: A binary semaphore must be


initialized with 1 or 0, and the implementation of wait and
signal operations must alternate. If the semaphore is
initialized with 1, then the first completed operation must
be wait. If the semaphore is initialized with 0, then the
first completed operation must be signal. Both wait and
signal operations can be blocked, if they are attempted in
a consecutive manner.
 Counting Semaphore: A counting semaphore can be
considered as a pool of permits. A thread used wait
operation to request a permit. If the pool is empty, the
thread waits until a permit becomes available. A thread
uses signal operation to return a permit to the pool. A
counting semaphore can take any initial value.
Using Semaphores
 Semaphores can be used to implement critical sections or
to enforce certain scheduling constraints.
 Critical sections are typically built by using semaphores
that are initialized to 1. In this case, one process can call
wait() in order to enter the critical section, preventing any
other processes from passing the wait condition.
 Once the process finishes the critical section, it calls
signal() which will allow the next process to enter the
critical section. In some cases, it is useful to start the
semaphore with a value greater than 1.
 This allows multiple processes to enter the critical
section at once.
Semaphore Implementation with no Busy waiting
(Cont.)

 Implementation of wait:

wait (S){
value--;
if (value < 0) {
add this process to waiting queue
block(); }
}

 Implementation of signal:

Signal (S){
value++;
if (value <= 0) {
remove a process P from the waiting
queue
wakeup(P); }
}
Classical Problems of
Synchronization
Readers-Writers Problem
 In computer science, the readers-writers problems are
examples of a common computing problem in concurrency.
The problem deals with situations in which many threads must
access the same shared memory at one time, some reading
and some writing, with the natural constraint that no process
may access the data for reading or writing while another
process is in the act of writing to it.
 In particular, it is allowed for two readers to access the share
at the same time.
 This is useful in many systems in order to optimize
performance–if many threads want to read a data structure
there is no need to limit them to accessing the data one at a
time.
 However, synchroniztion still must be insured in the case of a
writer–only one writer should be able to write at a time, and no
reader should run concurrently with the writer.
 Clearly, using a simple set of locks like we have described
previously will be inefficient
Three types of solutions

There are several different solutions which can be used to


build a Readers/Writers system.
 Reader preferred: waiting readers go before waiting
writers. A constraint is added so that no reader shall be kept
waiting if the share is currently opened for reading.
Writer preferred: waiting writers go before waiting readers.
A constraint is added so that no writer, once added to the
queue, shall be kept waiting longer than absolutely
necessary.
 Neither preferred: try to treat readers and writers fairly (a
simple queue is not good enough; we want parallel readers
whenever possible).
Reader writer problem using
semaphore
Int reader_count = 0 Void writer( void )
Semaphore mutex = 1; {
Semaphore db =1; while(true)
Void reader( void ) {
{ down(db);
while(true) [write data in database] critical
{ section
down(mutex); up(db);
reader_count++;
if(reader_count==1) then down(db); }
up(mutex) }
[ access data code run] critical section
down(mutex) Case 1 : R – W - here problem occur
reader_count--; Case 2 : W – R - here problem occur
if(reader_count == 0) then up(db); Case 3 : W – W - here problem occur
up(mutex); Case 4 : R – R - here no problem occur
[ process read data or remaining code]
}
}

perating System Concepts – 7th Edition, Feb 8, 2005 6.23 Silberschatz, Galvin and Gagne
Dining-Philosophers Problem

 Shared data
 Bowl of rice (data set)
 Semaphore chopstick [5] initialized to 1
Dining-Philosophers Problem
 The Dining Philosophers Problem is an illustrative
example of a common computing problem in
concurrency.
 The dining philosophers problem describes a group of
philosophers sitting at a table doing one of two things
 Eating or thinking. While eating, they are not thinking,
and while thinking, they are not eating. The
 Philosophers sit at a circular table with a large bowl of
spaghetti in the center.
 A chopstick is placed in between each philosopher,
thus each philosopher has one chopstick to his or her
left and one chopstick to his or her right.
 As spaghetti is difficult to serve and eat with a single
chopstick, it is assumed that a philosopher must eat
with two chopsticks. The philosopher can only use the
chopstick on his or her immediate left or right.
 The philosophers never speak to each other, which
creates a dangerous possibility of deadlock.
 Deadlock could occur if every philosopher holds a left
chopstick and waits perpetually for a right chopstick
(or vice versa). Originally used as a means of
illustrating the problem of deadlock, this system
reaches deadlock
 when there is a ’cycle of unwarranted requests’. In this
case philosopher P1 waits for the chopstick grabbed by
philosopher P2 who is waiting for the chopstick of
philosopher P3 and so forth, making a circular chain
Solution to the Dining Philosophers
problem

 A simple solution is to first wait for the left chopstick


using a semaphore. After successfully acquiring it, wait
for the right chopstick. After both chopsticks have been
acquired, eat.
 When done with eating, release the two chopsticks in the
same order, one by one, by calls to signal.
 Though simplistic, this solution may still lead to a
deadlock when every philosopher around the table is
holding his/her left chopstick and waiting for the right
one.
 A variation of this solution, makes a philosopher release
the left chopstick, if it can’t acquire the right one.
 The correct solution is to let a philosospher acquire both
the chopsticks or none. This can be done within a monitor
wherein only one philosopher is allowed to check for
availability of chopsticks at a time
Example with deadlock condition

Void philosopher(void)
{ use semaphore S[N]
While ( true)
{
thinking();
take_fork(i); //take left fork
Take _fork((i+1)%N) ; // take right fork, here N is number of
philosopher
Eat();
Put_fork(i); // put left fork
Put_fork((i+1)%N); // put right fork
}
}

perating System Concepts – 7th Edition, Feb 8, 2005 6.28 Silberschatz, Galvin and Gagne
Monitors
 Monitors: A high-level data abstraction tool that automatically
generates atomic operations on a given data structure. A
monitor has:
 Shared data.
 A set of atomic operations on that data.
 A set of condition variables.
 A monitor is a language construct. Compare this with
semaphores, which are usually an OS construct
 A monitor can be viewed as a class that encapsulates a set of
shared data as well as the operations on that data (e.g. the
critical sections).
 A monitor is a collection of procedures, variables, and data
structures grouped together. Processes can call the monitor
procedures but cannot access the internal data structures.
 Only one process at a time may be be active in a monitor.
 Active in a monitor means in ready queue or CPU with the
program counter somewhere in a monitor method.
 A monitor contains a lock and a set of condition variables.
The lock is used to enforce mutual exclusion.
 The condition variables are used as wait queues so that
other threads can sleep while the lock is held.
 Thus condition variables make it so that if a thread can
safely go to sleep and be guaranteed that when they wake
up they will have control of the lock again.
 The representation of a monitor type cannot be used
directly by the various processes. Thus, a procedure
defined within a monitor can access only those variables
declared locally within the monitor and its formal
parameters.
 Similarly, the local variables of a monitor can be accessed
by only the local procedures.
monitor class Account {
private int balance := 0
invariant balance >= 0

public method boolean withdraw(int amount)


precondition amount >= 0
{
if balance < amount then return false
else { balance := balance - amount ; return true }
}

public method deposit(int amount)


precondition amount >= 0
{
balance := balance + amount }}

You might also like