Synchronization in Operating Systems
Synchronization in Operating Systems
1
[Link]–I SEM CSE (OS) UNIT-III
We would arrive at this incorrect state because we allowed both processesto manipulate the variable
counter concurrently. A situation like this, whereseveral processes access and manipulate the same data
concurrently and theoutcome of the execution depends on the particular order in which the accesstakes place, is
called a race condition. To guard against the race conditionabove, we need to ensure that only one process at a
time can be manipulatingthe variable counter. To make such a guarantee, we require that the processes
be synchronized in some way.
1. The Critical-Section Problem
Consider a system consisting of n processes {PQ, PI, ..., P,,~\}. Each processhas a segment of code,
called a critical section, in which the process maybe changing common variables, updating a table, writing a
file, and so [Link] important feature of the system is that, when one process is executing inits critical section,
no other process is to be allowed to execute in its criticalsection. That is, no two processes are executing in their
critical sections at thesame 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 criticalsection. The section of code
implementing this request is the entry section. Thecritical section may be followed by an exit section. The
remaining code is theremainder section. The general structure of a typical process P, is shown inFigure 6.1.
The entry section and exit section are enclosed in boxes to highlightthese important segments of code.
do{
\\entry section
critical section
\\exit section
\\remainder section
} while (TRUE);
General structure of a typical process Pi.
A solution to the critical-section problem must satisfy the following threerequirements:
1. Mutual exclusion. If process P; is executing in its critical section, then noother processes can be executing in
their critical sections.
2. Progress. If no process is executing in its critical section and someprocesses wish to enter their critical
sections, then only those processesthat are not executing in their remainder sections can participate in the
decision on which will enter its critical section next, and this selectioncannot be postponed indefinitely.
3. Bounded waiting. There exists a bound, or limit, on the number of timesthat other processes are allowed to
enter their critical sections after aprocess has made a request to enter its critical section and before that
request is granted.
2. Peterson's Solution
It is a classic software-based solution to the critical-sectionproblem known as Peterson's solution.
Peterson's solution is restricted to two processes that alternate executionbetween their critical sections and
remainder sections. The processes arenumbered Po and Pi. For convenience, when presenting P,-, we use Pj
todenote the other process; that is, j equals 1 — i.
Peterson's solution requires two data items to be shared between the twoprocesses:
int turn;
boolean f l a g [2] •
The variable turn indicates whose turn it is to enter its critical section. That is,if turn == i, then process
P; is allowed to execute in its critical section. Theflag array is used to indicate if a process is ready to enter its
critical [Link] example, if f lag[i] is true, this value indicates that P; is ready to enterits critical section.
To enter the critical section, process P, first sets flag[i] to be true andthen sets turn to the value j, thereby
asserting that if the other process wishesto enter the critical section, it can do so. If both processes try to enter at
thesame time, turn will be set to both i and j at roughly the same time. Onlyone of these assignments will last;
the other will occur but will be overwrittenimmediately. The eventual value of turn decides which of the two
processesis allowed to enter its critical section first.
do
{
flag[i] = TRUE;
turn = j ;
while (flag[j] turn == j ) ;
critical section
flag[i] = FALSE;
remainder section
} while (TRUE);
The structure of process P-, in Peterson's solution.
2
[Link]–I SEM CSE (OS) UNIT-III
We now prove that this solution is correct. We need to show that:
1. Mutual exclusion is preserved.
2. The progress requirement is satisfied.
3. The bounded-waiting requirement is met.
To prove property 1, we note that each P; enters its critical section onlyif either flag[j] == false or turn --
i. Also note that, if both processescan be executing in their critical sections at the same time, then flag [0] ==
flag [1] == true. These two observations imply that Po and Pi could not havesuccessfully executed their while
statements at about the same time, since thevalue of turn can be either 0 or 1 but cannot be both. Hence, one of
the processes—say Pj—must have successfully executed the while statement, whereas P,had to execute at least
one additional statement ("turn == j"). However, since,at that time, f lag[j] == true, and turn == j, and this
condition will persistas long as Pj is in its critical section, the result follows: Mutual exclusion ispreserved.
To prove properties 2 and 3, we note that a process P, can be prevented fromentering the critical section
only if it is stuck in the while loop with the conditionflag [j] == true and turn == j; this loop is the only one
possible. If P; is notready to enter the critical section, then flag [j] == false, and P; can enter itscritical section. If
Pj has set flag [j ] to true and is also executing in its whilestatement, then either turn == i or turn == j . If turn
== i, then P, will enterthe critical section. If turn == j, then Pj will enter the critical section. However,once P;
exits its critical section, it will reset f lag[j] to false, allowing P, toenter its critical section. If Pj resets flag [j ] to
true, it must also set turn to i.
Thus, since P, does not change the value of the variable turn while executingthe while statement, P,- will
enter the critical section (progress) after at mostone entry by P/ (bounded waiting).
3. Synchronization Hardware
We have just described one software-based solution to the critical-sectionproblem. We explore several
more solutions to thecritical-section problem using techniques ranging from hardware to softwarebasedAPIs
available to application programmers. Hardware features can make any programming task easier and improve
system efficiency. In this section, we present some simple hardware instructionsthat are available on many
systems and show how they can be used effectivelyin solving the critical-section problem.
The critical-section problem could be solved simply in a uniprocessor environment, we could be sure
that the current sequenceof instructions would be allowed to execute in order without preemption.
Unfortunately, this solution is not as feasible in a multiprocessor [Link] interrupts on a
multiprocessor can be time consuming, as themessage is passed to all the processors. This message passing
delays entry intoeach critical section, and system efficiency decreases.
boolean TestAndSet(boolean *target) {
boolean rv = *target;
*target = TRUE;
return rv;
The definition of the TestAndSet () instruction.
do {
while (TestAndSetLock(&lock) )
; // do nothing
// critical section
lock = FALSE;
// remainder section
}while (TRUE);
Mutual-exclusion implementation with TestAndSet ( ) .
The TestAndSet() instruction can be defined as shown in Figure . The important characteristic is that
this instruction is executed [Link], if two TestAndSet C) instructions are executed simultaneously
(each ona different CPU), they will be executed sequentially in some arbitrary order. Ifthe machine supports the
TestAndSet () instruction, then we can implementmutual exclusion by declaring a Boolean variable lock,
initialized to [Link] structure of process P, is shown in above Figure.
The SwapO instruction, in contrast to the TestAndSet0 instruction,operates on the contents of two
words; it is defined as shown in Figure .Like the TestAndSet 0 instruction, it is executed atomically. If the
machinesupports the SwapO instruction, then mutual exclusion can be provided asfollows. A global Boolean
variable lock is declared and is initialized to [Link] addition, each process has a local Boolean variable key.
The structure ofprocess P, is shown in Figure .
3
[Link]–I SEM CSE (OS) UNIT-III
void Swap(boolean *a, boolean *b)
{
boolean temp = *a;
*a = *b;
*b = temp;
}
The definition of the Swap () instruction.
do { ,
key = TRUE;
while (key == TRUE)
Swap (&lock, &key) ,-
// critical section
lock = FALSE;
// remainder section
}while (TRUE);
Mutual-exclusion implementation with the SwapO instruction.
4. Semaphores
The various hardware-based solutions to the critical-section problem (usingthe TestAndSetC) and
SwapO instructions) arecomplicated for application programmers to use. To overcome this difficulty,we can use
a synchronization tool called a semaphore.
A semaphore S is an integer variable that, apart from initialization, isaccessed only through two standard
atomic operations: wait () and signal ().The waitO operation was originally termed P (from the Dutch probercn,
"totest"); signal () was originally called V (from verhogen, "to increment"). Thedefinition of wait() is as
follows:
wait(S) {
while (S <= 0)
; // no-op
S--;
}
The definition of signal () is as follows:
signal(S) {
S++;
}
All the modifications to the integer value of the semaphore in the wait ()and signal() operations must be
executed indivisibly. That is, when oneprocess modifies the semaphore value, no other process can
simultaneouslymodify that same semaphore value.
In addition, in the case of wait(S), thetesting of the integer value of S (S < 0), and its possible
modification (S—),must also be executed without interruption. We shall see how these operationscan be
implemented in Section 6.5.2; first, let us see how semaphores can beused.
Operating systems often distinguish between counting and binary [Link] value of a counting
semaphore can range over an unrestricted [Link] value of a binary semaphore can range only between 0
and 1. On somesystems, binary semaphores are known as mutex locks, as they are locks thatprovide mutual
Exclusion.
We can use binary semaphores to deal with the critical-section problem formultiple processes. The n
processes share a semaphore, mutex, initialized to [Link] process P, is organized as shown in Figure.
do {
wait(mutex);
// critical section
signal(mutex);
// remainder section
}while (TRUE);
Mutual-exclusion implementation with semaphores.
Counting semaphores can be used to control access to a given resourceconsisting of a finite number of
instances. The semaphore is initialized to thenumber of resources available. Each process that wishes to use a
4
[Link]–I SEM CSE (OS) UNIT-III
resourceperforms a waitQ operation on the semaphore (thereby decrementing thecount). When a process
releases a resource, it performs a signal () operation(incrementing the count). When the count for the semaphore
goes to 0, allresources are being used. After that, processes that wish to use a resource willblock until the count
becomes greater than 0.
The main disadvantage of the semaphore definition given here is that it requiresbusy waiting. While a
process is in its critical section, any other process thattries to enter its critical section must loop continuously in
the entry code. Thiscontinual looping is clearly a problem in a real multiprogramming system,where a single
CPU is shared among many processes. Busy waiting wastesCPU cycles that some other process might be able
to use productively. Thistype of semaphore is also called a spinlock because the process "spins" whilewaiting
for the lock.
To overcome the need for busy waiting, we can modify the definition ofthe wait () and signal ()
semaphore operations. When a process executes thewait () operation and finds that the semaphore value is not
positive, it mustwait. However, rather than engaging in busy waiting, the process can blockitself. The block
operation places a process into a waiting queue associatedwith the semaphore, and the state of the process is
switched to the waitingstate. Then control is transferred to the CPU scheduler, which selects anotherprocess to
execute.
A process that is blocked, waiting on a semaphore S, should be restartedwhen some other process
executes a signal() operation. The process isrestarted by a wakeup () operation, which changes the process from
the waitingstate to the ready state. The process is then placed in the ready queue.
To implement semaphores under this definition, we define a semaphore asa "C" struct:
typedef struct {
int value;
struct process *list;
} semaphore;
Each semaphore has an integer value and a list of processes l i s t . Whena process must wait on a
semaphore, it is added to the list of processes. Asignal () operation removes one process from the list of waiting
processesand awakens that process.
The wait () semaphore operation can now be defined as
wait(semaphore *S) {
S->value—;
if (S->value < 0) {
add this process to S->list;
block();
} }
5
[Link]–I SEM CSE (OS) UNIT-III
event in questionis the execution of a signal() operation. When such a state is reached, theseprocesses are said to
be deadlocked.
To illustrate this, we consider a system consisting of two processes, P1 and P2 , each accessing two
semaphores, S and Q, set to the value 1:
P1 P2
wait(S); wait(Q);
wait(Q); wait(S);
--------- -----------
--------- ------------
signal(S); signal(Q);
signal(Q); signal(S);
Suppose that P1executes wait (S) and then P2 executes wait (Q). When P1 executes wait(Q), it must
wait until P2 executes signal(Q). Similarly, whenP2 executes wait(S), it must wait until P1 executes signal(S).
Since thesesignal () operations cannot be executed, P1 and P2 are deadlocked.
We say that a set of processes is in a deadlock state when every process inthe set is waiting for an event
that can be caused only by another process in theset.
Another problem related to deadlocks is indefinite blocking, or starvation,a situation in which
processes wait indefinitely within the [Link] blocking may occur if we add and remove processes
from the listassociated with a semaphore in LIFO (last-in, first-out) order.
5. Classic Problems of Synchronization
In this section, we present a number of synchronization problems as examples. In our solutionsto the
problems, we use semaphores for synchronization.
The Bounded-Buffer Problem
The bounded-buffer problem is commonlyused to illustrate the power of synchronization primitives.
We assume that the pool consists of n buffers, each capable of holdingone item.
The mutex semaphore provides mutual exclusion for accesses to thebuffer pool and is initialized to the
value 1.
The empty and f u l l semaphorescount the number of empty and full buffers. The semaphore empty is
initializedto the value n; the semaphore f u l l is initialized to the value 0.
The code for the producer process is shown in below Figure ; the code forthe consumer process is shown
in below Figure. We can interpret this code as the producerproducing full buffers for the consumer or as the
consumer producing emptybuffers for the producer.
do {
// produce an item in nextp
wait(empty);
wait(mutex);
// add nextp to buffer
signal(mutex);
signal(full);
}while (TRUE) ,-
The structure of the producer process.
do {
wait(full);
wait(mutex);
// remove an item from buffer to nextc
signal(mutex);
signal(empty);
// consume the item in nextc
}while (TRUE);
The structure of the consumer process.
6
[Link]–I SEM CSE (OS) UNIT-III
Obviously, if two readers access the shared data simultaneously, noadverse affects will result.
However, if a writer and some other thread (eithera reader or a writer) access the database simultaneously,
chaos may [Link] ensure that these difficulties do not arise, we require that the writershave exclusive access
to the shared database. This synchronization problem isreferred to as the readers-writers problem.
The readers-writers problem, requires that no readerwill be kept waiting unless a writer has already
obtained permission to usethe shared object. In other words, no reader should wait for other readers to
finish simply because a writer is waiting.
In the solution to the readers-writers problem, the reader processesshare the following data structures:
semaphore mutex, wrt;
int readcount;
The semaphores mutex and wrt are initialized to 1; readcount is initializedto 0. The semaphore wrt is
common to both reader and writer processes.
The mutex semaphore is used to ensure mutual exclusion when the variablereadcount is updated. The
readcount variable keeps track of how manyprocesses are currently reading the object.
The semaphore wrt functions as amutual-exclusion semaphore for the writers. It is also used by the first reader
that enters or exits the critical section. It is not used by readers whoenter or exit while other readers are in their
critical sections or last.
do {
wait(wrt);
// writing is performed
signal (wrt) ,-
}while (TRUE);
The structure of a writer process.
do {
wait(mutex);
readcount + + ;
if (readcount == 1)
wait(wrt);
signal(mutex);
// reading is performed
wait (mutex) ,-
readcount--;
if (readcount == 0)
signal(wrt);
signal(mutex);
Jwhile (TRUE);
The structure of a reader process.
The code for a writer process is shown in above Figure; the code for a readerprocess is shown in above
Figure. Note that, if a writer is in the critical sectionand n readers are waiting, then one reader is queued on wrt,
and n — 1 readersare queued on mutex. Also observe that, when a writer executes signal (wrt),we may resume
the execution of either the waiting readers or a single waitingwriter. The selection is made by the scheduler.
The Dining-Philosophers Problem
Consider five philosophers who spend their lives thinking and eating. Thephilosophers share a circular table
surrounded by five chairs, each belongingto one philosopher. In the center of the table is a bowl of rice, and the
table is laidwith five single chopsticks. When a philosopher thinks, she doesnot interact with her colleagues.
From time to time, a philosopher gets hungryand tries to pick up the two chopsticks that are closest to her (the
chopsticksthat are between her and her left and right neighbors). A philosopher may pickup only one chopstick
at a time. Obviously, she cannot pick up a chopstick thatis already in the hand of a neighbor. When a hungry
philosopher has both herchopsticks at the same time, she eats without releasing her chopsticks. Whenshe is
finished eating, she puts down both of her chopsticks and starts thinkingagain.
6. Monitors
Although semaphores provide a convenient and effective mechanism forprocess synchronization, using
them incorrectly can result in timing errors that are difficult to detect.
To illustrate how, we review the semaphore solution to the criticalsectionproblem. All processes share a
semaphore variable mutex, which [Link] to 1. Each process must execute wait (mutex) before entering
thecritical section and signal (mutex) afterward. If this sequence is not observed,two processes may be in their
critical sections simultaneously.
Let us examinethe various difficulties that may result.
• Suppose that a process interchanges the order in which the wait(j andsignal () operations on the semaphore
mutex are executed, resulting inthe following execution:
signal(mutex);
critical section
wait(mutex);
In this situation, several processes may be executing in their critical sectionssimultaneously, violating the
rmitual-exclusion requirement.
• Suppose that a process replaces signal (mutex) with wait (mutex). Thatis, it executes
wait(mutex);
critical section
wait(mutex);
In this case, a deadlock will occur.
• Suppose that a process omits the wait (mutex), or the signal (mutex), orboth. In this case, either mutual
exclusion is violated or a deadlock willoccur.
To deal with such errors, researchers have developed high-level languageconstructs. In this section, we
describe one fundamental high-level synchronizationconstruct—the monitor type.
Usage
A type, or abstract data type, encapsulates private data with public methodsto operate on that data. A
monitor type presents a set of programmer-definedoperations that are provided mutual exclusion within the
monitor. The syntax of a monitor is shown in Figure.
monitor monitor name f
{
II shared variable declarations
procedure PI ( . . . ) {
}
p r o c e d u r e P 2 ( . . . ) {…..}
::::::::::::::::::::::::::::::::::::::::
p r o c e d u r e P n ( . . . ) {….}
9
[Link]–I SEM CSE (OS) UNIT-III
initializationcode(...){
……..
}
}
Syntax of a monitor.
Thus, a procedure defined within a monitor can access onlythose variables declared locally within the
monitor and its formal [Link], the local variables of a monitor can be accessed by only the
localprocedures.
The monitor construct ensures that only one process at a time can beactive within the monitor.
Consequently, the monitor construct, as defined so far, is not sufficiently powerful formodeling some
synchronization schemes. For this purpose, we need to defineadditional synchronization mechanisms. These
mechanisms are provided bythe condition construct. A programmer who needs to write a tailor-
madesynchronization scheme can define one or more variables of type condition:
condition x, y;
The only operations that can be invoked on a condition variable are wait ()and signal(). The operation
[Link] ;
means that the process invoking this operation is suspended until anotherprocess invokes
[Link]();
The x. signal () operation resumes exactly one suspended process. If noprocess is suspended, then the signal ()
operation has no effect; that is, thestate of x is the same as if the operation had never been executed. Contrast
this operation with the signal () operation associated withsemaphores, which always affects the state of the
semaphore.
12