You are on page 1of 33

Kalinga University

Department of Computer Science Engineering

Course: M.Tech Semester- I


Subject: Advanced Operating System
Subject Code: MTOOSD103

Unit-2
Process Synchronization is the task of coordinating the execution of processes in a way that no two
processes can have access to the same shared data and resources. It is specially needed in a multi-
process system when multiple processes are running together, and more than one processes try to gain
access to the same shared resource or data at the same time. This can lead to the inconsistency of
shared data. So the change made by one process not necessarily reflected when other processes
accessed the same shared data. To avoid this type of inconsistency of data, the processes need to be
synchronized with each other.

How Process Synchronization Works?


For Example, process A changing the data in a memory location while another process B is trying to
read the data from the same memory location. There is a high probability that data read by the second
process will be erroneous.

Sections of a Program
Here, are four essential elements of the critical section:

10 Most Common Interview Questions and Answers ????

 Entry Section: It is part of the process which decides the entry of a particular process.
 Critical Section: This part allows one process to enter and modify the shared variable.
 Exit Section: Exit section allows the other process that are waiting in the Entry Section, to
enter into the Critical Sections. It also checks that a process that finished its execution should
be removed through this Section.

1
 Remainder Section: All other parts of the Code, which is not in Critical, Entry, and Exit
Section, are known as the Remainder Section.

What is Critical Section Problem?


A critical section is a segment of code which can be accessed by a signal process at a specific point of
time. The section consists of shared data resources that required to be accessed by other processes.

 The entry to the critical section is handled by the wait() function, and it is represented as P().
 The exit from a critical section is controlled by the signal() function, represented as V().

In the critical section, only a single process can be executed. Other processes, waiting to execute their
critical section, need to wait until the current process completes its execution.

Rules for Critical Section


The critical section need to must enforce all three rules:

 Mutual Exclusion: Mutual Exclusion is a special type of binary semaphore which is used for
controlling access to the shared resource. It includes a priority inheritance mechanism to avoid
extended priority inversion problems. Not more than one process can execute in its critical
section at one time.
 Progress: This solution is used when no one is in the critical section, and someone wants in.
Then those processes not in their reminder section should decide who should go in, in a finite
time.
 Bound Waiting: When a process makes a request for getting into critical section, there is a
specific limit about number of processes can get into their critical section. So, when the limit is
reached, the system must allow request to the process to get into its critical section.

Solutions:- To The Critical Section


In Process Synchronization, critical section plays the main role so that the problem must be solved.

Here are some widely used methods to solve the critical section problem.

Peterson Solution
Peterson’s solution is widely used solution to critical section problems. This algorithm was developed
by a computer scientist Peterson that’s why it is named as a Peterson’s solution.

In this solution, when a process is executing in a critical state, then the other process only executes the
rest of the code, and the opposite can happen. This method also helps to make sure that only a single
process runs in the critical section at a specific time.

Example

2
PROCESS Pi
FLAG[i] = true
while( (turn != i) AND (CS is !free) ){ wait;
}
CRITICAL SECTION FLAG[i] = false
turn = j; //choose another process to go to CS

 Assume there are N processes (P1, P2, … PN) and every process at some point of time requires
to enter the Critical Section
 A FLAG[] array of size N is maintained which is by default false. So, whenever a process
requires to enter the critical section, it has to set its flag as true. For example, If Pi wants to
enter it will set FLAG[i]=TRUE.
 Another variable called TURN indicates the process number which is currently wating to enter
into the CS.
 The process which enters into the critical section while exiting would change the TURN to
another number from the list of ready processes.
 Example: turn is 2 then P2 enters the Critical section and while exiting turn=3 and therefore P3
breaks out of wait loop.

Synchronization Hardware
Some times the problems of the Critical Section are also resolved by hardware. Some operating system
offers a lock functionality where a Process acquires a lock when entering the Critical section and
releases the lock after leaving it.

So when another process is trying to enter the critical section, it will not be able to enter as it is locked.
It can only do so if it is free by acquiring the lock itself.

Mutex Locks
Synchronization hardware not simple method to implement for everyone, so strict software method
known as Mutex Locks was also introduced.

In this approach, in the entry section of code, a LOCK is obtained over the critical resources used
inside the critical section. In the exit section that lock is released.

Semaphore Solution

3
Semaphore is simply a variable that is non-negative and shared between threads. It is another
algorithm or solution to the critical section problem. It is a signaling mechanism and a thread that is
waiting on a semaphore, which can be signaled by another thread.

It uses two atomic operations, 1)wait, and 2) signal for the process synchronization.

Example
WAIT ( S ):
while ( S <= 0 );
S = S - 1;
SIGNAL ( S ):
S = S + 1;

Critical Section Problem


When more than one processes access a same code segment that segment is known as critical section.
Critical section contains shared variables or resources which are needed to be synchronized to maintain
consistency of data variable.

In simple terms a critical section is group of instructions/statements or region of code that need to be
executed atomically (read this post for atomicity), such as accessing a resource (file, input or output port,
global data, etc.).

In concurrent programming, if one thread tries to change the value of shared data at the same time as
another thread tries to read the value (i.e. data race across threads), the result is unpredictable.
The access to such shared variable (shared memory, shared files, shared port, etc…) to be synchronized.
Few programming languages have built-in support for synchronization.
It is critical to understand the importance of race condition while writing kernel mode programming (a
device driver, kernel thread, etc.). since the programmer can directly access and modifying kernel data
structures.

4
A simple solution to the critical section can be thought as shown below,
acquireLock();
Process Critical Section
releaseLock();
A thread must acquire a lock prior to executing a critical section. The lock can be acquired by only one
thread. There are various ways to implement locks in the above pseudo code.

Peterson’s solution
Peterson’s solution provides a good algorithmic description of solving the critical-section problem and
illustrates some of the complexities involved in designing software that addresses the requirements of
mutual exclusion, progress, and bounded waiting.

do {

   flag[i] = true;

   turn = j;

   while (flag[j] && turn == j);

   /* critical section */

   flag[i] = false;

   /* remainder section */

while (true);

The structure of process Pi in Peterson’s solution. This solution is restricted to two processes that
alternate execution between their critical sections and remainder sections. The processes are numbered
P0 and P1. We use Pj for convenience to denote the other process when Pi is present; that is, j equals 1
− I, Peterson’s solution requires the two processes to share two data items −

int turn;
boolean flag[2];

The variable turn denotes whose turn it is to enter its critical section. I.e., if turn == i, then process Pi
is allowed to execute in its critical section. If a process is ready to enter its critical section, the flag
array is used to indicate that. For E.g., if flag[i] is true, this value indicates that Pi is ready to enter its
critical section. With an explanation of these data structures complete, we are now ready to describe
the algorithm shown in above. 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. Turn will be set to both i and j at roughly the same time, if both processes try to
enter at the same time. Only one of these assignments will occur ultimately; the other will occur but
will be overwritten immediately. The final value of turn determines which of the two processes is

5
allowed to enter its critical section first. We now prove that this solution is correct. We need to show
that −

 Mutual exclusion is preserved.


 The progress requirement is satisfied.
 The bounded-waiting requirement is met.
To prove 1, we note that each Pi enters its critical section only if either flag[j] == false or turn == i.
Also note that, if both processes can be executing in their critical sections at the same time, then
flag[0] == flag[1] == true. These two observations indicate that P0 and P1 could not have successfully
executed their while statements at about the same time, since the value 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 Pi had to execute at least one additional statement (“turn == j”). However, at that
time, flag[j] == true and turn == j, and this condition will persist as long as Pj is in its critical section;
as a result, mutual exclusion is preserved.
To prove properties 2 and 3, we note that if a process is stuck in the while loop with the condition
flag[j] == true and turn == j, process Pi can be prevented from entering the critical section only; this
loop is the only one possible. flag[j] will be == false, and Pi can enter its critical section if Pj is not
ready to enter the critical section. If Pj has set, flag[j] = true and is also executing in its while
statement, then either turn == i or turn == j. If turn == i, Pi will enter the critical section then. Pj will
enter the critical section, If turn == j. Although once Pj exits its critical section, it will reset flag[j] to
false, allowing Pi to enter its critical section. Pj must also set turn to i, if Pj resets flag[j] to true.
Hence, since Pi does not change the value of the variable turn while executing the while statement, Pi
will enter the critical section (progress) after at most one entry by Pj (bounded waiting).
Disadvantage
 Peterson’s solution works for two processes, but this solution is best scheme in user mode for
critical section.
 This solution is also a busy waiting solution so CPU time is wasted. So that “SPIN
LOCK” problem can come. And this problem can come in any of the busy waiting solution.

Semaphores
Semaphores are integer variables that are used to solve the critical section problem by using two
atomic operations, wait and signal that are used for process synchronization.
The definitions of wait and signal are as follows −

 Wait
The wait operation decrements the value of its argument S, if it is positive. If S is negative or
zero, then no operation is performed.

wait(S)

   while (S<=0);

6
   S--;

 Signal
The signal operation increments the value of its argument S.

signal(S)

   S++;

Types of Semaphores
There are two main types of semaphores i.e. counting semaphores and binary semaphores. Details
about these are given as follows −

 Counting Semaphores
These are integer value semaphores and have an unrestricted value domain. These semaphores
are used to coordinate the resource access, where the semaphore count is the number of
available resources. If the resources are added, semaphore count automatically incremented
and if the resources are removed, the count is decremented.

 Binary Semaphores
The binary semaphores are like counting semaphores but their value is restricted to 0 and 1.
The wait operation only works when the semaphore is 1 and the signal operation succeeds
when semaphore is 0. It is sometimes easier to implement binary semaphores than counting
semaphores.
Advantages of Semaphores
Some of the advantages of semaphores are as follows −

 Semaphores allow only one process into the critical section. They follow the mutual exclusion
principle strictly and are much more efficient than some other methods of synchronization.
 There is no resource wastage because of busy waiting in semaphores as processor time is not
wasted unnecessarily to check if a condition is fulfilled to allow a process to access the critical
section.
 Semaphores are implemented in the machine independent code of the microkernel. So they are
machine independent.
Disadvantages of Semaphores
Some of the disadvantages of semaphores are as follows −

7
 Semaphores are complicated so the wait and signal operations must be implemented in the
correct order to prevent deadlocks.
 Semaphores are impractical for last scale use as their use leads to loss of modularity. This
happens because the wait and signal operations prevent the creation of a structured layout for
the system.
 Semaphores may lead to a priority inversion where low priority processes may access the
critical section first and high priority processes later.

Test and Set Lock –


 
Test and Set Lock (TSL) is a synchronization mechanism.
It uses a test and set instruction to provide the synchronization among the processes executing
concurrently.
 

Test-and-Set Instruction
 
It is an instruction that returns the old value of a memory location and sets the memory
location value to 1 as a single atomic operation.
If one process is currently executing a test-and-set, no other process is allowed to begin
another test-and-set until the first process test-and-set is finished.

 
It is implemented as-
 

 
Initially, lock value is set to 0.
Lock value = 0 means the critical section is currently vacant and no process is present inside it.
Lock value = 1 means the critical section is currently occupied and a process is present inside
it.

8
 
Working-
 
This synchronization mechanism works as explained in the following scenes-
 
Scene-01:
 
Process P0 arrives.
It executes the test-and-set(Lock) instruction.
Since lock value is set to 0, so it returns value 0 to the while loop and sets the lock value to 1.
The returned value 0 breaks the while loop condition.
Process P0 enters the critical section and executes.
Now, even if process P0 gets preempted in the middle, no other process can enter the critical
section.
Any other process can enter only after process P0 completes and sets the lock value to 0.
 
Scene-02:
 
Another process P1 arrives.
It executes the test-and-set(Lock) instruction.
Since lock value is now 1, so it returns value 1 to the while loop and sets the lock value to 1.
The returned value 1 does not break the while loop condition.
The process P1 is trapped inside an infinite while loop.
The while loop keeps the process P1 busy until the lock value becomes 0 and its condition
breaks.
 
Scene-03:
 
Process P0 comes out of the critical section and sets the lock value to 0.
The while loop condition breaks.
Now, process P1 waiting for the critical section enters the critical section.
Now, even if process P1 gets preempted in the middle, no other process can enter the critical
section.
Any other process can enter only after process P1 completes and sets the lock value to 0.
 
Characteristics-
 

9
The characteristics of this synchronization mechanism are-
It ensures mutual exclusion.
It is deadlock free.
It does not guarantee bounded waiting and may cause starvation.
It suffers from spin lock.
It is not architectural neutral since it requires the operating system to support test-and-set
instruction.
It is a busy waiting solution which keeps the CPU busy when the process is actually waiting.

Classical Problem of Synchronization


The Dining Philosophers Problem

In 1965, Dijkstra posed and solved a synchronization problem he called the dining philosophers
problem. Since that time, everyone inventing yet another synchronization primitive has felt obligated
to demonstrate how wonderful the new primitive is by showing how elegantly it solves the dining
philosophers problem. The problem can be stated quite simply as follows. Five philosophers are seated
around a circular table. Each philosopher has a plate of spaghetti. The spaghetti is so slippery that a
philosopher needs two forks to eat it. Between each pair of plates is one fork. The layout of the table is
illustrated in Fig. 2-31.

Figure 2-31. Lunch time in the Philosophy Department.

The life of a philosopher consists of alternate periods of eating and thinking. (This is something of an
abstraction, even for philosophers, but the other activities are irrelevant here.) When a philosopher gets
hungry, she tries to acquire her left and right fork, one at a time, in either order. If successful in
acquiring two forks, she eats for a while, then puts down the forks, and continues to think. The key
question is: Can you write a program for each philosopher that does what it is supposed to do and
never gets stuck? (It has been pointed out that the two-fork requirement is somewhat artificial; perhaps
we should switch from Italian food to Chinese food, substituting rice for spaghetti and chopsticks for
forks.)

Figure 2-32 shows the obvious solution. The procedure take_fork waits until the specified fork is
available and then seizes it. Unfortunately, the obvious solution is wrong. Suppose that all five

10
philosophers take their left forks simultaneously. None will be able to take their right forks, and there
will be a deadlock.

#define N 5                /* number of philosophers */


 
void philosopher(int i)    /* i: philosopher number, from 0 to 4 */
{
    while (TRUE) {
        think( );           /* philosopher is thinking */
        take_fork(i);       /* take left fork */
        take_fork((i+1) % N);  /* take right fork; % is modulo operator */
        eat();              /* yum-yum, spaghetti */
        put_fork(i);        /* Put left fork back on the table */
        put_fork((i+1) % N);   /* put right fork back on the table */
    }
}

Figure 2-32. A nonsolution to the dining philosophers problem.

We could modify the program so that after taking the left fork, the program checks to see if the right
fork is available. If it is not, the philosopher puts down the left one, waits for some time, and then
repeats the whole process. This proposal too, fails, although for a different reason. With a little bit of
bad luck, all the philosophers could start the algorithm simultaneously, picking up their left forks,
seeing that their right forks were not available, putting down their left forks, waiting, picking up their
left forks again simultaneously, and so on, forever. A situation like this, in which all the programs
continue to run indefinitely but fail to make any progress is called starvation. (It is called starvation
even when the problem does not occur in an Italian or a Chinese restaurant.)

Now you might think, “if the philosophers would just wait a random time instead of the same time
after failing to acquire the right-hand fork, the chance that everything would continue in lockstep for
even an hour is very small.” This observation is true, and in nearly all applications trying again later is
not a problem. For example, in the popular Ethernet local area network, if two computers send a
packet at the same time, each one waits a random time and tries again; in practice this solution works
fine. However, in a few applications one would prefer a solution that always works and cannot fail due
to an unlikely series of random numbers. Think about safety control in a nuclear power plant.

One improvement to Fig. 2-32 that has no deadlock and no starvation is to protect the five statements
following the call to think by a binary semaphore. Before starting to acquire forks, a philosopher
would do a down on mutex. After replacing the forks, she would do an up on mutex. From a theoretical
viewpoint, this solution is adequate. From a practical one, it has a performance bug: only one
philosopher can be eating at any instant. With five forks available, we should be able to allow two
philosophers to eat at the same time.

The solution presented in Fig. 2-33 is deadlock-free and allows the maximum parallelism for an
arbitrary number of philosophers. It uses an array, state, to keep track of whether a philosopher is
eating, thinking, or hungry (trying to acquire forks). A philosopher may move only into eating state if
neither neighbor is eating. Philosopher i’s neighbors are defined by the macros LEFT and RICHT. In
other words, if i is 2, LEFT is 1 and RIGHT is 3.

11
The program uses an array of semaphores, one per philosopher, so hungry philosophers can block if
the needed forks are busy. Note that each process runs the procedure philosopher as its main code, but
the other procedures, take_forks, put_forks, and test are ordinary procedures and not separate
processes.

#define N              5   /* number of philosophers */


#define LEFT           (i+N−1)%N /* number of i's left neighbor */
#define RIGHT          (i+1)%N /* number of i's right neighbor */
#define THINKING       0   /* philosopher is thinking */
#define HUNGRY         1   /* philosopher is trying to get forks */
#define EATING         2   /* philosopher is eating */
typedef int semaphore;     /* semaphores are a special kind of int */
int state[N];              /* array to keep track of everyone's state */
semaphore mutex = 1;       /* mutual exclusion for critical regions */
semaphore s[N];            /* one semaphore per philosopher */
 
void philosopher (int i)   /* i: philosopher number, from 0 to N−1 */
{
while (TRUE) {         /* repeat forever */
think();           /* philosopher is thinking */
take_forks(i);     /* acquire two forks or block */
eat();             /* yum-yum, spaghetti */
put_forks(i);      /* put both forks back on table */
}
}
 
void take_forks(int i)     /* i: philosopher number, from 0 to N−1 */
{
down(&mutex);       /* enter critical region */
state[i] = HUNGRY;     /* record fact that philosopher i is hungry */
test(i);               /* try to acquire 2 forks */
up(&mutex);   /* exit critical region */
down(&s[i]);  /* block if forks were not acquired */
}
 
void put_forks(i)          /* i: philosopher number, from 0 to N−1 */
{
down(&mutex); /* enter critical region */
state[i] = THINKING;   /* philosopher has finished eating */
test(LEFT);            /* see if left neighbor can now eat */
test(RIGHT);           /* see if right neighbor can now eat */
up(&mutex);   /* exit critical region */
}
 
void test(i)               /* i: philosopher number, from 0 to N−1 */
{
  if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
state[i] = EATING;
up(&s[i]);
 }

12
}

Figure 2-33. A solution to the dining philosophers problem.

The Readers and Writers Problem

The dining philosophers problem is useful for modeling processes that are competing for exclusive
access to a limited number of resources, such as I/O devices. Another famous problem is the readers
and writers problem (Courtois et al., 1971), which models access to a database. Imagine, for example,
an airline reservation system, with many competing processes wishing to read and write it. It is
acceptable to have multiple processes reading the database at the same time, but if one process is
updating (writing) the database, no other processes may have access to the database, not even readers.
The question is how do you program the readers and the writers? One solution is shown in Fig. 2-34.

In this solution, the first reader to get access to the database does a down on the semaphore db.
Subsequent readers merely increment a counter, rc. As readers leave, they decrement the counter and
the last one out does an up on the semaphore, allowing a blocked writer, if there is one, to get in.

The solution presented here implicitly contains a subtle decision that is worth commenting on.
Suppose that while a reader is using the database, another reader comes along. Since having two
readers at the same time is not a problem, the second reader is admitted. A third and subsequent
readers can also be admitted if they come along.

Now suppose that a writer comes along. The writer cannot be admitted to the database, since writers
must have exclusive access, so the writer is suspended. Later, additional readers show up. As long as
at least one reader is still active, subsequent readers are admitted. As a consequence of this strategy, as
long as there is a steady supply of readers, they will all get in as soon as they arrive. The writer will be
kept suspended until no reader is present. If a new reader arrives, say, every 2 seconds, and each
reader takes 5 seconds to do its work, the writer will never get in.

To prevent this situation, the program could be written slightly differently: when a reader arrives and a
writer is waiting, the reader is suspended behind the writer instead of being admitted immediately. In
this way, a writer has to wait for readers that were active when it arrived to finish but does not have to
wait for readers that came along after it. The disadvantage of this solution is that it achieves less
concurrency and thus lower performance. Courtois et al. present a solution that gives priority to
writers. For details, we refer you to the paper.

typedef int semaphore;         /* use your imagination */


semaphore mutex = 1;           /* controls access to 'rc' */
semaphore db = 1;              /* controls access to the database */
int rc = 0;                    /* # of processes reading or wanting to */
 
void reader(void)
{
while (TRUE) {             /* repeat forever */
down(&mutex);       /* get exclusive access to 'rc' */
rc = rc + 1;           /* one reader more now */
if (re == 1) down(&db);   /* if this is the first reader… */
up{&mutex);            /* release exclusive access to 'rc' */
read_data_base();      /* access the data */

13
down(&mutex);          /* get exclusive access to 'rc' */
rc = rc − 1;     /* one reader fewer now */
if (rc == 0) up(&db);  /* if this is the last reader… */
up(&mutex);            /* release exclusive access to 'rc' */
use_data_read();       /* noncritical region */
}
}
 
void writer(void)
{
while (TRUE) {             /* repeat forever */
think_up_data();      /* noncritical region */
down(&db);             /* get exclusive access */
write_data_base();     /* update the data */
up(&db);          /* release exclusive access */
}
}

The Sleeping Barber Problem : Another classical IPC problem takes place in a barber shop. The
barber shop has one barber, one barber chair, and n chairs for waiting customers, if any, to sit on. If
there are no customers present, the barber sits down in the barber chair and falls asleep, as illustrated
in Fig. 2-35. When a customer arrives, he has to wake up the sleeping barber. If additional customers
arrive while the barber is cutting a customer’s hair, they either sit down (if there are empty chairs) or
leave the shop (if all chairs are full). The problem is to program the barber and the customers without
getting into race conditions. This problem is similar to various queueing situations, such as a multi
person helpdesk with a computerized call waiting system for holding a limited number of incoming
calls.

Figure 2-35. The sleeping barber.

Our solution uses three semaphores: customers, which counts waiting customers (excluding the
customer in the barber chair, who is not waiting), barbers, the number of barbers (0 or 1) who are idle,
waiting for customers, and mutex, which is used for mutual exclusion. We also need a variable,
waiting, which also counts the waiting customers. It is essentially a copy of customers. The reason for
having waiting is that there is no way to read the current value of a semaphore. In this solution, a
customer entering the shop has to count the number of waiting customers. If it is less than the number
of chairs, he stays; otherwise, he leaves.

14
Our solution is shown in Fig. 2-36. When the barber shows up for work in the morning, he executes
the procedure barber, causing him to block on the semaphore customers because it is initially 0. The
barber then goes to sleep, as shown in Fig. 2-35. He stays asleep until the first customer shows up.

#define CHAIRS 5            /* # chairs for waiting customers */


typedef int semaphore;  /* use your imagination */
semaphore customers = 0;   /* # of customers waiting for service */
semaphore barbers = 0;        /* # of barbers waiting for customers */
semaphore mutex = 1;   /* for mutual exclusion */
int waiting = 0;               /* customers are waiting (not being cut) */
 
void barber(void)
{
white (TRUE) {
down(&customers);      /* go to sleep if # of customers is 0 */
down(&mutex);          /* acquire access to 'waiting' */
waiting = waiting − 1; /* decrement count of waiting customers */
up(&barbers);          /* one barber is now ready to cut hair */
up(&mutex);            /* release 'waiting' */
cut_hair();            /* cut hair (outside critical region) */
}
}
 
void customer(void)
{
down(&mutex);              /* enter critical region */
if (waiting < CHAIRS) {  /* if there are no free chairs, leave */
waiting = waiting + 1; /* increment count of waiting customers */
up(&customers);        /* wake up barber if necessary */
up(&mutex);            /* release access to 'waiting' */
down(&barbers);        /* go to sleep if # of free barbers is 0 */
get_haircut();         /* be seated and be serviced */
} else {
up(&mutex);            /* shop is full; do not wait */
}
}

Figure 2-36. A solution to the sleeping barber problem.

When a customer arrives, he executes customer, starting by acquiring mutex to enter a critical region.
If another customer enters shortly thereafter, the second one will not be able to do anything until the
first one has released mutex. The customer then checks to see if the number of waiting customers is
less than the number of chairs. If not, he releases mutex and leaves without a haircut.

If there is an available chair, the customer increments the integer variable, waiting. Then he does an up
on the semaphore customers, thus waking up the barber. At this point, the customer and barber are
both awake. When the customer releases mutex, the barber grabs it, does some housekeeping, and
begins the haircut.

15
When the haircut is over, the customer exits the procedure and leaves the shop. Unlike our earlier
examples, there is no loop for the customer because each one gets only one haircut. The barber loops,
however, to try to get the next customer. If one is present, another haircut is given. If not, the barber
goes to sleep.

As an aside, it is worth pointing out that although the readers and writers and sleeping barber problems
do not involve data transfer, they are still belong to the area of IPC because they involve
synchronization between multiple processes.

Deadlock
A deadlock happens in operating system when two or more processes need some resource to complete
their execution that is held by the other process.

In the above diagram, the process 1 has resource 1 and needs to acquire resource 2. Similarly process 2
has resource 2 and needs to acquire resource 1. Process 1 and process 2 are in deadlock as each of
them needs the other’s resource to complete their execution but neither of them is willing to relinquish
their resources.
Coffman Conditions
A deadlock occurs if the four Coffman conditions hold true. But these conditions are not mutually
exclusive.
The Coffman conditions are given as follows −

 Mutual Exclusion
There should be a resource that can only be held by one process at a time. In the diagram
below, there is a single instance of Resource 1 and it is held by Process 1 only.

16
 Hold and Wait
A process can hold multiple resources and still request more resources from other processes
which are holding them. In the diagram given below, Process 2 holds Resource 2 and Resource
3 and is requesting the Resource 1 which is held by Process 1.

 No Preemption
A resource cannot be preempted from a process by force. A process can only release a resource
voluntarily. In the diagram below, Process 2 cannot preempt Resource 1 from Process 1. It will
only be released when Process 1 relinquishes it voluntarily after its execution is complete.

 Circular Wait
A process is waiting for the resource held by the second process, which is waiting for the
resource held by the third process and so on, till the last process is waiting for a resource held
by the first process. This forms a circular chain. For example: Process 1 is allocated Resource2
and it is requesting Resource 1. Similarly, Process 2 is allocated Resource 1 and it is requesting
Resource 2. This forms a circular wait loop.

17
Deadlock Detection
A deadlock can be detected by a resource scheduler as it keeps track of all the resources that are
allocated to different processes. After a deadlock is detected, it can be resolved using the following
methods −

 All the processes that are involved in the deadlock are terminated. This is not a good approach
as all the progress made by the processes is destroyed.
 Resources can be preempted from some processes and given to others till the deadlock is
resolved.
Deadlock Prevention
It is very important to prevent a deadlock before it can occur. So, the system checks each transaction
before it is executed to make sure it does not lead to deadlock. If there is even a slight chance that a
transaction may lead to deadlock in the future, it is never allowed to execute.
Deadlock Avoidance
It is better to avoid a deadlock rather than take measures after the deadlock has occurred. The wait for
graph can be used for deadlock avoidance. This is however only useful for smaller databases as it can
get quite complex in larger databases.

S.N User-Level Threads Kernel-Level Thread


.

1 User-level threads are faster to create and manage. Kernel-level threads are slower to create
and manage.

18
2 Implementation is by a thread library at the user level. Operating system supports creation of
Kernel threads.

3 User-level thread is generic and can run on any Kernel-level thread is specific to the
operating system. operating system.

4 Multi-threaded applications cannot take advantage of Kernel routines themselves can be


multiprocessing. multithreaded.

Recovery from Deadlock

When a Deadlock Detection Algorithm determines that a deadlock has occurred in the system, the
system must recover from that deadlock. There are two approaches of breaking a Deadlock:

1. Process Termination:
To eliminate the deadlock, we can simply kill one or more processes. For this, we use two methods:
 (a). Abort all the Deadlocked Processes:
Aborting all the processes will certainly break the deadlock, but with a great expenses.
The deadlocked processes may have computed for a long time and the result of those
partial computations must be discarded and there is a probability to recalculate them later.

 (b). Abort one process at a time untill deadlock is eliminated:


Abort one deadlocked process at a time, untill deadlock cycle is eliminated from the
system. Due to this method, there may be considerable overhead, because after aborting
each process, we have to run deadlock detection algorithm to check whether any
processes are still deadlocked.

2. Resource Preemption:
To eliminate deadlocks using resource preemption, we preepmt some resources from processes and
give those resources to other processes. This method will raise three issues –
 (a). Selecting a victim:
We must determine which resources and which processes are to be preempted and also
the order to minimize the cost.

 (b). Rollback:
We must determine what should be done with the process from which resources are
preempted. One simple idea is total rollback. That means abort the process and restart it.

 (c) Starvation:
In a system, it may happen that same process is always picked as a victim. As a result,
that process will never complete its designated task. This situation is
called Starvation and must be avoided. One solution is that a process must be picked as a
victim only a finite number of times.

Memory Management

19
Swapping

Swapping is a memory management scheme in which any process can be temporarily swapped from
main memory to secondary memory so that the main memory can be made available for other
processes. It is used to improve main memory utilization. In secondary memory, the place where the
swapped-out process is stored is called swap space.

The purpose of the swapping in operating system is to access the data present in the hard disk and
bring it to RAM so that the application programs can use it. The thing to remember is that swapping is
used only when data is not present in RAM.

Although the process of swapping affects the performance of the system, it helps to run larger and
more than one process. This is the reason why swapping is also referred to as memory compaction.

The concept of swapping has divided into two more concepts: Swap-in and Swap-out.

o Swap-out is a method of removing a process from RAM and adding it to the hard disk.
o Swap-in is a method of removing a program from a hard disk and putting it back into the main
memory or RAM.

Example: Suppose the user process's size is 2048KB and is a standard hard disk where swapping has
a data transfer rate of 1Mbps. Now we will calculate how long it will take to transfer from main
memory to secondary memory.

1. User process size is 2048Kb  
2. Data transfer rate is 1Mbps = 1024 kbps  
3. Time = process size / transfer rate  
4.      = 2048 / 1024  
5.      = 2 seconds  
6.      = 2000 milliseconds  
7. Now taking swap-in and swap-out time, the process will take 4000 milliseconds.   
Advantages of Swapping
1. It helps the CPU to manage multiple processes within a single main memory.
2. It helps to create and use virtual memory.
3. Swapping allows the CPU to perform multiple tasks simultaneously. Therefore, processes do
not have to wait very long before they are executed.
4. It improves the main memory utilization.

Disadvantages of Swapping
1. If the computer system loses power, the user may lose all information related to the program in
case of substantial swapping activity.

20
2. If the swapping algorithm is not good, the composite method can increase the number of Page
Fault and decrease the overall processing performance.

o In a single tasking operating system, only one process occupies the user program area of
memory and stays in memory until the process is complete.
o In a multitasking operating system, a situation arises when all the active processes cannot
coordinate in the main memory, then a process is swap out from the main memory so that other
processes can enter it.

In operating systems, Memory Management is the function responsible for allocating and managing
computer’s main memory. Memory Management function keeps track of the status of each memory
location, either allocated or free to ensure effective and efficient use of Primary Memory.
There are two Memory Management Techniques: Contiguous, and Non-Contiguous. In Contiguous
Technique, executing process must be loaded entirely in main-memory. Contiguous Technique can
be divided into:
1. Fixed (or static) partitioning
2. Variable (or dynamic) partitioning

Fixed Partitioning:
This is the oldest and simplest technique used to put more than one processes in the main memory.
In this partitioning, number of partitions (non-overlapping) in RAM are fixed but size of each
partition may or may not be same. As it is contiguous allocation, hence no spanning is allowed.
Here partition are made before execution or during system configure.

21
As illustrated in above figure, first process is only consuming 1MB out of 4MB in the main memory.
Hence, Internal Fragmentation in first block is (4-1) = 3MB.
Sum of Internal Fragmentation in every block = (4-1)+(8-7)+(8-7)+(16-14)= 3+1+1+2 = 7MB.

Suppose process P5 of size 7MB comes. But this process cannot be accommodated inspite of
available free space because of contiguous allocation (as spanning is not allowed). Hence, 7MB
becomes part of External Fragmentation.
There are some advantages and disadvantages of fixed partitioning.

Advantages of Fixed Partitioning –


1. Easy to implement:
Algorithms needed to implement Fixed Partitioning are easy to implement. It simply
requires putting a process into certain partition without focussing on the emergence of
Internal and External Fragmentation.
2. Little OS overhead:
Processing of Fixed Partitioning require lesser excess and indirect computational power.

Disadvantages of Fixed Partitioning –


1. Internal Fragmentation:
Main memory use is inefficient. Any program, no matter how small, occupies an entire
partition. This can cause internal fragmentation.
2. External Fragmentation:
The total unused space (as stated above) of various partitions cannot be used to load the
processes even though there is space available but not in the contiguous form (as
spanning is not allowed).
3. Limit process size:
Process of size greater than size of partition in Main Memory cannot be accommodated.
Partition size cannot be varied according to the size of incoming process’s size. Hence,
process size of 32MB in above stated example is invalid.
4. Limitation on Degree of Multiprogramming:
Partition in Main Memory are made before execution or during system configure. Main
Memory is divided into fixed number of partition. Suppose if there are   partitions in
RAM and   are the number of processes, then condition must be fulfilled. Number of
processes greater than number of partitions in RAM is invalid in Fixed Partitioning.

Variable Partitioning
In operating systems, Memory Management is the function responsible for allocating and managing
computer’s main memory. Memory Management function keeps track of the status of each memory
location, either allocated or free to ensure effective and efficient use of Primary Memory.
There are two Memory Management Techniques: Contiguous, and Non-Contiguous. In Contiguous
Technique, executing process must be loaded entirely in main-memory. Contiguous Technique can
be divided into:

1. Fixed (or static) partitioning


2. Variable (or dynamic) partitioning

22
Variable Partitioning –
It is a part of Contiguous allocation technique. It is used to alleviate the problem faced by Fixed
Partitioning. In contrast with fixed partitioning, partitions are not made before the execution or
during system configure. Various features associated with variable Partitioning-
1. Initially RAM is empty and partitions are made during the run-time according to
process’s need instead of partitioning during system configure.
2. The size of partition will be equal to incoming process.
3. The partition size varies according to the need of the process so that the internal
fragmentation can be avoided to ensure efficient utilisation of RAM.
4. Number of partitions in RAM is not fixed and depends on the number of incoming
process and Main Memory’s size.

There are some advantages and disadvantages of variable partitioning over fixed partitioning as
given below.
Advantages of Variable Partitioning –
1. No Internal Fragmentation:
In variable Partitioning, space in main memory is allocated strictly according to the need
of process, hence there is no case of internal fragmentation. There will be no unused
space left in the partition.
2. No restriction on Degree of Multiprogramming:
More number of processes can be accommodated due to absence of internal
fragmentation. A process can be loaded until the memory is empty.
3. No Limitation on the size of the process:
In Fixed partitioning, the process with the size greater than the size of the largest partition
could not be loaded and process can not be divided as it is invalid in contiguous
allocation technique. Here, In variable partitioning, the process size can’t be restricted
since the partition size is decided according to the process size.
Disadvantages of Variable Partitioning –
1. Difficult Implementation:
Implementing variable Partitioning is difficult as compared to Fixed Partitioning as it
involves allocation of memory during run-time rather than during system configure.
2. External Fragmentation:
There will be external fragmentation inspite of absence of internal fragmentation.
For example, suppose in above example- process P1(2MB) and process P3(1MB)
completed their execution. Hence two spaces are left i.e. 2MB and 1MB. Let’s suppose
process P5 of size 3MB comes. The empty space in memory cannot be allocated as no

23
spanning is allowed in contiguous allocation. The rule says that process must be
contiguously present in main memory to get executed. Hence it results in External
Fragmentation.

Now P5 of size 3 MB cannot be accommodated in spite of required available space


because in contiguous no spanning is allowed.
Paging is a memory management scheme that eliminates the need for contiguous allocation of
physical memory. This scheme permits the physical address space of a process to be non –
contiguous.
 Logical Address or Virtual Address (represented in bits): An address generated by the
CPU
 Logical Address Space or Virtual Address Space( represented in words or bytes): The set
of all logical addresses generated by a program
 Physical Address (represented in bits): An address actually available on memory unit
 Physical Address Space (represented in words or bytes): The set of all physical addresses
corresponding to the logical addresses
Example:
 If Logical Address = 31 bit, then Logical Address Space = 231 words = 2 G words (1 G =
230)
 If Logical Address Space = 128 M words = 27 * 220 words, then Logical Address =
log2 227 = 27 bits
 If Physical Address = 22 bit, then Physical Address Space = 222 words = 4 M words (1 M
= 220)
 If Physical Address Space = 16 M words = 24 * 220 words, then Physical Address =
log2 224 = 24 bits
The mapping from virtual to physical address is done by the memory management unit (MMU)
which is a hardware device and this mapping is known as paging technique.
 The Physical Address Space is conceptually divided into a number of fixed-size blocks,
called frames.
 The Logical address Space is also splitted into fixed-size blocks, called pages.
 Page Size = Frame Size
Let us consider an example:
 Physical Address = 12 bits, then Physical Address Space = 4 K words
 Logical Address = 13 bits, then Logical Address Space = 8 K words
 Page size = frame size = 1 K words (assumption)

24
 
Address generated by CPU is divided into
 Page number(p): Number of bits required to represent the pages in Logical Address
Space or Page number
 Page offset(d): Number of bits required to represent particular word in a page or page
size of Logical Address Space or word number of a page or page offset.
Physical Address is divided into
 Frame number(f): Number of bits required to represent the frame of Physical Address
Space or Frame number.
 Frame offset(d): Number of bits required to represent particular word in a frame or
frame size of Physical Address Space or word number of a frame or frame offset.
 
The hardware implementation of page table can be done by using dedicated registers. But the usage
of register for the page table is satisfactory only if page table is small. If page table contain large
number of entries then we can use TLB(translation Look-aside buffer), a special, small, fast look up
hardware cache.
 The TLB is associative, high speed memory.
 Each entry in TLB consists of two parts: a tag and a value.
 When this memory is used, then an item is compared with all tags simultaneously.If the
item is found, then corresponding value is returned.

25
 
Main memory access time = m
If page table are kept in main memory,
Effective access time = m(for page table) + m(for particular page in page table)

Segmentation
A process is divided into Segments. The chunks that a program is divided into which are not
necessarily all of the same sizes are called segments. Segmentation gives user’s view of the process
which paging does not give. Here the user’s view is mapped to physical memory.
There are types of segmentation:
1. Virtual memory segmentation –
Each process is divided into a number of segments, not all of which are resident at any
one point in time.
2. Simple segmentation –
Each process is divided into a number of segments, all of which are loaded into memory
at run time, though not necessarily contiguously.
There is no simple relationship between logical addresses and physical addresses in segmentation. A
table stores the information about all such segments and is called Segment Table.

26
Segment Table – It maps two-dimensional Logical address into one-dimensional Physical address.
It’s each table entry has:
 Base Address: It contains the starting physical address where the segments reside in
memory.
 Limit: It specifies the length of the segment.

Translation of Two dimensional Logical Address to one dimensional Physical Address.

Address generated by the CPU is divided into:


 Segment number (s): Number of bits required to represent the segment.
 Segment offset (d): Number of bits required to represent the size of the segment.
Advantages of Segmentation –

27
 No Internal fragmentation.
 Segment Table consumes less space in comparison to Page table in paging.
Disadvantage of Segmentation –
 As processes are loaded and removed from the memory, the free memory space is broken
into little pieces, causing External fragmentation.
This article has been contributed by Vikash Kumar. Please write comments if you find anything
incorrect, or you want to share more information about the topic discussed above

Virtual Memory
Virtual Memory is a storage allocation scheme in which secondary memory can be addressed as
though it were part of main memory. The addresses a program may use to reference memory are
distinguished from the addresses the memory system uses to identify physical storage sites, and
program generated addresses are translated automatically to the corresponding machine addresses.
The size of virtual storage is limited by the addressing scheme of the computer system and amount
of secondary memory is available not by the actual number of the main storage locations.
It is a technique that is implemented using both hardware and software. It maps memory addresses
used by a program, called virtual addresses, into physical addresses in computer memory.
1. All memory references within a process are logical addresses that are dynamically
translated into physical addresses at run time. This means that a process can be swapped
in and out of main memory such that it occupies different places in main memory at
different times during the course of execution.
2. A process may be broken into number of pieces and these pieces need not be
continuously located in the main memory during execution. The combination of dynamic
run-time address translation and use of page or segment table permits this.
If these characteristics are present then, it is not necessary that all the pages or segments are present
in the main memory during execution. This means that the required pages need to be loaded into
memory whenever required. Virtual memory is implemented using Demand Paging or Demand
Segmentation.
Demand Paging
A demand paging system is quite similar to a paging system with swapping where processes reside in
secondary memory and pages are loaded only on demand, not in advance. When a context switch
occurs, the operating system does not copy any of the old program’s pages out to the disk or any of
the new program’s pages into the main memory Instead, it just begins executing the new program
after loading the first page and fetches that program’s pages as they are referenced.

28
While executing a program, if the program references a page which is not available in the main
memory because it was swapped out a little ago, the processor treats this invalid memory reference as
a page fault and transfers control from the program to the operating system to demand the page back
into the memory.
Advantages
Following are the advantages of Demand Paging −

 Large virtual memory.


 More efficient use of memory.
 There is no limit on degree of multiprogramming.
Disadvantages
 Number of tables and the amount of processor overhead for handling page interrupts are
greater than in the case of the simple paged management techniques.
Page Replacement Algorithm
Page replacement algorithms are the techniques using which an Operating System decides which
memory pages to swap out, write to disk when a page of memory needs to be allocated. Paging
happens whenever a page fault occurs and a free page cannot be used for allocation purpose
accounting to reason that pages are not available or the number of free pages is lower than required
pages.
When the page that was selected for replacement and was paged out, is referenced again, it has to
read in from disk, and this requires for I/O completion. This process determines the quality of the
page replacement algorithm: the lesser the time waiting for page-ins, the better is the algorithm.
A page replacement algorithm looks at the limited information about accessing the pages provided by
hardware, and tries to select which pages should be replaced to minimize the total number of page
misses, while balancing it with the costs of primary storage and processor time of the algorithm itself.
There are many different page replacement algorithms. We evaluate an algorithm by running it on a
particular string of memory reference and computing the number of page faults,
Reference String
The string of memory references is called reference string. Reference strings are generated artificially
or by tracing a given system and recording the address of each memory reference. The latter choice
produces a large number of data, where we note two things.
 For a given page size, we need to consider only the page number, not the entire address.
 If we have a reference to a page p, then any immediately following references to page p will
never cause a page fault. Page p will be in memory after the first reference; the immediately
following references will not fault.
 For example, consider the following sequence of addresses − 123,215,600,1234,76,96
 If page size is 100, then the reference string is 1,2,6,12,0,0
First In First Out (FIFO) algorithm
 Oldest page in main memory is the one which will be selected for replacement.
 Easy to implement, keep a list, replace pages from the tail and add new pages at the head.

29
Optimal Page algorithm
 An optimal page-replacement algorithm has the lowest page-fault rate of all algorithms. An
optimal page-replacement algorithm exists, and has been called OPT or MIN.
 Replace the page that will not be used for the longest period of time. Use the time when a page
is to be used.

Least Recently Used (LRU) algorithm


 Page which has not been used for the longest time in main memory is the one which will be
selected for replacement.
 Easy to implement, keep a list, replace pages by looking back into time.

30
Page Buffering algorithm

 To get a process start quickly, keep a pool of free frames.


 On page fault, select a page to be replaced.
 Write the new page in the frame of free pool, mark the page table and restart the process.
 Now write the dirty page out of disk and place the frame holding replaced page in free pool.
Least frequently Used(LFU) algorithm
 The page with the smallest count is the one which will be selected for replacement.
 This algorithm suffers from the situation in which a page is used heavily during the initial
phase of a process, but then is never used again.
Most frequently Used(MFU) algorithm
 This algorithm is based on the argument that the page with the smallest count was probably
just brought in and has yet to be used.
Thrashing
Thrashing is a condition or a situation when the system is spending a major portion of its time in
servicing the page faults, but the actual processing done is very negligible.

The basic concept involved is that if a process is allocated too few frames, then there will be too
many and too frequent page faults. As a result, no useful work would be done by the CPU and the

31
CPU utilisation would fall drastically. The long-term scheduler would then try to improve the CPU
utilisation by loading some more processes into the memory thereby increasing the degree of
multiprogramming. This would result in a further decrease in the CPU utilization triggering a
chained reaction of higher page faults followed by an increase in the degree of multiprogramming,
called Thrashing.
Locality Model –
A locality is a set of pages that are actively used together. The locality model states that as a process
executes, it moves from one locality to another. A program is generally composed of several
different localities which may overlap.

For example when a function is called, it defines a new locality where memory references are made
to the instructions of the function call, it’s local and global variables, etc. Similarly, when the
function is exited, the process leaves this locality.
Techniques to handle:
1. Working Set Model –
This model is based on the above-stated concept of the Locality Model.
The basic principle states that if we allocate enough frames to a process to accommodate
its current locality, it will only fault whenever it moves to some new locality. But if the
allocated frames are lesser than the size of the current locality, the process is bound to
thrash.
According to this model, based on a parameter A, the working set is defined as the set of
pages in the most recent ‘A’ page references. Hence, all the actively used pages would
always end up being a part of the working set.
The accuracy of the working set is dependant on the value of parameter A. If A is too
large, then working sets may overlap. On the other hand, for smaller values of A, the
locality might not be covered entirely.

If D is the total demand for frames and   is the working set size for a process i,

Now, if ‘m’ is the number of frames available in the memory, there are 2 possibilities:
 (i) D>m i.e. total demand exceeds the number of frames, then thrashing will
occur as some processes would not get enough frames.
 (ii) D<=m, then there would be no thrashing.
If there are enough extra frames, then some more processes can be loaded in the memory.
On the other hand, if the summation of working set sizes exceeds the availability of
frames, then some of the processes have to be suspended(swapped out of memory).

This technique prevents thrashing along with ensuring the highest degree of
multiprogramming possible. Thus, it optimizes CPU utilisation.

32
2. Page Fault Frequency –
A more direct approach to handle thrashing is the one that uses Page-Fault Frequency
concept.

The problem associated with Thrashing is the high page fault rate and thus, the concept here is to
control the page fault rate.
If the page fault rate is too high, it indicates that the process has too few frames allocated to it. On
the contrary, a low page fault rate indicates that the process has too many frames.
Upper and lower limits can be established on the desired page fault rate as shown in the diagram.
If the page fault rate falls below the lower limit, frames can be removed from the process. Similarly,
if the page fault rate exceeds the upper limit, more number of frames can be allocated to the process.

In other words, the graphical state of the system should be kept limited to the rectangular region
formed in the given diagram.

Demand Segmentation
Operating system also uses demand segmentation, which is similar to demand paging. Operating
system to uses demand segmentation where there is insufficient hardware available to
implement ‘Demand Paging’.

The segment table has a valid bit to specify if the segment is already in physical memory or not. If a
segment is not in physical memory, then segment fault results, which traps to the operating system and
brings the needed segment into physical memory, much like a page fault.

Demand segmentation allows for pages that are often referenced with each other to be brought into
memory together, this decreases the number of page faults.

Another space server would be to keep some of a segment’s page tables on disk and swap them into
memory when needed.

33

You might also like