Unit - Ii Process Management, Synchronization and Threads
Unit - Ii Process Management, Synchronization and Threads
PROCESS STATE
The state of a process is defined in part by the current activity of that process.
Each process may be in one of the following states:
• Waiting:
A suspended process lacks some resource other than the CPU. Such processes are normally not
considered for execution until the related suspending conditions is fulfilled. The running process become
suspended by invoking I/O routines whose result it needs in order to proceed.
• Terminated:
When the process finally stops. A process terminates when it finishes executing its last statement. At that
point in time, the process may return some data to its parent process. Sometimes there are additional
circumstances when termination occurs. A process can cause the termination of another process via an
appropriate system call.
• Identifier or process number :A unique identifier associated with each process, to distinguish it
from all other processes.
• Process state: The state may be new, ready, running, waiting or terminated.
• Priority:Priority level relative to other process.
• Program counter: The counter indicates the address of the next instruction to be executed for
this process.
• Context data: These are data that are present in registers in the process while the process is
executing.
• CPU registers: They include accumulator, general purpose registers, index registers etc.
Whenever a processor switches over from one process to another process, information about
current status of the old process is saved in the register along with the program counter so that
the process be allowed to continue correctly afterwards.
• CPU-scheduling information: This information includes a process priority, pointers to
scheduling queues, and any other scheduling parameters.
• Accounting information: This information includes the amount of CPU and real time used, time
limits, account numbers, job or process numbers and so on.
• I/O status information: The information includes the list of I/O devices allocated to this
process, a list of open files and so on.
PROCESSOR SCHEDULING
Scheduling is a fundamental operating system function. All computer resources are scheduled
before use. Since CPU is one of the primary computer resources, its scheduling is central to operating
system design. Scheduling refers to a set of policies and mechanisms supported by operating system that
controls the order in which the work to be done is completed. A scheduler is an operating system
program (module) that selects the next job to be admitted for execution. The main objective of
scheduling is to increase CPU utilization and higher throughput. Throughput is the amount of work
accomplished in a given time interval. CPU scheduling is the basis of operating system which supports
multiprogramming concepts. This mechanism improves the overall efficiency of the computer system by
getting more work done in less time.
Scheduling Criteria:
Different CPU - scheduling algorithms have different properties, and the choice of choosing which
algorithm to use in a particular situation is done by comparing these algorithms based on some criteria.
The criteria include the following:
• CPU utilization: We want to keep the CPU as busy as possible in all the time.
• Throughput: The number of processes that are completed per time unit, called throughput. For
long processes, this rate may be one process per hour; for short transactions, it may be ten
processes per second.
• Turnaround time: How much time it takes to execute a process. The interval from the time of
submission of a process to the time of completion is the turnaround time. Turnaround time is the
sum of the periods spent waiting to get into memory, waiting in the ready queue, executing on the
CPU , and doing I/O .
• Waiting time: The time that a process spends on waiting in the ready queue is called wating
time. Waiting time is the sum of the periods spent waiting in the ready queue.
• Response time: The time from the submission of a request until the first response is produced
this measure, called response time, is the time it takes to start responding, not the time it takes to
output the response.
It is desirable to maximize CPU utilization and throughput and to minimize turnaround time, waiting
time, and response time.
Scheduling Queues:
As processes enter the system, they are put into a job queue that consists of all processes in the system.
The processes that are residing in main memory and are ready and waiting to execute are kept on a list
called the ready queue. This queue is generally stored as a linked list. A ready-queue header contains
pointers to the first and final PCB s in the list. Each PCB includes a pointer field
that points to the next PCB in the ready queue.
The system also includes other queues. When a process is allocated the CPU , it executes for a while and
eventually quits, is interrupted, or waits for the occurrence of a particular event, such as the completion
of an I/O request. Suppose the process makes an I/O request to a shared device, such as a disk. Since
there are many processes in the system, the disk may be busy with the I/O request of some other process.
The process therefore may have to wait for the disk. The list of processes waiting for a particular I/O
device is called a device queue. Each device has its own device queue.
A common representation of process scheduling is a queueing diagram, such as that in Figure 3.7. Each
rectangular box represents a queue. Two types of queues are present: the ready queue and a set of device
queues. The circles represent the resources that serve the queues, and the arrows indicate the flow of
processes in the system. A new process is initially put in the ready queue. It waits there until it is selected
for execution, or is dispatched. Once the process is allocated the CPU and is executing, one of several
events could occur:
• The process could issue an I/O request and then be placed in an I/O queue.
• The process could create a new subprocess and wait for the subprocess’s termination.
• The process could be removed forcibly from the interrupt, and be put back in the ready queue.
CPU , as a result of an
In the first two cases, the process eventually switches from the waiting state to the ready state and is then
put back in the ready queue. A process continues this cycle until it terminates, at which time it is
removed from all queues and has its PCB and resources deallocated.
Types of Schedulers
This is also called job scheduler. This determines which job shall be admitted for
immediate processing. There are always more processes than it can be executed
by the CPU. These processes are kept in large storage devices like disk for later
processing. The long term scheduler selects processes from this pool and loads
them into memory. In memory these processes belong to a ready queue. The
short term scheduler (also called the CPU scheduler) selects from among the
processes in memory which are ready to execute and assigns the CPU to one of
them. The long term scheduler executes less frequently. The long-term scheduler
controls the degree of multiprogramming – the number of processes in memory. If
the degree of multiprogramming is stable, then the average rate of process
creation must be equal to the average departure rate of processes leaving the
system. Thus, the long-term scheduler may need to be invoked only when a
process leaves the system. Because of longer time taken by CPU during execution,
the long term scheduler can afford to take more time to decide which process
should be selected for execution.
It may also be very important that the long-term scheduler should take a careful
selection of processes, i.e. processes should be a combination of CPU and I/O
bound types. Generally, most processes can be put into any of the two categories:
CPU bound or I/O bound. If all processes are I/O bound, the ready queue will
always be empty and the short-term scheduler will have nothing to do. If all
processes are CPU bound, no process will be waiting for I/O operation and again
the system will be unbalanced. Therefore, the long-term scheduler provides good
performance by selecting a combination of CPU bound and I/O bound processes.
Medium-term schedulers:
Most of the processes require some I/O operation. In that case, it may become
suspended for I/O operation after running a while. It is beneficial to remove these
process (suspended) from main memory to hard disk to make room for other
processes. At some later time these process can be reloaded into memory and
continued where from it was left earlier. Saving of the suspended process is said
to be swapped out or rolled out. The process is swapped in and swapped out by
the medium term scheduler.
The medium term scheduler has nothing to do with the suspended processes. But
the moment the suspending condition is fulfilled, the medium term scheduler get
activated to allocate the memory and swapped in the process and make it ready
for execution. The medium-term scheduler is also helpful for reducing the degree
of multiprogramming, when the long term-term scheduler is absent or minimal.
Short-term scheduler:
This is also called CPU scheduler. When ever the CPU becomes idle, the operating
system must select one of the processes in the ready queue to be executed. The
selection process is carried out by the short-term scheduler. It allocates processes
belonging to ready queue to CPU for immediate processing. It’s main objective is
to maximize CPU utilization. Compared to the other two schedulers this is more
frequent. It must select a new process for execution quite often because a CPU
executes a process only for few milliseconds before it goes for I/O operation. Often
the short term scheduler executes at least once in every 10 milliseconds. If it
takes 1 millisecond to decide to execute a process for 10 milliseconds, the
1/(10+1) = 9% of the CPU is being wasted simply for scheduling the work.
Therefore. it must be very fast.
Context Switch (or) Process Switch:
When a CPU is executing a process, if that process get interrupted or I/O wait then the CPU is going
switches from the current process to another process which is waiting for burst time. In this context
switch mechanism system stores and restoring the state (context) of a process or thread so that execution
can be resumed from the same point at a later time. This enables multiple processes or multiple
programs to share a single CPU and is an essential feature of a multitasking operating system.
When the scheduler switches the CPU from executing one process to execute another, the context
switcher saves the content of all processor registers for the process being removed from the CPU, in its
process descriptor. The context of a process is represented in the process control block of a process.
Content switching times are highly dependent on hardware support. When the process is switched, the
following information is stored in the respective process control block.
Program Counter
Scheduling Information
Base and limit register value
Currently used register
Changed State
I/O State
Accounting
Scheduling Algorithms
CPU scheduling deals with the problem of deciding which of the processes in the
ready queue is to be allocated the CPU. There are several scheduling algorithms
exists. A major division among scheduling algorithms is that whether they support
pre-emptive or non-preemptive scheduling discipline. A scheduling discipline
is non-preemptive if once a process has been given the CPU, the CPU cannot be
taken away from that process. A scheduling discipline is pre-emptive if the CPU
can be taken away. Preemptive scheduling is more useful in high priority process
which requires immediate response. In non-preemptive systems, jobs are made to
wait by longer jobs, but the treatment of all processes is fairer. The decision
whether to schedule preemptive or not depends on the environment and the type
of application most likely to be supported by a given operating system.
First-Come-First-Served Scheduling:
This is one of the simplest scheduling algorithm. With this scheme, the process
that requests the CPU first is allocated the CPU first. The implementation of the
FCFS policy is easily managed with a FIFO queue. When a process enters the
ready queue, its PCB is linked onto the tail of the queue. When the CPU is free, it
is allocated to the process at the head of the queue. The running process is then
removed from the queue.
P1 P2
0 20 24
If both processes P1 and P2 arrive in order P1 -P2 in quick succession, the
turnaround times are 20 and 24 units of time respectively (P2 must wait for P1 to
complete) thus giving an average of 22 (20+24)/2) units of time. The
corresponding waiting times are 0 and 20 units of time with average of 1 0 time
units.
P2 P1
0 4 24
However, when the same processes arrive in P2-P 1 order, the turn around times
are 4 and 24 units of time respectively giving an average of 14 (4+24)/2) units of
time and the average waiting time is(0+4)/2)=2. This is a substantial reduction.
This shows how short jobs may be delayed in FCFS scheduling algorithm.
The FCFS scheduling algorithm is non preemptive. Once the CPU has been
allocated to a process, that process keeps the CPU until it releases the CPU, either
by terminating or by requesting I/O. The FCFS algorithm is particularly
troublesome for time-sharing, where each user needs to get a share of the CPU at
regular intervals.
Shortest-Job-First Scheduling:
0 3 8 16 26
The SJF scheduling algorithm is provably optimal, in that it gives the minimum
average waiting time for a given set of processes. By moving a short process
before a long one, the waiting time of the short process decreases more than it
increases the waiting time of the long process. Consequently, the average waiting
time decreases.
P1 0 8
P2 1 4
P3 2 9
P4 3 5
0 1 5 10 17 26
Process P1 is started at time 0, since it is the only process in the queue. Process
P2 arrives at time 1. The remaining time for process P1 is larger than the time
required by P2, so P1 is preempted, and P2 is scheduled. The average waiting time
for this example is
Priority Scheduling:
A priority is associated with each process and the scheduler always picks up the
highest priority process for execution from the ready queue. Equal priority
processes are scheduled in FCFS order. An SJF algorithm is simply a priority
algorithm where the priority is the inverse of the next CPU burst. The larger the
CPU burst, the lower the priority, and vice versa. The level of priority may be
determined on the basis of resource requirements, processes characteristics and
its run time behavior.
This is one of the oldest, simplest and widely used algorithm. The round-robin
scheduling algorithm is primarily used in a time-sharing and a multi-user system
where the primary requirement is to provide reasonably good response times and
in general to share the system fairly among all system users. It is similar to FCFS
scheduling, but preemption is added to switch between processes. A small unit of
time, called a time quantum (or time slice), is defined. A time quantum is
generally from 10 to 100 milliseconds. The ready queue is treated as a circular
queue. The CPU scheduler goes around the ready queue, allocating the CPU to
each process for a time interval of up to 1 time quantum.
Completed job
CPU
X Z Y X
Preemption
If we use a time-slice of 5 units of time, then P1 gets the first 5 units of time.
Since it requires another 20 units of time, it is pre-empted after the first time slice
and the CPU is given to the next process i.e. P2. Since P2 just needs 5 units of
time, it terminates as time-slice expires. The CPU is then given to the next process
P3. Once each process has received one time slice, the CPU is returned to P1 for
an additional time-slice. Thus the resulting round robin schedule is:
0 5 10 15 20 25 30 35
In MLQ, processes are classified into different groups. For example, interactive
processes (foreground) and batch processes (background) could be considered as
two types of processes because of their different response time requirements,
scheduling needs and priorities.
System Process
High priority
Scheduler
Interactive Process
Round Robin Switch
CPU
Scheduler
First Cum
Batch Process
First Serve
In addition, there must be scheduling among the queues. There are different
possibilities to manage queues. One possibility is to assign a time slice to each
queue, which it can schedule among different processes in its queue. Foreground
processes can be assigned 80% of CPU whereas background processes are given
20% of the CPU time.
The second possibility is to execute the high priority queue first. For example, no
process in the batch queue could run unless the queue for system processes and
interactive processes were all empty. If an interactive process entered the ready
queue while a batch process is running, the batch process would be pre-empted.
Interprocess Communication
• Convenience: Even an individual user may work on may tasks at the same
time. For instance, a user may be editing, printing, and compiling in parallel.
Cooperating processes require an interprocess communication (IPC) mechanism
that will allow them to exchange data and information. Inter Process
Communication is a capability supported by operating system that allows one
process to communicate with another process. The processes can be running on
the same computer or on different computers connected through a network. IPC
enables one application to control another application, and for several applications
to share the same data without interfering with one another.
• Critical Resource
• Critical Section
• Mutual Exclusion
• Shared Memory
• Message Passing
The operating system consists of a collection of such processes which are basically
two types:
• Operating system processes : Those that execute system code.
• User processes : Those that execute user's code.
To make this concept more clear, consider the batch operating system again. A
shared buffer is used for communication between the reader process and the
executor process. These processes must be synchronized so that, for example, the
executor process never attempts to read data from the input if the buffer is
empty.
Race Condition:
Processes that are working together often share some common storage that one
can read and write. The shared storage may be in main memory or it may be a
shared file. Processes frequently need to communicate with other processes. When
a user wants to read from a file, it must tell the file process what it wants, then
the file process has to inform the disk process to read the required block.
When a process wants to print a file, it enters the file name in a special spooler
directory. Another process, the printer process, periodically checks to see if
there are any files to be printed, and if there are it prints them and then removes
their names from the directory.
Imagine that the spooler directory has a large number of slots, numbered 0, 1, 2,
…, each one capable of holding a file name. Also imagine that there are two shared
variables, out, which points to the next file to be printed, and in, which points to
the next free slot in the directory and these are available to all processes. At a
certain instant, slot 0 to 3 are empty and slots 4 to 6 are full. More or less
simultaneously, process A and B decided to queue a file for printing.
Spooler directory
4 abc
out=4
5 prog.c
in = 7
Process A 6 prog.n
Process B
} while (TRUE);
o Bounded Waiting: There exists a bound on the number of times that other
processes are allowed to enter their critical sections after a process has
made a request to enter its critical section and before that request is
granted.
Mutual Exclusion:
Processes frequently need to communicate with other processes. When a user
wants to read from a file, it must tell the file process what it wants, then the file
process has to inform the disk process to read the required block.
Processes that are working together often share some common storage that one
can read and write. The shared storage may be in main memory or it may be a
shared file. Each process has segment of code, called a critical section, which
accesses shared memory or files. The key issue involving shared memory or
shared files is to find way to prohibit more than one process from reading and
writing the shared data at the same time. What we need is mutual exclusion -
some way of making sure that if one process is executing in its critical section, the
other processes will be excluded from doing the same thing.
Module Mutex
var
P1busy, P2busy : boolean;
Process P1
begin
while true do
begin
P1busy :=true;
While P2busy do {keep testing};
critical.-,section;
P1busy:=false;
Other_P1busy_Processing
end {while}
end; {P1}
Process P2
begin
while true do
begin
P2busy :=true;
While P1busy do {keep testing};
critical.-,section;
P2busy:=false;
Other_P2busy_Processing
end {while}
end; {P2}
{Parent process}
begin (mutex)
P1busy:=false;
P2busy:=false;
Initiate P1, P2
End (mutex)
P1 first sets P1busy and then tests P2busy to determine what to do next. When it
finds P2busy to be false, process P1 may safely proceed to the Critical section
knowing that no matter how the two processes may be interleaved, process P2 is
certain to find P2busy set and to stay away from the critical section. The single
change ensures mutual exclusion.
But consider a case where P1 wishes to enter the critical section and sets P1busy
to indicate the fact. If process P2 wishes to enter the critical section at the same
time and pre-empts process P1 just before P1 tests P2busy.
Process P2 may set P2busy and start looping while waiting for P1busy to become
false. When control is eventually returned to Process P1, it finds P2busy set and
starts looping while it waits for P2busy to become false. And so both processes are looping forever,
each awaiting the other one to clear the way. In order to remove this kind of behavior, we must add
another requirement to occur in our algorithm. When more than one process wishes to enter the
critical section, the decision to grant entrance to one of them must be made in finite time.
Peterson’s Solution:
Next, we illustrate a classic software-based solution to the critical-section problem known as
Peterson’s solution. Because of the way modern computer architectures perform basic machine-
language instructions, such as load and store , there are no guarantees that Peterson’s solution will
work correctly on such architectures. However, we present the solution because it 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.
Peterson’s solution is restricted to two processes that alternate execution between their critical
sections and remainder sections. The processes are numbered P 0 and P 1 . For convenience, when
presenting P i , we use P j to denote the other process; 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 indicates whose turn it is to enter its critical section. That is, if turn == i , then
process P i is allowed to execute in its critical section. The flag array is used to indicate if a process
is ready to enter its critical section. For example, if flag[i] is true , this value indicates that P i is
ready to enter its critical section.
To enter the critical section, process P i first sets flag[i] to be true and then sets turn to the value j ,
thereby asserting that if the other process wishes to enter the critical section, it can do so. If both
processes try to enter at the same time,then the eventual value of turn determines which of the two
processes is allowed to enter its critical section first.
PROCESS Pi Code:
while (true) {
flag[i] = TRUE ;
turn = j;
while (flag[j] && turn == j)
waiting to enter Critical section;
critical section
flag[i] = FALSE ;
remainder section
}
The structure of process P i in Peterson’s solution.
To prove property 1, we note that each P i 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 imply that P 0 and P 1 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, P j —must have successfully
executed the w hile statement, whereas P i had to execute at least one additional statement (“ turn
== j ”). However, at that time, flag[j] == true and turn == j , and this condition persists as long as P j
is in its critical section; as a result, mutual exclusion is preserved.
To prove properties 2 and 3, we note that a process P i can be prevented from entering the critical
section only if it is stuck in the w hile loop with the condition flag[j] == true and turn == j ; this
loop is the only one possible. If P j is not ready to enter the critical section, then flag[j] == false ,
and P i can enter its critical section. If P j has set flag[j] to true and is also executing in its while
statement, then either turn == i or turn == j . If turn == i , then P i will enter the critical section. If
turn == j , then P j will enter the critical section. However, once P j exits its critical section, it will
reset flag[j] to false , allowing P i to enter its critical section. If P j resets flag[j] to true , it must also
set turn to i . Thus, since P i does not change the value of the variable turn while executing the while
statement, P i will enter the critical section (progress) after at most one entry by P j (bounded
waiting).
Synchronization Hardware:
do {
waiting[i] = true;
key = ture;
while (waiting[i] && key)
key = TestAndSet(lock);
waiting[i] = flase;
critical section
j = (i+1) % n;
while ((j != i) && !waiting[j])
j = (j+1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;
remainder section
} while(1);
To prove that the mutual exclusion requirement is met, note that process Pi can
enter its critical section only if either waiting[i] == flase or key == flase. The
value of key can become false only if the TestAndSet is executed. The first process
to execute the TestAndSet will find key == flase; all others must wait. The
variable waiting[i] can become false only if another process leaves its critical
section; only one waiting[i] is set to false, maintaining the mutual exclusion
requirement.
To prove the progress requirement is met, note that a process exiting the critical
section either sets lock to false, or sets waiting[i] to false. Both allow a process
that is waiting to enter its critical section to proceed.
Semaphores:
a. Wait(s):
while S <= 0 do (keep testing);
S: = S-1;
wait operation decrements the value of semaphore variable as soon as it
would become non-negative.
b. Signal(s) S:= S+1;
Signal operation increments the value of semaphore variable.
Modifications to the integer value of the semaphore in the wait and signal
operations are executed indivisibly. That is, when one process modifies the
semaphore no other process can simultaneously modify the same semaphore
value. In addition in the case of wait(s), the testing of the integer value of S (S
<= 0) and its possible modification (S :=S-1) must also be executed without any
interruption.
We can use binary semaphores to deal with the critical-section problem for
multiple processes. Counting semaphores can be used to control access to a given
resource consisting of a finite number of instances. The semaphore is initialized to
the number of resources available. Each process that wishes to use a resource
perform a wait() operation on the semaphore (thereby decrementing the count).
When a process releases a resource, it perform a signal() operation (incrementing
the count). When the count for the semaphore goes to 0, all resources are being
used. After that, processes that wish to use a resource will block until the count
becomes greater than 0.
The parent process in the program first initializes binary semaphore variable bsem
to 1 indicating that the source is available. As shown in the table (figure 8) at time
T1 no process is active to share the resource. But at time T2 all the three
processes become active and want to enter their critical sections to share the
resource by running the wait operation. At T2, the bsem is decremented to 0
which indicates that some processes has been given permission to enter the
critical section. At time T3, we find that it is P1 which has been given some
permission. One important thing is to be noted that only one process is allowed by
semaphore at a time to the critical section.
Once P1 is given the permission, it prevents other processes P2 & P3 to read the
value of bsem as 1 till the wait operation of P1 decrements bsem to 0. This is why
wait operation is executed without interruption.
After grabbing the control from semaphore P1 starts sharing the resource which is
depicted at time T3. At T4, P1 executes signal operation to release the resource
and comes out of its critical section. As shown in the table that the value of bsem
becomes 1 since the resource is now free.
The two remaining processes P2 and P3 have an equal chance to compete the
resource. In our example, process P3 become the next to enter the critical section
and to use the shared resource. At time T7, process P3 releases the resource and
semaphore variable bsem again becomes 1. At this time, the two other processes
P1 and P2 will attempt to compete for the resource and they have equal chance to
get access.
In our example, it is P2 which gets the chance but it might happens one of the
three processes could have never got the chance.
module Sem-mutex var bsem : semaphore; {binary
semaphore)
process P1;
Begin
while true do
wait (bsem);
Critical_section
Signal (bsem);
The rest_of P1_Processing
end; (P1)
process P2;
Begin
while true do
wait (bsem);
Critical-section;
signal (bsem);
The rest of P2-Processing
end; (P2)
process P3;
Begin
while true do
wait (bsem);
Critical-section;
signal (bsem);
The rest of P3-Processing
end; (P3)
(Parent process)
begin (sem-mutex)
bsem:= 1; (free)
initiate P1, P2, P3;
end; (Mutux)
Program 2. Mutual Exclusion with Semaphore
Figure 8: Run time behaviour of Process
We also present a table (figure 8) showing the run time behaviour of three
processes and functioning of semaphore. Each column of the table show the
activity of a particular process and the value of a semaphore after certain action
has been taken on this process.
Classical Problems of Synchronization
• Bounded-Buffer Problem
We assume that the pool consists of n buffers, each capable of holding one item.
The mutex semaphore provides mutual exclusion for accesses to the buffer pool
and is initialized to the value 1. The empty and full semaphores count the number
of empty and full buffers. The semaphore empty is initialized to the value n; the
semaphore full if initialized to the value 0.
while (true) {
wait (full);
wait (mutex);
signal (mutex);
signal (empty);
We can interpret these codes as the producer producing full buffers for the
consumer or as the consumer producing empty buffers for the producer.
Concurrently executing processes that are sharing a data object, such as a file or a
variable, fall into two groups: readers and writers. The processes in the readers
group want only to read the contents of the shared object, whereas, the
processes in writers group want to update (read and write) the value of shared
object. There is no problem if multiple readers access the shared object
simultaneously. However, if a writer and some other process (either a reader or
writer) access the shared object simultaneously, data may become inconsistent.
To ensure that such a problem does not arise, we must guarantee that when a
writer is accessing the shared object, no reader or writer accesses that shared
object. This synchronization problem is termed as readers-writers problem. The
readers-writers problem has several variations. The simplest one referred to as the
first reader-writer problem, requires that no reader will be kept waiting unless a
writer has already obtained permission to use the shared object. In other words,
no reader should wait for others readers to finish simply because a writer is
waiting. The second readers-writers problem requires that, once a writer is ready,
that writer performs its write as soon as possible. In other words, if a writer is
waiting to access the object, no new readers may start reading.
A solution to either problem may result in starvation. In the first case writers may
starve, in the second case, readers may starve.
Following is a solution to the first readers-writers problem. The reader process
share the following data structures:
semaphore mutex, wrt;
int readcount;
The semaphore mutex and wrt are initialized to 1 and readcount is initialized to 0.
The semaphore wrt is common to both reader and writer processes. The mutex
semaphore is used to ensure mutual exclusion when the variable readcount is
updated. The readcount variable keeps track of how many processes are currently
reading the object. The semaphore wrt functions as a mutual-exclusion semaphore
for the writers. It is also used by the first or last reader that enters or exists the
critical section. It is not used by readers who enter or exit while other readers are
in their critical section.
If a writer is in the critical section and n readers are waiting, then one reader is
queued on wrt, and n-1 readers are 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 waiting writer.
while (true) {
wait (wrt) ;
// writing is performed
signal (wrt) ;
}
while (true) {
wait (mutex) ;
readcount ++ ;
if (readercount == 1) wait (wrt) ;
signal (mutex)
// reading is performed
wait (mutex) ;
readcount - - ;
if (redacount == 0) signal (wrt) ;
signal (mutex) ;
}
• Dining-Philosophers Problem
Consider five philosophers sitting around a circular table. There is a bowl of rice in
the centre of the table and five chopsticks – one in between each pair of
philosophers.
Initially, all the philosophers are in the thinking phase and while thinking, they
make sure that they do not interact with each other. As time passes by,
philosophers might feel hungry. When a philosopher feels hungry, he attempts to
pick up the two chopsticks kept in close proximity to him ( that are in between him
and his left and his right philosophers). If the philosophers on his left and right are
not eating, he successfully gets the two chopsticks. With the two chopsticks in his
hand, he starts eating. After he finishes eating, the chopsticks are positioned back
on the table and the philosopher begins to think again. On the contrary, if the
philosopher on his left or right is already eating, then fails to grab the two
chopsticks at the same time, and thus, has to wait.
// eat
signal ( chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
}
This solution is simple and ensure that no two neighbors are eating at the same
time. However, the solution is not free from deadlock. Suppose all the
philosophers attempt to grab the chopsticks simultaneously and grab one
chopstick successfully. In this case, all the elements of chopstick will be 0. Thus,
when each philosopher attempts to grab the second chopstick, he will go in waiting
state forever.
Several possible remedies to the deadlock problem are available:
• Allow at most four philosophers to be sitting simultaneously at the table.
• Allow a philosopher to pick up his chopsticks only if both chopsticks are
available.
• Use an asymmetric solution; that is, an odd philosopher picks up first his left
chopstick and then his right chopsticks, whereas an even philosopher picks
up his right chopstick and then her left chopstick.
Dining-philosophers problem can be solved with the use of monitors.
Monitors
monitor <monitor-name>
{
// shared data ( or variable) declarations
data type <variable-name>;
…
// function (or procedure) declarations
return_type <function_name> (parameters)
{
// body of function
}
.
.
monitor-name()
{
// initialization
}
}
The variables defined inside a monitor can only be accessed by the functions
defined within the monitor, and it is not feasible for any process to access these
variables. Thus, if any process has to access these variables, it is only possible
through the execution of the functions defined inside the monitor. Further, the
monitor construct checks that only one process may be executing within the
monitor at a given moment. But if a process is executing within the monitor, then
other requesting processes are blocked and placed on an entry queue.
Schematic view of a monitor
However, the monitor construct, as defined so far, is not sufficiently powerful for
modeling some synchronization schemes. For this purpose, we need to define
additional synchronization mechanisms. These mechanisms are provided by the
condition construct. We can define a mechanism by defining variables of
condition type on which only two operations can be invoked: wait and signal.
dp.pickup(i);
…
eat
…
dp.putdown(i);
monitor DP
{
enum { THINKING; HUNGRY, EATING) state [5] ;
condition self [5];
void pickup (int i) {
state[i] = HUNGRY;
test(i);
if (state[i] != EATING)
self [i].wait;
}
void putdown (int i) {
state[i] = THINKING;
// test left and right neighbors
test((i + 4) % 5);
test((i + 1) % 5);
}
What is Thread?
A thread is a flow of execution throug h the process code, with its own prog ram counter, system reg isters and
stack. A thread is also called a lig ht weig ht process. T hreads provide a way to improve application performance
throug h parallelism. T hreads represent a software approach to improving performance of operating system by
reducing the overhead thread is equivalent to a classical process.
Each thread belong s to exactly one process and no thread can exist outside a process. Each thread represents
a separate flow of control.T hreads have been successfully used in implementing network servers and web
server. T hey also provide a suitable foundation for parallel execution of applications on shared memory
multiprocessors. Folowing fig ure shows the working of the sing le and multithreaded processes.
1 Process is heavy weig ht or resource intensive. T hread is lig ht weig ht taking lesser resources
than a process.
1 Process switching needs interaction with T hread switching does not need to interact with
operating system. operating system.
1 In multiple processing environments each All threads can share same set of open files, child
process executes the same code but has its processes.
own memory and file resources.
1 If one process is blocked then no other While one thread is blocked and waiting , second
process can execute until the first process is thread in the same task can run.
unblocked.
1 Multiple processes without using threads use Multiple threaded processes use fewer
more resources. resources.
1 In multiple processes each process operates One thread can read, write or chang e another
independently of the others. thread's data.
Advantag es of Thread
T hread minimize context switching time.
Efficient communication.
Types of Thread
T hreads are implemented in following two ways
Kernel Level T hreads -- Operating System manag ed threads acting on kernel, an operating system
core.
Advantag es
T hread switching does not require Kernel mode privileg es.
T he Kernel maintains context information for the process as a whole and for individuals threads within the
process. Scheduling by the Kernel is done on a thread basis. T he Kernel performs thread creation, scheduling
and manag ement in Kernel space. Kernel threads are g enerally slower to create and manag e than the user
threads.
Advantag es
Kernel can simultaneously schedule multiple threads from the same process on multiple processes.
If one thread in a process is blocked, the Kernel can schedule another thread of the same process.
Disadvantag es
Kernel threads are g enerally slower to create and manag e than the user threads.
T ransfer of control from one thread to another within same process requires a mode switch to the Kernel.
Multithreading Models
Some operating system provide a combined user level thread and Kernel level thread facility. Solaris is a g ood
example of this combined approach. In a combined system, multiple threads within the same application can run in
parallel on multiple processors and a blocking system call need not block the entire process. Multithreading
models are three types
Following diag ram shows the many to many model. In this model, developers can create as many user threads as
necessary and the corresponding Kernel threads can run in parallels on a multiprocessor.
Many to One Model
Many to one model maps many user level threads to one Kernel level thread. T hread manag ement is done in
user space. When thread makes a blocking system call, the entire process will be blocks. Only one thread can
access the Kernel at a time,so multiple threads are unable to run in parallel on multiprocessors.
If the user level thread libraries are implemented in the operating system in such a way that system does not
support them then Kernel threads use the many to one relationship modes.
Disadvantag e of this model is that creating user thread requires the corresponding Kernel thread. OS/2,
windows NT and windows 2000 use one to one relationship model.
Difference between User Level & Kernel Level Thread
1 User level threads are faster to create and Kernel level threads are slower to create and
manag e. manag e.
2 Implementation is by a thread library at the user Operating system supports creation of Kernel
level. threads.
3 User level thread is g eneric and can run on any Kernel level thread is specific to the operating
operating system. system.
1Q. a) Explain multithreading. Distinguish between single threaded process multithreaded process
b) Explain Peterson’s solution to Critical Section Problem. [ 6 + 6 M ,April 2014 ]
2Q. a) Define context switching. Explain queuing diagram representation of process scheduling. 6M
b) Write short notes on monitors and semaphores. 6M [April 2014]
6Q. a) Explain the critical section problem and describe the Peterson’s solution for the same.
b) Explain the Readers-Writers synchronization problem and describe the solution using
semaphores [ 6 + 6 Marks April 2011]
8Q. a) Describe
i) Shortest Job First scheduling
ii) Priority scheduling 6
b) Explain the scheduling criteria. 6 [ Apirl 2011]
Review Questions:
1Q. Describe the differences among short-term, medium-term, and long-term scheduling.
2Q. Describe the actions taken by a kernel to context-switch between processes.
***NOTE: Also concentrate other topics which are not covered in this questions.