OS - Unit 2 Complete
OS - Unit 2 Complete
Unit 2
Concurrent Processes:
1. Process Concept, 9. Classical Problem in Concurrency-
a) Dining Philosopher Problem,
2. Critical Section Problem,
b) Sleeping Barber Problem;
2. Principle of Concurrency,
10. Inter Process Communication
3. Producer / Consumer Problem,
models and Schemes,
4. Mutual Exclusion,
11. Process generation.
5. Dekker’s solution,
6. Peterson’s solution,
7. Semaphores,
8. Test and Set operation;
Process Concept: Definition
• Processes are basically the programs that are dispatched from the
ready state and are scheduled in the CPU for execution.
• PCB(Process Control Block) holds the concept of process.
• A process can create other processes which are known as Child
Processes.
• The process takes more time to terminate and it is isolated means it
does not share the memory with any other process.
• The process can have the following states new, ready, running,
waiting, terminated, and suspended. Process is a program in
execution, It must progress in sequential fashion.
• A process is active entity while a program is passive entity stored on
disk (executable file),
• Program becomes process when executable file loaded into
memory
• Execution of program started via GUI mouse clicks, command line
entry of its name, etc
• One program can be several processes
Process Concept: States of a Process
• It describes two processes, the producer and the consumer, which share a common, fixed-size buffer
used as a queue.
• Producer produce an item and put it into buffer. If buffer is already full then producer will have to
wait for an empty block in buffer.
• Consumer consume an item from buffer. If buffer is already empty then consumer will have to wait
for an item in buffer.
• Implement Peterson’s Algorithm for the two processes using shared memory such that there is
mutual exclusion between them. The solution should have free from synchronization problems.
Peterson’s solution
Peterson’s Solution is a classical software based solution to the
critical section problem.
In Peterson’s solution, we have two shared variables:
• Boolean flag[i] :Initialized to FALSE, initially no one is
interested in entering the critical section
• int turn : The process whose turn is to enter the critical
section.
• Peterson’s Solution preserves all three conditions :
• Mutual Exclusion is assured as only one process can
access the critical section at any time.
• Progress is also assured, as a process outside the
critical section does not block other processes from
entering the critical section.
• Bounded Waiting is preserved as every process gets a
fair chance.
• Disadvantages of Peterson’s Solution
• It involves Busy waiting
• It is limited to 2 processes.
Peterson’s algorithm
// code for producer (j) // code for consumer i
// producer j is ready // consumer i is ready
// to produce an item // to consume an item
flag[j] = true; flag[i] = true;
// but consumer (i) can consume an item // but producer (j) can produce an item
turn = i; turn = j;
// if consumer is ready to consume an item // if producer is ready to produce an item
// and if its consumer's turn // and if its producer's turn
while (flag[i] == true && turn == i) while (flag[j] == true && turn == j)
{ {
// then producer will wait } // then consumer will wait }
// otherwise producer will produce // otherwise consumer will consume
// an item and put it into buffer (critical Section) // an item from buffer (critical Section)
// Now, producer is out of critical section // Now, consumer is out of critical section
flag[j] = false; flag[i] = false;
// end of code for producer // end of code for consumer
//--------------------------------------------------------
TestAndSet
• TestAndSet is a hardware solution to the The enter_CS() and leave_CS() functions to implement
critical section of a process are realized using test-and-set
synchronization problem. In TestAndSet, instruction as follows:
we have a shared lock variable which can void enter_CS(X)
take either of the two values, 0 as Unlock int TestAndSet(int &lock)
{
and 1 as Lock int initial = lock;
lock = 1;
• Before entering into the critical section, a return initial;
process inquires about the lock. If it is }
locked, it keeps on waiting until it becomes void enter CS(X)
{
free and if it is not locked, it takes the lock while test-and-set(X) ;
and executes the critical section. }
void leave CS(X)
• In TestAndSet, Mutual exclusion and {
progress are preserved but bounded X = 0;
}
waiting cannot be preserved. In the above solution, X is a memory location associated
with the CS and is initialized to 0.
Dekker’s algorithm
• Dekker’s algorithm allows two threads to share a single-use resource without conflict,
using only shared memory for communication. It avoids the strict alternation of a naïve
turn-taking algorithm, and was one of the first mutual exclusion algorithms to be invented.
• Although there are many versions of Dekker’s Solution, the final or 5th version is the one
that satisfies all of the conditions and is the most efficient of them all.
Main() Thread2()
// if 2nd thread is more favoured
{
{ if (favaouredthread == 2) do / wait until thread1 wants to enter
// to denote which thread will enter next { // its critical section
// gives access to other thread while (thread1wantstoenter == true)
int favouredthread = 1; thread1wantstoenter = false; {
// flags to indicate if each thread is in // wait until this thread is favoured // if 1st thread is more favored
{ thread2wantstoenter = true; if (favaouredthread == 1)
// queue to enter its critical section {
// entry section
boolean thread1wantstoenter = false; // gives access to other thread
/red
thread2wantstoenter = false;
boolean thread2wantstoenter = false; while (favouredthread == 2) ; // wait until this thread is favoured
thread1wantstoenter = true; while (favouredthread == 1) ;
startThreads();
} thread2wantstoenter = true;
} } }
Thread1() // critical section
// favor the 2nd thread // critical section
{ // favour the 1st thread
favouredthread = 2;
do { favouredthread = 1;
// exit section
// exit section
thread1wantstoenter = true; // indicate thread1 has completed // indicate thread2 has completed
// its critical section // its critical section
// entry section thread1wantstoenter = false; thread2wantstoenter = false;
// wait until thread2 wants to enter // remainder section // remainder section
} while (completed == false) } while (completed == false)
// its critical section
} }
while (thread2wantstoenter == true) {
Semaphores and its types
• Semaphores are compound data types with two fields one is a Non-negative
integer S.V and the second is Set of processes in a queue S.L.
• Semaphore was proposed by Dijkstra in 1965 which is a very significant
technique to manage concurrent processes by using a simple integer value,
which is known as a semaphore. Semaphore is simply an integer variable that
is shared between threads.
• It is used to solve critical section problems, and by using two atomic
operations, it will be solved. In this, wait and signal that is used for process
synchronization.
• Semaphores are of two types:
• Binary Semaphore – This is also known as mutex lock. It can have only two
values – 0 and 1. Its value is initialized to 1. It is used to implement the
solution of critical section problems with multiple processes.
• Counting Semaphore – Its value can range over an unrestricted domain. It is
used to control access to a resource that has multiple instances.
Semaphore Implementation
Some point regarding P and V operation :
• P operation is also called wait, sleep, or
down operation, and V operation is also
called signal, wake-up, or up operation.
• Both operations are atomic and
semaphore(s) is always initialized to one.
Here atomic means that variable on which
read, modify and update happens at the
same time/moment with no pre-emption
i.e. in-between read, modify and update no
other operation is performed that may
change the variable.
• A critical section is surrounded by both
operations to implement process
synchronization. See the below image. The
critical section of Process P is in between P
and V operation.
Implementation of binary semaphores
struct semaphore { sleep();
enum value(0, 1); }
// q contains all Process Control Blocks (PCBs) }
// corresponding to processes got blocked V(Semaphore s)
// while performing down operation. {
Queue<process> q; if (s.q is empty)
} {
P(semaphore s) s.value = 1;
{ }
if (s.value == 1) else
{ {
s.value = 0; // select a process from waiting queue
} Process p=q.pop();
else wakeup(p);
{ }
// add the process to the waiting queue }
q.push(P)
Implementation of binary semaphores
• Now, let us see how it implements mutual
exclusion. Let there be two processes P1 and
P2 and a semaphore s is initialized as 1. Now if
suppose P1 enters in its critical section then
the value of semaphore s becomes 0. Now if
P2 wants to enter its critical section then it will
wait until s > 0, this can only happen when P1
finishes its critical section and calls V
operation on semaphores.
• This way mutual exclusion is achieved. Look at
the image for details which is Binary
semaphore.
• Limitations of Semaphores :
• One of the biggest limitations of semaphore is
priority inversion.
• Deadlock, suppose a process is trying to wake up
another process which is not in a sleep state.
Therefore, a deadlock may block indefinitely.
• The operating system has to keep track of all calls
to wait and to signal the semaphore.
Implementation of counting semaphore
struct Semaphore { return;
int value; }
// q contains all Process Control Blocks(PCBs) V(Semaphore s)
// corresponding to processes got blocked {
// while performing down operation. s.value = s.value + 1;
Queue<process> q; if (s.value >= 0) {
} P(Semaphore s) // remove process p from queue
{ Process p=q.pop();
s.value = s.value - 1; wakeup(p);
if (s.value < 0) { }
// add process to queue else
// here p is a process which is currently executing return;
q.push(p); }
block();
}
else
Producer Consumer Problem using Semaphores
• Problem Statement – We have a buffer of fixed size. A producer can
produce an item and can place in the buffer. A consumer can pick items
and can consume them. We need to ensure that when a producer is
placing an item in the buffer, then at the same time consumer should not
Producer Consumer
consume any item. In this problem, buffer is the critical section.
do{ do{
• To solve this problem, we need two counting semaphores – Full and
Empty. “Full” keeps track of number of items in the buffer at any given
time and “Empty” keeps track of number of unoccupied slots. //produce an item wait(full);
• When producer produces an item then the value of “empty” is reduced wait(mutex);
by 1 because one slot will be filled now. The value of mutex is also wait(empty);
reduced to prevent consumer to access the buffer. Now, the producer has // remove item from buffer
placed the item and thus the value of “full” is increased by 1. The value wait(mutex);
of mutex is also increased by 1 because the task of producer has been
completed and consumer can access the buffer. //place in buffer signal(mutex);
• As the consumer is removing an item from buffer, therefore the value of signal(empty);
“full” is reduced by 1 and the value is mutex is also reduced so that the signal(mutex);
producer cannot access the buffer at this moment. Now, the consumer // consumes item
has consumed the item, thus increasing the value of “empty” by 1. The signal(full);
value of mutex is also increased so that producer can access the buffer
now. }while(true) }while(true)
• Initialization of semaphores –
• mutex = 1
• Full = 0 // Initially, all slots are empty. Thus full slots are 0
• Empty = n // All slots are empty initially
Inter Process Communication
• Cooperating processes need inter-process
communication (IPC)
• Two models of IPC
• Shared memory- example Producer-Consumer
problem
• Message passing