unit-2-notes
unit-2-notes
com
Processes
Process Concept
A process is an instance of a program in execution.
An operating system executes a variety of programs:
o Batch system – jobs
o Time-shared systems – user programs or tasks
The Process
Process memory is divided into four sections.
o The program code, also called text section
o Current activity including program counter, processor registers
o Stack containing temporary data
Function parameters, return addresses, local variables
o Data section containing global variables
o Heap containing memory dynamically allocated during run time
A Process in memory
It is important that only one process can be running on any processor at any
instant.
Many processes may be ready and waiting.
2|Page
[Link]
Process state. The state may be new, ready, running, waiting, halted, and so on.
Program counter. The counter indicates the address of the next instruction to be
executed for this process.
CPU registers. The registers vary in number and type, depending on the
computer architecture. They include accumulators, index registers, stack pointers,
and general-purpose registers, plus any condition-code information. Along with
the program counter, this state information must be saved when an interrupt
occurs, to allow the process to be continued correctly afterward.
Threads
A process is a program that performs a single thread of execution. This single
thread of control allows the process to perform only one task at a time.
Most modern operating systems have extended the process concept to allow a
process to have multiple threads of execution and thus to perform more than one
task at a time.
This feature is especially beneficial on multicore systems, where multiple threads
can run in parallel.
3|Page
[Link]
On a system that supports threads, the PCB is expanded to include information for
each thread.
Process Scheduling
The objective of multiprogramming is to have some process running at all times,
to maximize CPU utilization.
The objective of time sharing is to switch the CPU among processes so frequently
that users can interact with each program while it is running.
Process scheduler – selects an available process (possibly from a set of several
available processes) for program execution on the CPU.
Scheduling Queues
• Job queue – set of all processes in the system
• Ready queue – set of all processes residing in main memory, ready and
waiting to execute. This queue is generally stored as a linked list. A ready-
queue header contains pointers to the first and final PCBs in the list. Each
PCB includes a pointer field that points to the next PCB in the ready
queue.
• Device queues – set of processes waiting for an I/O device. Each device
has its own device queue
Ready Queue and various I/O device Queues
4|Page
[Link]
In the first two cases, the process eventually switches fromthe 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.
Schedulers
A process migrates among the various scheduling queues throughout its lifetime.
The operating system must select, for scheduling purposes, processes from these
queues in some fashion.
The selection process is carried out by the appropriate scheduler.
Long-term scheduler (or job scheduler) – selects which processes should be
brought into the ready queue from the pool
o Long-term scheduler is invoked infrequently (seconds, minutes) - (may be
slow)
o The long-term scheduler controls the degree of multiprogramming (the
number of processes in memory)
o long-term scheduler make a careful selection. In general, most processes
can be described as either I/O bound or CPU bound.
5|Page
[Link]
Context Switch
When CPU switches to another process, the system must save the state of the old
process and load the saved state for the new process via a context switch
Context Switch - Switching the CPU to another process requires performing a
state save of the current process and a state restore of a different process.
Context of a process represented in the PCB
Context-switch time is pure overhead, because the system does no useful work
while switching.
Context-switch times are highly dependent on hardware support.
o some processors provide multiple sets of registers. A context switch here
simply requires changing the pointer to the current register set.
Operations on Processes
The processes in most systems can execute concurrently, and they may be created
and deleted dynamically. Thus, these systems must provide a mechanism for process
creation and termination.
During the course of execution, a process may create several new
processes.
6|Page
[Link]
The creating process is called a parent process, and the new processes
are called the children of that process.
Each of these new processes may in turn create other processes,
forming a tree of processes.
Most operating systems (including UNIX, Linux, and Windows)
identify processes according to a unique process identifier (or pid),
which is typically an integer number.
The pid provides a unique value for each process in the system, and it
can be used as an index to access various attributes of a process within
the kernel.
Process Creation
There are also two address-space possibilities for the new process:
• The child process is a duplicate of the parent process (it has the same
program and data as the parent).
• The child process has a new program loaded into it.
UNIX SYSTEM
7|Page
[Link]
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{pid t pid;
/* fork a child process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}else if (pid == 0) { /* child process */
execlp("/bin/ls","ls",NULL);
}else { /* parent process */
/* parent will wait for the child to complete */
wait(NULL);
printf("Child Complete");
}
return 0;
}
Process creation using the fork() system call.
8|Page
[Link]
Process Termination
Process executes last statement and then asks the operating system to delete it
using the exit() system call.
o Returns status data from child to parent (via wait())
o Process’ resources are deallocated by operating system
Parent may terminate the execution of children processes using the abort() system
call. Some reasons for doing so:
o Child has exceeded allocated resources
o Task assigned to child is no longer required
o The parent is exiting and the operating systems does not allow a child to
continue if its parent terminates
Some operating systems do not allow child to exists if its parent has terminated.
If a process terminates, then all its children must also be terminated.
cascading termination. All children, grandchildren, etc. are terminated.
The termination is initiated by the operating system.
The parent process may wait for termination of a child process by using the
wait()system call. The call returns status information and the pid of the
terminated process
pid = wait(&status);
If no parent waiting (did not invoke wait()) process is a zombie
If parent terminated without invoking wait , process is an orphan
9|Page
[Link]
Producer–Consumer problem
Paradigm for cooperating processes, producer process produces information that
is consumed by a consumer process
One solution to the producer–consumer problem uses shared memory. To allow
producer and consumer processes to run concurrently, we must have available a
buffer of items that can be filled by the producer and emptied by the consumer.
This buffer will reside in a region of memory that is shared by the producer and
consumer processes.
A producer can produce one item while the consumer is consuming another item.
The producer and consumer must be synchronized, so that the consumer does not
try to consume an item that has not yet been produced.
10 | P a g e
[Link]
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
The shared buffer is implemented as a circular array with two logical pointers: in and out.
- The variable in points to the next free position in the buffer; out points to the
first full position in the buffer.
- The buffer is empty when in == out;
- The buffer is full when ((in + 1) % BUFFER SIZE) == out.
Bounded-Buffer – Producer
item next_produced;
while (true) {
/* produce an item in next produced */
while (((in + 1) % BUFFER_SIZE) == out)
; /* do nothing */
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
}
11 | P a g e
[Link]
o receive(message)
The message size is either fixed or variable
Implementation of communication link
o Physical:
Shared memory
Hardware bus
Network
o Logical:
Direct or indirect
Synchronous or asynchronous
Automatic or explicit buffering
Naming
Processes that want to communicate must have a way to refer to each other.
They can use either direct or indirect communication.
Direct Communication
Each process that wants to communicate must explicitly name the recipient or
sender of the communication. In this scheme, the send() and receive() primitives are
defined as:
send(P, message)—Send a message to process P.
receive(Q, message)—Receive a message from process Q.
Properties of Communication Link
• A link is established automatically between every pair of processes that want to
communicate. The processes need to know only each other’s identity to communicate.
• A link is associated with exactly two processes.
• Between each pair of processes, there exists exactly one link.
This scheme exhibits symmetry in addressing; that is, both the sender process and
the receiver process must name the other to communicate.
A variant of this scheme employs asymmetry in addressing. Here, only the sender names
the recipient; the recipient is not required to name the sender.
In this scheme, the send() and receive() primitives are defined as follows:
send(P, message)—Send a message to process P.
receive(id, message)—Receive a message from any process. The variable id is set
to the name of the process with which communication has taken place.
Indirect Communication
Messages are directed and received from mailboxes (also referred to as ports)
o Each mailbox has a unique id
o Processes can communicate only if they share a mailbox
Properties of communication link
o Link established only if processes share a common mailbox
o A link may be associated with many processes
o Each pair of processes may share several communication links
o Link may be unidirectional or bi-directional
Operations
• create a new mailbox (port)
• send and receive messages through mailbox
• destroy a mailbox
12 | P a g e
[Link]
Buffering
13 | P a g e
[Link]
Threads
Overview
A thread is a basic unit of CPU utilization.
It comprises a thread ID, a program counter, a register set, and a stack.
It shares with other threads belonging to the same process its code section, data
section, and other operating-system resources, such as open files and signals.
A traditional (or heavyweight) process has a single thread of control.
If a process has multiple threads of control, it can perform more than one task at a
time.
Motivation
Most modern applications are multithreaded
Threads run within application
Multiple tasks with the application can be implemented by separate threads
◦ Update display
◦ Fetch data
◦ Spell checking
Answer a network request
14 | P a g e
[Link]
Threads also play a vital role in remote procedure call (RPC) systems.
Can simplify code, increase efficiency
Most operating system kernels are generally multithreaded.
Benefits
The benefits of multithreaded programming can be broken down into four major
categories:
1. Responsiveness.
Multithreading an interactive application may allow a program to continue
running even if part of it is blocked or is performing a lengthy operation, thereby
increasing responsiveness to the user.
2. Resource sharing.
Processes can only share resources through techniques such as shared memory
and message passing. Such techniques must be explicitly arranged by the programmer.
However, threads share the memory and the resources of the process to which
they belong by default.
The benefit of sharing code and data is that it allows an application to have
several different threads of activity within the same address space.
3. Economy.
Allocating memory and resources for process creation is costly.
4. Scalability.
The benefits of multithreading can be even greater in a multiprocessor
architecture, where threads may be running in parallel on different processing cores.
Multicore Programming
Whether the cores appear across CPU chips or within CPU chips, we call these
systems multicore or multiprocessor systems.
Multithreaded programming provides a mechanism for more efficient use
of these multiple computing cores and improved concurrency.
15 | P a g e
[Link]
On a system with a single computing core, concurrency merely means that the
execution of the threads will be interleaved over time because the processing core is
capable of executing only one thread at a time.
On a system with multiple cores, however, concurrency means that the threads
can run in parallel, because the system can assign a separate thread to each core.
Parallelism implies a system can perform more than one task simultaneously
Concurrency supports more than one task making progress
Programming Challenges
Designers of operating systems must write scheduling algorithms that use
multiple processing cores to allow the parallel execution.
For application programmers, the challenge is to modify existing programs as
well as design new programs that are multithreaded.
Types of parallelism
• Data parallelism – distributes subsets of the same data across multiple
cores, same operation on each
16 | P a g e
[Link]
Multithreading models
Support for threads may be provided either at the user level, for user threads, or
by the kernel, for kernel threads.
User threads are supported above the kernel and are managed without kernel
support, whereas kernel threads are supported and managed directly by the
operating system.
User threads Kernel threads
User threads are supported above the Kernel threads are supported directly by the
Kernel and are implemented by a thread operating system
library at the user level
Thread creation & scheduling are done in Thread creation, scheduling and
the user space, without kernel. Therefore management are done by the operating
they are fast to create and manage system.
Blocking system call will cause the entire If the thread performs a blocking system
process to block call, the kernel can schedule another thread
in the application for execution
Many-to-One
One-to-One
Many-to-Many
Many-to-One Model
17 | P a g e
[Link]
One–to-one model
Many-to-Many Model
18 | P a g e
[Link]
One variation on the many-to-many model still multiplexes many user level threads to a
smaller or equal number of kernel threads but also allows a user-level thread to be bound
to a kernel thread. This variation is sometimes referred to as the two-level model.
The native process structures and services provided by the Windows Kernel are
relatively simple and general purpose, allowing each OS subsystem to emulate a
particular process structure and functionality.
Characteristics of Windows processes:
• Windows processes are implemented as objects.
• A process can be created as new process, or as a copy of an existing process.
• An executable process may contain one or more threads.
• Both process and thread objects have built-in synchronization capabilities.
19 | P a g e
[Link]
Windows uses the token to validate the user’s ability to access secured objects or
to perform restricted functions on the system and on secured objects. The access
token controls whether the process can change its own attributes.
Related to the process is a series of blocks that define the virtual address space
currently assigned to this process.
The process cannot directly modify these structures but must rely on the virtual
memory manager, which provides a memory allocation service for the process.
The process includes an object table, with handles to other objects known to this
process. Figure The process has access to a file object and to a section object that
defines a section of shared memory.
20 | P a g e
[Link]
21 | P a g e
[Link]
The thread processor affinity is the set of processors in a multiprocessor system that may
execute this thread; this set is equal to or a subset of the process processor affinity.
Multithreading
Windows supports concurrency among processes because threads in different
processes may execute concurrently (appear to run at the same time).
Multiple threads within the same process may be allocated to separate processors
and
execute simultaneously (actually run at the same time).
A multithreaded process achieves concurrency without the overhead of using
multiple processes.
Threads within the same process can exchange information through their common
address space and have access to the shared resources of the process.
Threads in different processes can exchange information through shared memory
that has been set up between the two processes.
Thread States
22 | P a g e
[Link]
Basic Concepts:
CPU scheduling is the basis for multi-programmed operating systems.
By switching the CPU among processes, the OS can make the computer work
more productive.
23 | P a g e
[Link]
CPU Scheduler
Whenever 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, or CPU scheduler.
The scheduler selects a single process from the memory among various processes that are
ready to execute, and allocates the CPU to one of them.
Dispatcher
Dispatcher is a module that gives control of the CPU to the process selected by the short-
term scheduler. This function involves the following:
switching context.
switching to user mode.
jumping to the proper location in the user program to restart that program.
Dispatch latency – The time taken for the dispatcher to stop one process and start
another running.
Scheduling Criteria
Many criteria are there for comparing CPU scheduling algorithm. Some criteria
required for determining the best algorithm are given below.
CPU utilization – keep the CPU as busy as possible. The range is about 40% for
lightly loaded system and about 90% for heavily loaded system.
Throughput – The number of processes that complete their execution per time unit.
Turnaround time – The interval from the time of submission of a process to the time
of completion is the Turnaround time.
25 | P a g e
[Link]
Waiting time – amount of time a process has been waiting in the ready queue (or) the
sum of the periods spent waiting in the ready queue.
Response time – amount of time it takes from when a request was submitted until the
first response is produced, not the output (for time-sharing environment) is the
response time.
Example Problem
Consider the following set of processes that arrive at time 0, with the length of the
CPU burst time given in milliseconds:
Burst
Process
Time(ms)
P1 24
P2 3
P3 3
P P P
1 2 3
0 24 27 30
26 | P a g e
[Link]
Waiting time
Waiting time for P1 = 0; P2 = 24; P3 = 27
Average waiting time: (0 + 24 + 27)/3 = 17 ms.
Turnaround Time = Waiting Time + Burst Time
Turnaround Time for P1 = (0+24)=24; P2 = (24+3)=27; P3 = (27+3)=30
Average Turnaround Time = (24+27+30)/3 = 27 ms
Convoy effect
All other processes wait for the one big process to get off the CPU. This results in lower
CPU and device utilization which could be overcome if shorter processes were allowed to
go first.
This algorithm associates with each process the length of its next CPU burst. Use
these lengths to schedule the process with the shortest time.
When the CPU is available, it is assigned to the process that has the smallest next
CPU burst. It is also called as shortest next CPU burst.
If two processes have the same length next CPU burst, FCFS scheduling is used
to break the tie.
SJF is optimal – gives minimum average waiting time for a given set of processes
(by moving the 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,
therefore the average waiting time decreases.
The difficulty with the SJF algorithm is knowing the length of the next CPU
request.
Example - Nonpreemptive
P
4
P
1
P
3
P
2
0 3 9 1
6 2
4
Waiting time for P1 = 3; P2 = 16; P3 = 9; P4 = 0
Average waiting time = (3 + 16 + 9 + 0) / 4 = 7
Turnaround Time P1=9; P2=24; P3=16; P4=3
ATT = (9+24+16+3)/4 = 27 ms
27 | P a g e
[Link]
P
1
P
2
P
4
P
1
P
3
0 1 5 1
0 1
7 2
6
Waiting Time = Finishing Time-(Arrival time + Burst Time)
WT of P1 = 17-(0+8) = 9; P2=0; P3=15; P4=2
Average waiting time = [(10-1)+(1-1)+(17-2)+5-3)]/4 = 26/4 = 6.5 msec
Priority Scheduling
28 | P a g e
[Link]
P2 P5 P1 P3 P4
0 1 6 16 18 19
Example
P
1
P
2
P
3
P
1
P
1
P
1
P
1
P
1
0 4 7 1
0 1
4 1
8 2
2 2
6 3
0
30 | P a g e
[Link]
Each queue has its own algorithm for example the foreground queue might be
scheduled by an RR algorithm and the background queue is scheduled by an
FCFS algorithm.
The foreground queue have absolute priority over background queue.
Scheduling must be done between the queues
Fixed priority scheduling; (i.e., serve all from foreground then from background).
Due to fixed priority scheduling there are possibility of starvation.
The solution to this problem is : “Time slice” – each queue gets a certain amount
of CPU time which it can schedule amongst its processes; i.e., 80% to foreground
in RR 20% to background in FCFS.
No process in the batch queue could run unless the queues for system processes,
interactive processes and interactive editing processes were empty.
Q2 – FCFS
Scheduling
A new job enters queue Q0 which is served FCFS. When it gains CPU, job
receives 8 milliseconds. If it does not finish in 8 milliseconds, job is moved to
queue Q1.
At Q1 job is again served FCFS and receives 16 additional milliseconds. If it still
does not complete, it is preempted and moved to queue Q2.
Multilevel Feedback Queues
Process Synchronization
Cooperating processes can either directly share a logical address space (that is,
both code and data) or be allowed to share data only through files or messages.
Concurrent access to shared data may result in data inconsistency.
Maintaining data consistency requires mechanisms to ensure the orderly
execution of cooperating processes
Race Condition
Producer-consumer problem. It is described that how a bounded buffer could be
used to enable processes to share memory
o Bounded buffer problem. The solution allows at most
BUFFERSIZE-1 items in the buffer at the same time.
o An integer variable counter, initialized to 0. counter is incremented every
time we add a new item to the buffer and is decremented every time we
remove one item from the buffer.
; /* do nothing */
nextConsumed = buffer [out] ;
out = (out + 1) % BUFFER_SIZE;
counter--;
/* consume the item in nextConsumed */
}
Suppose that the value of the variable counter is currently 5 and that the producer
and consumer processes concurrently execute the statements “counter++” and
“counter--”.
Following the execution of these two statements, the value of the variable counter
may be 4, 5, or 6! The only correct result, though, is counter == 5, which is
generated correctly if the producer and consumer execute separately.
We can show that the value of counter may be incorrect as follows. Note that the
statement “counter++” may be implemented in machine language (on a typical
machine) as follows:
o register1 = counter
o register1 = register1 + 1
o counter = register1
where register1 is one of the local CPU registers. Similarly, the statement
“counter--” is implemented as follows:
o register2 = counter
o register2 = register2 − 1
o counter = register2
where again register2 is one of the local CPU registers. Even though register1
and register2 may be the same physical register (an accumulator, say), remember
that the contents of this register will be saved and restored by the interrupt
handler.
The concurrent execution of “counter++” and “counter--” is equivalent to a
sequential execution in which the lower-level statements presented previously are
interleaved in some arbitrary order One such interleaving is the following:
o T0: producer execute register1 = counter {register1 = 5}
o T1: producer execute register1 = register1 + 1 {register1 = 6}
o T2: consumer execute register2 = counter {register2 = 5}
o T3: consumer execute register2 = register2 − 1 {register2 = 4}
o T4: producer execute counter = register1 {counter = 6}
o T5: consumer execute counter = register2 {counter = 4}
Notice that we have arrived at the incorrect state “counter == 4”, indicating that
four buffers are full, when, in fact, five buffers are full. If we reversed the order of
the statements at T4 and T5, we would arrive at the incorrect state “counter == 6”.
Although both the producer and consumer routines are correct separately, they
may not function correctly when executed concurrently.
We would arrive at incorrect state because we allowed both processes to
manipulate the variable counter concurrently.
A race condition is a situation where two or more processes access shared data
concurrently and final value of shared data depends on timing (race to access and
modify data)
33 | P a g e
[Link]
Definition - To guard against the race condition, we need to ensure that only one
process at a time can be manipulating the variable counter and this is referred as
process synchronization.
Definition: Each process has a segment of code, called a critical section (CS), in
which the process may be changing common variables, updating a table, writing a file,
and so on.
The important feature of the system is that, when one process is executing in its
CS, no other process is to be allowed to execute in its CS.
That is, no two processes are executing in their CSs at the same time.
Each process must request permission to enter its CS. The section of code
implementing this request is the entry section.
The CS may be followed by an exit section.
The remaining code is the remainder section.
Peterson's Solution
A classic software-based solution to the critical-section problem known as
Peterson's solution.
Does not require strict alternation.
34 | P a g e
[Link]
35 | P a g e
[Link]
To prove property 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 imply that P0 and P1 could not have successfully
executed their while statements at about the same time.
Mutex Locks
A mutex lock has a boolean variable available whose value indicates if the lock is
available or not.
If the lock is available, a call to acquire() succeeds, and the lock is then
considered unavailable.
A process that attempts to acquire an unavailable lock is blocked until the lock is
released.
The definition of acquire() is as follows:
acquire()
{
while (!available); /* busy wait */
available = false;;
}
36 | P a g e
[Link]
Semaphores
Semaphore Usage
The value of a counting semaphore can range over an unrestricted domain. The
value of a binary semaphore can range only between 0 and 1.
Binary semaphores behave similarly to mutex locks.
On systems that do not provide mutex locks, binary semaphores can be used
instead for providing mutual exclusion.
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 performs a wait() operation on the
semaphore (thereby decrementing the count).
When a process releases a resource, it performs a signal() operation (incrementing
the count).
37 | P a g e
[Link]
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
We can also use semaphores to solve various synchronization problems.
For example, consider two concurrently running processes: P1 with a statement
S1 and P2 with a statement S2. Suppose we require that S2 be executed only after
S1 has completed. We can implement this scheme readily by letting P1 and P2
share a common semaphore synch, initialized to 0. In process P1, we insert the
statements
S1;
signal(synch);
Semaphore Implementation
To overcome the need for busy waiting, we can modify the definition of the wait()
and signal() operations as follows: When a process executes the wait() operation
and finds that the semaphore value is not positive, it must wait.
Rather than engaging in busy waiting, the process can block itself.
The block operation places a process into a waiting queue associated with the
semaphore, and the state of the process is switched to the waiting state.
Then control is transferred to the CPU scheduler, which selects another process to
execute.
A process that is blocked, waiting on a semaphore S, should be restarted when
some other process executes a signal() operation.
The process is restarted by a wakeup() operation, which changes the process from
the waiting state to the ready state.
The process is then placed in the ready queue. (The CPU may or may not be
switched from the running process to the newly ready process, depending on the
CPU-scheduling algorithm.)
To implement semaphores under this definition, we define a semaphore as
follows:
typedef struct
{
int value;
struct process *list;
} semaphore;
38 | P a g e
[Link]
A signal() operation removes one process from the list of waiting processes and
awakens that process.
The wait() semaphore operation can be defined as
wait(semaphore *S)
{
S->value--;
if (S->value < 0)
{
add this process to S->list;
block();
}
}
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
.. ..
.. ..
.. ..
signal(S); signal(Q);
signal(Q); signal(S);
Priority Inversion
A scheduling challenge arises when a higher-priority process needs to read or
modify kernel data that are currently being accessed by a lower-priority process—
or a chain of lower-priority processes.
Since kernel data are typically protected with a lock, the higher-priority process
will have to wait for a lower-priority one to finish with the resource.
The situation becomes more complicated if the lower-priority process is
preempted in favor of another process with a higher priority.
This problem is known as priority inversion. It occurs only in systems with more
than two priorities, so one solution is to have only two priorities.
Typically these systems solve the problem by implementing a priority-
inheritance protocol. According to this protocol, all processes that are accessing
resources needed by a higher-priority process inherit the higher priority until they
are finished with the resources in question.
When they are finished, their priorities revert to their original values. In the
example above, a priority-inheritance protocol would allow process L to
temporarily inherit the priority of process
The producer and consumer processes share the following data structures:
int n;
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0
40 | P a g e
[Link]
We can interpret this code as the producer producing full buffers for the consumer
or as the consumer producing empty buffers for the producer.
In the solution to the first readers–writers problem, the reader processes share the
following data structures:
semaphore rw mutex = 1;
semaphore mutex = 1;
int read count = 0;
The semaphores mutex and rw mutex are initialized to 1; read count is initialized
to 0.
The semaphore rw mutex is common to both reader and writer processes.
The mutex semaphore is used to ensure mutual exclusion when the variable read
count is updated.
The read count variable keeps track of how many processes are currently reading
the object.
The semaphore rw mutex functions as a mutual exclusion semaphore for the
writers.
wait(mutex);
read count--;
if (read count == 0)
signal(rw mutex);
signal(mutex);
} while (true);
When a philosopher thinks, she does not interact with her colleagues.
From time to time, a philosopher gets hungry and tries to pick up the two
chopsticks that are closest to her (the chopsticks that are between her and
her left and right neighbors).
A philosopher may pick up only one chopstick at a time. Obviously, she
cannot pick up a chopstick that is already in the hand of a neighbor.
When a hungry philosopher has both her chopsticks at the same time, she
eats without releasing her chopsticks.
When she is finished eating, she puts down both of her chopsticks and
starts thinking again.
43 | P a g e
[Link]
Monitors
44 | P a g e
[Link]
Suppose that a process interchanges the order in which the wait() and signal()
operations on the semaphore mutex are executed, resulting in the following
execution:
signal(mutex);
...
critical section
...
wait(mutex);
In this situation, several processes maybe executing in their CSs
simultaneously, violating the mutual-exclusion requirement.
This error may be discovered only if several processes are simultaneously
active in their CSs. Note that this situation may not always be
reproducible.
Suppose that a process replaces signal(mutex) with wait(mutex). That is, it
executes
wait(mutex);
...
critical section
...
wait(mutex);
In this case, a deadlock will occur.
Suppose that a process omits the wait(mutex), or the signal(mutex), or both. In
this case, either mutual exclusion is violated or a deadlock will occur.
Monitor Usage
A type, or abstract data type, encapsulates private data with public methods to
operate on that data. A monitor type presents a set of programmer-defined
operations that are provided mutual exclusion within the monitor.
The monitor type also contains the declaration of variables whose values define
the state of an instance of that type, along with the bodies of procedures or
functions that operate on those variables.
Syntax of a monitor.
45 | P a g e
[Link]
condition x, y;
The only operations that can be invoked on a condition variable are wait() and signal().
The operation
[Link]();
means that the process invoking this operation is suspended until another process invokes
[Link]();
The [Link]() operation resumes exactly one suspended process.
Philosopher i can set the variable state[i] = EATING only if her two neighbors are not
eating:
(state[(i+4) % 5] != EATING) and(state[(i+1) % 5] != EATING).
self[i].wait();
}
void putdown(int i)
{
state[i] = THINKING;
test((i + 4) % 5);
test((i + 1) % 5);
}
void test(int i)
{
if ((state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) &&
(state[(i + 1) % 5] != EATING))
{
state[i] = EATING;
self[i].signal();
}
}
initialization code()
{
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
48 | P a g e
[Link]
An integer variable
next count is also provided to count the number of processes suspended on next
Thus, each external function F is replaced by
wait(mutex);
...
body of F
...
if (next count > 0)
signal(next);
else
signal(mutex);
We can now describe how condition variables are implemented as well. For each
condition x, we introduce a semaphore x sem and an integer variable x count, both
initialized to 0.
The operation [Link]() can now be implemented as
x count++;
if (next count > 0)
signal(next);
else
signal(mutex);
wait(x sem);
x count--;
The operation [Link]() can be implemented as
if (x count > 0)
{
next count++;
signal(x sem);
wait(next);
next count--;
}
49 | P a g e
[Link]
When [Link]() is executed, the process with the smallest priority number is
resumed next.
A process that needs to access the resource must observe the following sequence:
[Link](t);
...
access the resource;
...
[Link]();
where R is an instance of type ResourceAllocator.
Dead lock
Definition:
A process request resources, if the resources are not available at that time, the
process enters in to a wait state. It may happen that waiting processes will never again
change the state, because the resources they have requested are held by other
waiting processes. This situation is called as dead lock.
System Model
A system consists of a finite number of resources to be distributed among a
number of competing processes.
The resources may be partitioned into several types (or classes), each consisting
of some number of identical instances.
CPUcycles, files,and I/O devices (such as printers and DVD drives) are examples
of resource types.
50 | P a g e
[Link]
If a system has two CPUs, then the resource type CPU has two instances.
Similarly, the resource type printer may have five instances.
If a process requests an instance of a resource type, the allocation of any instance
of the type should satisfy the request.
A process must request a resource before using it and must release the resource
after using it.
Under the normal mode of operation, a process may utilize a resource in only the
following sequence:
[Link]. The process requests the resource. If the request cannot be granted
immediately then the requesting process must wait until it can acquire the resource.
2. Use. The process can operate on the resource
3. Release. The process releases the resource.
Deadlock Characterizations:-
In a deadlock, processes never finish executing, and system resources are tied up,
preventing other jobs from starting.
Resource-Allocation Graph
Deadlocks can be described more precisely in terms of a directed graph called a
system resource-allocation graph.
This graph consists of a set of vertices V and a set of edges E.
The set of vertices V is partitioned into two different types of nodes:
P = {P1, P2, ...,Pn}, the set consisting of all the active processes in the system,
and R = {R1, R2, ..., Rm}, the set consisting of all resource types in the system.
Adirected edge fromprocess Pi to resource type Rj is denoted by Pi → Rj ; it
signifies that process Pi has requested an instance of resource type Rj andis
currently waiting for that resource.
A directed edge from resource type Rjto process Pi is denoted by Rj → Pi ; it
signifies that an instance of resourcetype Rj has been allocated to process Pi .
51 | P a g e
[Link]
Processes P1, P2, and P3 are deadlocked. Process P2 is waiting for the resource
R3, which is held by process P3. Process P3 is waiting for either process P1 or
process P2 to release resource R2. In addition, process P1 is waiting for process
P2 to release resource R1.
we also have a cycle: P1 → R1 → P3 → R2 → P1
if the graph contains no cycles, then no process in the system is deadlocked.
52 | P a g e
[Link]
Deadlock Prevention
For a deadlock to occur, each of the four necessary conditions must hold.
By ensuring that at least one of these conditions cannot hold, we can prevent the
occurrence of a deadlock.
Mutual Exclusion
The mutual exclusion condition must hold. That is, at least one resource must be
non sharable.
Sharable resources do not require mutually exclusive access and thus cannot be
involved in a deadlock.
Read-only files are a good example of a sharable resource. If several processes
attempt to open a read-only file at the same time, they can be granted
simultaneous access to the file.
A process never needs to wait for a sharable resource.
We cannot prevent deadlocks by denying the mutual-exclusion condition, because
some resources are intrinsically non sharable.
53 | P a g e
[Link]
To ensure that the hold-and-wait condition never occurs in the system, we must
guarantee that, whenever a process requests a resource, it does not hold any other
resources.
One protocol that can be used requires each process to request and be allocated all
its resources before it begins execution.
An alternative protocol allows a process to request resources only when it has
none. A process may request some resources and use them. Before it can request
any additional resources, however, it must release all the resources that it is
currently allocated.
To illustrate the difference between these two protocols, we consider a process
that copies data from a DVD drive to a file on disk, sorts the file, and then prints
the results to a printer.
Both these protocols have two main disadvantages.
o First, resource utilization may be low, since resources may be allocated
but unused for a long period.
o Second, starvation is possible. A process that needs several popular
resources may have to wait indefinitely, because at least one of the
resources that it needs is always allocated to some other process.
No Preemption
The third necessary condition for deadlocks is that there be no preemption of
resources that have already been allocated.
To ensure that this condition does not hold, we can use the following protocol.
o If a process is holding some resources and requests another resource that
cannot be immediately allocated to it (that is, the process must wait), then
all resources currently being held are preempted.
o The preempted resources are added to the list of resources for which the
process is waiting. The process will be restarted only when it can regain its
old resources, as well as the new ones that it is requesting
Circular Wait
One way to ensure that this condition never holds is to impose a total ordering of
all resource types and to require that each process requests resources in an
increasing order of enumeration.
Assign to each resource type a unique integer number, which allows us to
compare two resources and to determine whether one precedes another in our
ordering.
Each process can request resources only in an increasing order of enumeration.
If these two protocols are used, then the circular-wait condition cannot hold.
Deadlock Avoidance
Safe State
A state is safe if the system can allocate resources to each process (up to its
maximum) in some order and still avoid a deadlock.
A system is in a safe state only if there exists a safe sequence.
Safe sequence
55 | P a g e
[Link]
Example
P0, P1, and P2. Process P0 requires ten tape drives, process P1 may need as any
as four tape drives, and process P2 may need up to nine tape drives. Suppose that, at time
t0, process P0 is holding five tape drives, process P1 is holding two tape drives, and
process P2 is holding two tape drives.
Maximum Needs Current Needs
P0 10 5
P1 4 2
P2 9 2
At time t0, the system is in a safe state. The sequence <P1, P0, P2> satisfies the
safety condition. Process P1 can immediately be allocated all its tape drives and
then return them then process P0 can get all its tape drives and return them and
finally process P2 can get all its tape drives and return them
A system can go from a safe state to an unsafe state.
Suppose that, at time t1, process P2 requests and is allocated one more tape drive.
The system is no longer in a safe state. At this point, only process P1 can be
allocated all its tape drives.
When it returns them, the system will have only four available tape drives. Since
process P0 is allocated five tape drives but has a maximum of ten, it may request
five more tape drives.
If it does so, it will have to wait, because they are unavailable. Similarly, process
P2 may request six additional tape drives and have to wait, resulting in a
deadlock.
Resource-Allocation-Graph
A new type of edge, called a claim edge is introduced.
A claim edge Pi → Rj indicates that process Pi may request resource Rj at some
time in the future. This edge resembles a request edge in direction but is
represented in the graph by a dashed line.
When process Pi requests resource Rj , the claim edge Pi → Rj is converted to a
request edge. Similarly, when a resource Rj is released by Pi , the assignment
edge Rj → Pi is reconverted to a claim edge Pi → Rj .
That is, before process Pi starts executing, all its claim edges must already appear
in the resource-allocation graph.
56 | P a g e
[Link]
If no cycle exists, then the allocation of the resource will leave the system in a
safe state.
If a cycle is found, then the allocation will put the system in an unsafe state. In
that case, process Pi will have to wait for its requests to be satisfied.
Banker’s Algorithm
The resource-allocation-graph algorithm is not applicable to a resource allocation
system with multiple instances of each resource type.
The name was chosen because the algorithm could be used in a banking system to
ensure that the bank never allocated its available cash in such a way that it could
no longer satisfy the needs of all its customers.
When a new process enters the system, it must declare the maximum number of
instances of each resource type that it may need. This number may not exceed the
total number of resources in the system.
When a user requests a set of resources, the system must determine whether the
allocation of these resources will leave the system in a safe state. If it will, the
resources are allocated; otherwise, the process must wait until some other process
releases enough resources.
Data structures
[Link] of lengthmindicates the number of available resources of each
type. If Available[j] equals k, then k instances of resource type Rj are available.
Max. An n × m matrix defines the maximum demand of each process. If Max[i][j]
equals k, then process Pi may request at most k instances of resource type Rj .
Allocation. An n × m matrix defines the number of resources of each type
currently allocated to each process. If Allocation[i][j] equals k, then process Pi is
currently allocated k instances of resource type Rj .
Need. An n × m matrix indicates the remaining resource need of each process. If
Need[i][j] equals k, then process Pi may need k more instances of resource type Rj
to complete its task. Note that Need[i][j] equalsMax[i][j] − Allocation[i][j].
Safety Algorithm
We can now present the algorithm for finding out whether or not a system is
in a safe state.
This algorithm can be described as follows:
1. Let Work and Finish be vectors of length m and n, respectively.
57 | P a g e
[Link]
This algorithm may require an order ofm × n2 operations to determine whether a state is
safe.
Resource-Request Algorithm
Let Requesti be the request vector for process Pi . If Requesti [ j] == k, then
process Pi wants k instances of resource type Rj . When a request for resources is made
by process Pi , the following actions are taken:
1. If Requesti ≤Needi , go to step 2. Otherwise, raise an error condition, since the
process has exceeded its maximum claim.
2. If Requesti ≤ Available, go to step 3. Otherwise, Pi must wait, since the
resources are not available.
4. Have the system pretend to have allocated the requested resources to process Pi
by modifying the state as follows:
Available = Available–Requesti ;
Allocationi = Allocationi + Requesti ;
Needi = Needi –Requesti ;
If the resulting resource-allocation state is safe, the transaction is completed, and process
Pi is allocated its resources. However, if the new state is unsafe, then Pi must wait for
Requesti , and the old resource-allocation state is restored.
Deadlock Detection
58 | P a g e
[Link]
To detect deadlocks, the system needs to maintain the wait for graph and
periodically invoke an algorithm that searches for a cycle in the graph.
An algorithm to detect a cycle in a graph requires an order of n2 operations,
where n is the number of vertices in the graph.
Data Structures
• [Link] of lengthmindicates the number of available resources of
each type.
• Allocation. An n × m matrix defines the number of resources of each type
currently allocated to each process.
• Request. An n × m matrix indicates the current request of each process. If
Request[i][j] equals k, then process Pi is requesting k more instances of resource
type Rj .
Algorithm:
b. Requesti ≤Work
If no such i exists, go to step 4.
3. Work =Work + Allocationi
Finish[i] = true
Go to step 2.
4. If Finish[i] ==false for some i, 0≤i<n, then the system is in a deadlocked
state. Moreover, if Finish[i] == false, then process Pi is deadlocked.
Detection-Algorithm Usage
When should we invoke the detection algorithm? The answer depends on two
factors:
1. How often is a deadlock likely to occur?
2. How many processes will be affected by deadlock when it happens?
If deadlocks occur frequently, then the detection algorithm should be invoked
frequently. Resources allocated to deadlocked processes will be idle until the deadlock
can be broken. In addition, the number of processes involved in the deadlock cycle may
grow.
Deadlocks occur only when some process makes a request that cannot be granted
immediately. This request may be the final request that completes a chain of waiting
processes.
Process Termination
To eliminate deadlocks by aborting a process, we use one of two methods. In both
methods, the system reclaims all resources allocated to the terminated processes.
• Abort all deadlocked processes. This method clearly will break the deadlock
cycle, but at great expense. The deadlocked processes may have computed for a long
time, and the results of these partial computations must be discarded and probably will
have to be recomputed later.
• Abort one process at a time until the deadlock cycle is eliminated. This
method incurs considerable overhead, since after each process is aborted, a deadlock-
detection algorithm must be invoked to determine whether any processes are still
deadlocked.
Many factors may affect which process is chosen, including:
1. What the priority of the process is
60 | P a g e
[Link]
2. How long the process has computed and how much longer the process will
compute before completing its designated task
3. How many and what types of resources the process has used(for example,
whether the resources are simple to preempt)
4. How many more resources the process needs in order to complete
5. How many processes will need to be terminated
6. Whether the process is interactive or batch
Resource Preemption
To eliminate deadlocks using resource preemption, we successively preempt some
resources from processes and give these resources to other processes until the deadlock
cycle is broken.
Issues
1. Selecting a victim.
Which resources and which processes are to be preempted?
As in process termination, we must determine the order of preemption to
minimize cost.
Cost factors may include such parameters as the number of resources a
deadlocked process is holding and the amount of time the process has thus
far consumed.
2. Rollback.
If we preempt a resource from a process, what should be done with that
process?
We must roll back the process to some safe state and restart it from that
state.
Since, in general, it is difficult to determine what a safe state is, the
simplest solution is a total rollback: abort the process and then restart it.
Although it is more effective to roll back the process only as far as
necessary to break the deadlock, this method requires the system to keep
more information about the state of all running processes.
3. Starvation.
How do we ensure that starvation will not occur?
How can we guarantee that resources will not always be preempted from
the same process?
61 | P a g e