0% found this document useful (0 votes)
22 views

chp6 Os

Uploaded by

Nasir Ali
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views

chp6 Os

Uploaded by

Nasir Ali
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 62

OPERATING SYSTEM (IT-224)

(ASSIGNMENT 2)

Submitted To: Ms. Huda Gull

Submitted By: Alishba Sehar

Roll No: 22011556-105

Major: BSIT-IV

Section: B

Submission Date: April 30,2024

UNIVERSITY OF GUJRAT

DEPARTMENT OF INFORMATION TECHNOLOGY (2022-2026)


1
2
3
4
The code for the

producer process can be modified as follows:

while (true) {

/* produce an item in next produced */

while (count == BUFFER SIZE)

; /* do nothing */

buffer[in] = next produced;

in = (in + 1) % BUFFER SIZE;

count++;

The code for the consumer process can be modified as follows:

while (true) {

while (count == 0)

; /* do nothing */

next consumed = buffer[out];

out = (out + 1) % BUFFER SIZE;

count--;

/* consume the item in next consumed */

5
Rules to be followed by processes whenever they are accessing their critical section

6
General Structure of a Process

The structure of a process handling the critical-section problem typically looks like this:

c
Copy code
while (true) {
// entry section: request permission to enter the critical section
critical_section();
// exit section: signal that the critical section is done
remainder_section();
}

Race Condition Example

The race condition occurs when two or more processes try to access and modify shared data
concurrently, leading to unpredictable results. The image you provided shows a race condition
scenario when assigning a process ID (PID) in an operating system.

Explanation of the Image

1. Processes P0 and P1 both want to create a child process using the fork() system call.
2. Each process requests the next available PID from the system.
3. The system returns the same PID (2615) to both processes because they both requested it
before the system could update the next_available_pid.

7
4. Both processes end up with child processes having the same PID (2615), which can cause
significant issues in process management.

This situation occurs because there is no mutual exclusion when accessing and updating the
next_available_pid. Both processes can read and modify this variable concurrently, leading to
the race condition.

Solutions to the Critical-Section Problem

In operating systems, there are two general approaches to handle critical sections:

1. Preemptive Kernels: Allow a process to be preempted while it is running in kernel


mode. These kernels are more responsive and suitable for real-time applications but are
harder to design because they need to ensure no race conditions in kernel data structures.
2. Nonpreemptive Kernels: Do not allow a process running in kernel mode to be
preempted. This simplifies the design and ensures no race conditions in kernel data
structures but can lead to lower system responsiveness.

Ensuring Mutual Exclusion

To prevent race conditions, the kernel can use various synchronization mechanisms such as:

• Locks: Ensure that only one process can access a critical section at a time.
• Semaphores: Control access to shared resources by using counters.
• Monitors: Encapsulate shared variables, the operations that can be performed on them, and the
synchronization necessary to ensure mutual exclusion.

Summary

The critical-section problem is a key issue in concurrent programming where processes need to
safely access shared resources. Solutions must ensure mutual exclusion, progress, and bounded
waiting. The example of the race condition in assigning PIDs illustrates the need for careful
synchronization in system-level programming to prevent data corruption and ensure system
stability.

8
9
What is a Semaphore?

A semaphore is a synchronization tool used to control access to a common resource by multiple


processes in a concurrent system such as an operating system. It is a variable that is used to
manage the concurrent access by keeping track of the number of available resources.

Types of Semaphores

There are two main types of semaphores:

1. Counting Semaphore:
o This type of semaphore can have any integer value.
o It is used to manage a resource that has a limited number of instances.
o For example, if there are 5 printers available, a counting semaphore can be initialized to
5. Each time a printer is used, the semaphore is decremented. When a printer is
released, the semaphore is incremented.
2. Binary Semaphore (or Mutex Lock):
o This type of semaphore can only have the value 0 or 1.
o It is used to ensure mutual exclusion, meaning only one process can access the resource
at a time.
o It behaves like a lock: 0 means the resource is locked (not available), and 1 means the
resource is unlocked (available).

10
Operations on Semaphores

There are two main operations you can perform on a semaphore:

1. wait() (also called P() operation):


o This operation decreases the value of the semaphore.
o If the semaphore’s value is greater than zero, the process can proceed and decrease the
semaphore’s value.
o If the semaphore’s value is zero or less, the process must wait until the semaphore’s
value is greater than zero.

c
Copy code
wait(S) {
while (S <= 0)
; // busy wait
S--;
}

2. signal() (also called V() operation):


o This operation increases the value of the semaphore.
o If there are processes waiting (the semaphore’s value is zero or negative), one of the
waiting processes is allowed to proceed.

c
Copy code
signal(S) {
S++;
}

How Semaphores Work

To understand how semaphores work, let's go through an example.

Example: Resource Allocation

Suppose we have 3 printers (a shared resource) and multiple processes that need to print
documents.

1. Initialization:
o Initialize a counting semaphore printerSemaphore to 3 (since there are 3 printers).

c
Copy code
semaphore printerSemaphore = 3;

2. Process Requests a Printer:


o When a process needs a printer, it performs a wait(printerSemaphore) operation.

11
o If printerSemaphore > 0, the process gets a printer and printerSemaphore is
decremented.
o If printerSemaphore == 0, the process waits (busy wait).

c
Copy code
wait(printerSemaphore);
// Process uses a printer

3. Process Releases a Printer:


o When the process is done printing, it performs a signal(printerSemaphore)
operation.
o This increments printerSemaphore, indicating that a printer is now available.

c
Copy code
// Process finishes using the printer
signal(printerSemaphore);

Solving Synchronization Problems

Semaphores can be used to solve various synchronization problems. One common problem is
ensuring the correct sequence of operations between processes.

Example: Process Synchronization

Suppose we have two processes, P1 and P2. We want to ensure that P2 runs a specific operation
only after P1 has completed its operation.

1. Initialize Semaphore:
o Initialize a binary semaphore synch to 0.

c
Copy code
semaphore synch = 0;

2. Process P1:
o P1 performs its operation and then signals the semaphore.

c
Copy code
P1 {
// Perform operation S1
signal(synch);
}

3. Process P2:
o P2 waits on the semaphore. It will only proceed with its operation when the semaphore
is signaled by P1.

12
c
Copy code
P2 {
wait(synch);
// Perform operation S2
}

Implementing Semaphores

In practice, semaphores are implemented using a combination of hardware and software to


ensure atomicity and avoid busy waiting.

1. Data Structure:
o A semaphore can be represented as a structure containing an integer value and a list of
waiting processes.

c
Copy code
typedef struct {
int value;
struct process *list;
} semaphore;

2. Wait Operation:
o Decrement the semaphore value.
o If the value is less than zero, add the process to the waiting list and suspend it.

c
Copy code
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
sleep();
}
}

3. Signal Operation:
o Increment the semaphore value.
o If the value is zero or negative, remove a process from the waiting list and wake it up.

c
Copy code
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
}

Atomicity and Efficiency


13
To ensure that semaphore operations are atomic (i.e., no two processes can modify the
semaphore simultaneously), the following methods can be used:

• Disabling Interrupts: In a single-processor system, disable interrupts while modifying the


semaphore.
• Spinlocks and Atomic Instructions: In multiprocessor systems, use special instructions or
spinlocks to ensure atomicity.

Conclusion

Semaphores are powerful tools for managing concurrent access to shared resources. They help in
solving synchronization problems by ensuring mutual exclusion and proper sequencing of
operations. Understanding how to use and implement semaphores is crucial for developing
reliable and efficient concurrent systems.

14
Understanding Monitors

Monitors are a high-level synchronization construct that provides a convenient mechanism for
process synchronization. They help prevent timing errors that can occur with the incorrect use of
lower-level synchronization primitives like semaphores or mutex locks.

Basic Concept of Monitors

A monitor is an abstract data type (ADT) that encapsulates:

1. Shared Data: Variables that are shared among multiple processes.


2. Operations: Functions that operate on the shared data.
3. Initialization Code: Code that initializes the shared data.

15
Only one process can be active within the monitor at a time, which ensures mutual exclusion.
This means that no two processes can be inside the monitor simultaneously.

Structure of a Monitor

A monitor consists of:

• Shared Data: The variables that represent the state of the monitor.
• Operations: The functions that can be executed within the monitor. These operations ensure
mutual exclusion.
• Initialization Code: Code that sets up the initial state of the shared data.

Example Syntax of a Monitor

Here’s a pseudocode example of a monitor:

pseudocode
Copy code
monitor MonitorName {
// Shared variable declarations
shared data;

// Initialization code
initialization {
// Initialize shared data
}

// Operations
function Operation1(...) {
...
}

function Operation2(...) {
...
}

...

function OperationN(...) {
...
}
}

Condition Variables

Monitors use condition variables for more complex synchronization. Condition variables enable
processes to wait within the monitor until a particular condition is true.

• Condition Variable Declaration: condition x, y;


• Operations on Condition Variables:
o x.wait(); - Suspends the process until another process invokes x.signal();.

16
o x.signal(); - Resumes exactly one suspended process. If no process is suspended, it
has no effect.

Figures Explanation

Figure 6.12: Schematic View of a Monitor

Error! Filename not specified.

This figure shows the basic structure of a monitor:

• Shared Data: The variables shared by all processes.


• Operations: The functions that can be called within the monitor.
• Initialization Code: The code that initializes the shared data.
• Entry Queue: The queue of processes waiting to enter the monitor.

Figure 6.13: Monitor with Condition Variables

Error! Filename not specified.

This figure adds condition variables to the monitor structure:

• Condition Variables: x and y, each with its own queue.


• Entry Queue: The queue of processes waiting to enter the monitor.
• Shared Data: The variables shared by all processes.
• Operations: The functions that can be called within the monitor.
• Initialization Code: The code that initializes the shared data.

Implementing Monitors Using Semaphores

Monitors can be implemented using semaphores. Each monitor uses a binary semaphore (mutex)
to ensure mutual exclusion.

• Mutex Semaphore: Ensures that only one process is active within the monitor.
• Condition Variables Implementation:
o Binary Semaphore for Each Condition: x_sem, initialized to 0.
o Counter for Each Condition: x_count, initialized to 0.

Process Resumption within a Monitor

When a process is resumed after waiting on a condition variable, it’s important to determine the
order of resumption:

• First-Come, First-Served (FCFS): The simplest method where the first process to wait is the first
to be resumed.

17
• Conditional Wait: Processes can wait with a priority number, and the process with the smallest
priority number is resumed first.

Example Monitor: Resource Allocator

Here’s an example of a monitor that allocates a single resource among competing processes:

pseudocode
Copy code
monitor ResourceAllocator {
boolean busy;
condition x;

void acquire(int time) {


if (busy)
x.wait(time);
busy = true;
}

void release() {
busy = false;
x.signal();
}

initialization {
busy = false;
}
}

In this example:

• busy: A shared variable indicating whether the resource is busy.


• x: A condition variable used to wait for the resource to become available.
• acquire(int time): Acquires the resource, waiting if it’s busy.
• release(): Releases the resource and signals the next waiting process.

Conclusion

Monitors provide a higher-level synchronization mechanism that simplifies process


synchronization and reduces the risk of timing errors. They encapsulate shared data and
operations, ensuring mutual exclusion and providing condition variables for more complex
synchronization needs.

18
Chp 5:

Introduction:
Single CPU Core and Process Execution

• Single CPU Core: In a computer with only one CPU core, only one process (task or
program) can be executed at a time.
• Waiting Processes: Any other processes that need to be run must wait until the CPU
becomes available.

Multiprogramming

• Goal: The main aim of multiprogramming is to keep the CPU busy by having some
process running at all times. This maximizes CPU utilization, meaning the CPU is not
sitting idle and wasting time.

How Multiprogramming Works

1. Execution Until Wait: A process runs on the CPU until it needs to wait for something,
usually an I/O operation (like reading data from a disk or waiting for user input).
2. Idle CPU in Simple Systems: In a simple system without multiprogramming, the CPU
would just sit idle during this wait time, doing nothing productive.
3. Productive Use of Time in Multiprogramming: Instead of letting the CPU sit idle,
multiprogramming keeps several processes in memory simultaneously.
4. Process Switch: When one process needs to wait, the operating system switches the CPU
to another process that is ready to run.
5. Continuous Pattern: This pattern continues, ensuring that whenever one process has to
wait, another process can use the CPU. This way, the CPU is always doing useful work.

Summary

Multiprogramming is a technique used to keep the CPU busy by switching between processes
whenever one process needs to wait, ensuring that the CPU is always working on something
productive. This maximizes CPU utilization and improves the efficiency of the system.

Let's break down the concepts shown in the images and the explanation about CPU scheduling
and burst durations.

Process Execution Cycle (Figure 5.1)


19
Key Points:

1. CPU Burst: This is a period during which a process is actively using the CPU for
computations.
o Example activities: load, store, add, read from file.
2. I/O Burst: This is a period during which a process is waiting for an I/O operation to
complete.
o Example activities: wait for I/O.
3. Alternation Between CPU and I/O Bursts: A process alternates between these bursts:
o A process starts with a CPU burst.
o When it needs to wait for I/O, it enters an I/O burst.
o Once the I/O operation is complete, it goes back to a CPU burst.
o This cycle continues until the process finishes execution.

CPU Burst Durations (Figure 5.2)


Key Points:

1. Histogram of CPU Burst Durations:


o The histogram shows the frequency (how often they occur) of different CPU burst
durations (how long they last).
2. Frequency Distribution:
o Most CPU bursts are short (high frequency of short bursts).
o There are fewer long CPU bursts (low frequency of long bursts).
o This distribution is often described as exponential or hyperexponential.

20
3. I/O-Bound vs. CPU-Bound Programs:
o I/O-Bound Programs: These programs perform a lot of I/O operations. They have many
short CPU bursts because they quickly go back to waiting for I/O.
o CPU-Bound Programs: These programs perform intensive computations with fewer I/O
operations. They have fewer but longer CPU bursts.

Importance in CPU Scheduling

Understanding the nature of CPU and I/O bursts helps in designing effective CPU scheduling
algorithms:

• For I/O-Bound Programs: Since they have many short CPU bursts, they often need to frequently
switch between CPU and I/O.
• For CPU-Bound Programs: Since they have longer CPU bursts, they benefit from fewer
interruptions to maximize their CPU usage.

Let's break down the concept of the CPU Scheduler in simpler terms:

What is a CPU Scheduler?


The CPU scheduler is a component of the operating system responsible for managing which
process runs on the CPU at any given time.

Key Points

1. Idle CPU and Ready Queue:


o Idle CPU: When the CPU is not executing any process (it is idle), it needs to find
a new process to run.
o Ready Queue: This is a list of processes that are ready to run but are currently
waiting for their turn to use the CPU.
2. Selection Process:
o The CPU scheduler selects a process from the ready queue to execute.

21
oThis process is chosen from those currently in memory and ready to execute.
3. Implementation of the Ready Queue:
o The ready queue is not necessarily organized in a first-in, first-out (FIFO) manner.
It can be implemented in various ways, depending on the scheduling algorithm
used.
o FIFO Queue: Processes are queued in the order they arrive.
o Priority Queue: Processes are queued based on their priority levels.
o Tree Structure: Processes are organized in a hierarchical structure.
o Unordered Linked List: Processes are stored in a list without any specific order.
4. Process Control Blocks (PCBs):
o The records in the ready queue are generally process control blocks (PCBs).
o PCB: This is a data structure used by the operating system to store information
about each process. It includes details like the process ID, state, priority, and other
relevant information.
o
o Summary

• The CPU Scheduler is responsible for selecting which process runs on the CPU when it
becomes idle.
• The Ready Queue contains processes that are ready to run. It can be organized in
different ways (FIFO, priority queue, tree, unordered list).
• The records in the ready queue are Process Control Blocks (PCBs), which store
information about each process.

By efficiently managing the ready queue and selecting processes, the CPU scheduler ensures that
the CPU is utilized effectively and processes are executed in an optimal order.

Dispatcher Role (Figure 5.3)


The dispatcher is responsible for switching the CPU from one process to another. Here’s how it
works:

1. P0 Executing: Process P0P_0P0 is currently running on the CPU.


2. Save State into PCB0: The dispatcher saves the current state of P0P_0P0 into its Process
Control Block (PCB). This state includes information such as the CPU registers, program
counter, etc.
3. Restore State from PCB1: The dispatcher restores the state of the next process P1P_1P1
from its PCB.
4. P1 Executing: Process P1P_1P1 starts executing on the CPU.
5. Dispatch Latency: The time it takes to stop one process and start another is called
dispatch latency.

Preemptive and Nonpreemptive Scheduling

22
CPU Scheduling Decisions

CPU-scheduling decisions happen under the following circumstances:

Process Switches to Waiting State:


Example: A process makes an I/O request or waits for a child process to terminate.
Process Switches to Ready State:
Example: An interrupt occurs.
Process Switches from Waiting to Ready State:
Example: Completion of an I/O operation.
Process Terminates:
The process has completed its execution.

Scheduling Types
Nonpreemptive (Cooperative) Scheduling:
Scheduling occurs only in situations 1 and 4.
Once the CPU is allocated to a process, it runs until it either terminates or switches to the waiting
state.
No process can be preempted to start another process.
Simpler to implement but not suitable for modern multitasking systems.
Preemptive Scheduling:
Scheduling can occur in all four situations, including 2 and 3.
The CPU can be taken away from a running process and allocated to another process.
Allows better responsiveness and utilization of the CPU.
Modern operating systems like Windows, macOS, Linux, and UNIX use preemptive scheduling.
Challenges with Preemptive Scheduling
Race Conditions:
Occur when processes share data.
If one process is preempted while updating shared data, another process might read inconsistent
data.
Requires mechanisms like mutex locks to prevent data corruption.

Kernel Design:
Nonpreemptive Kernel:
Waits for system calls to complete or processes to block before switching.
Ensures kernel data structures remain consistent.
Not ideal for real-time systems where tasks have strict timing requirements.
Preemptive Kernel:
Can preempt processes even during kernel operations.
Needs to handle synchronization to maintain data consistency.
Better suited for real-time and multitasking environments.
Summary
Dispatcher: Manages the process of switching the CPU from one process to another, involving
saving and restoring process states.

Nonpreemptive Scheduling: Simpler but less responsive, processes run until they finish or wait.

23
Preemptive Scheduling: More complex but allows better CPU utilization and responsiveness by
preempting processes when necessary.

Challenges: Includes handling race conditions and ensuring kernel data consistency.

By understanding these concepts, you can see how modern operating systems manage
multitasking efficiently while addressing potential issues with shared data and kernel operations.

Dispatcher
The dispatcher is a critical component in the CPU scheduling function. It is responsible for
giving control of the CPU core to the process selected by the CPU scheduler.

Functions of the Dispatcher

The dispatcher performs the following functions:

1. Switching Context:
o The dispatcher saves the state of the currently running process and loads the state
of the next process to be executed.
o This process involves saving and restoring information such as the program
counter, CPU registers, and memory management information.
2. Switching to User Mode:
o After the context switch, the dispatcher switches the CPU to user mode from
kernel mode.
o User mode is a restricted mode of operation designed to protect the system from
malicious or erroneous user programs.

24
3. Jumping to the Proper Location in the User Program:
o The dispatcher sets the program counter to the appropriate location in the user
program to resume execution.

Importance of Dispatcher Speed

• The dispatcher should perform its tasks as quickly as possible because it is invoked
during every context switch.
• The time it takes for the dispatcher to stop one process and start another is known as
dispatch latency.
• Dispatch Latency: This is the time overhead involved in switching from one process to
another. Reducing dispatch latency is important to minimize the overhead and ensure
efficient CPU utilization.

Context Switch Frequency

• Context switches occur whenever the CPU is allocated from one process to another.
• Voluntary Context Switch: Occurs when a process voluntarily gives up the CPU, for
example, when it performs an I/O operation.
• Nonvoluntary Context Switch: Occurs when the CPU is taken away from a process,
such as when its timeslice expires or it is preempted by a higher-priority process.

Monitoring Context Switches

• On Linux systems, the number of context switches can be monitored using the vmstat
command.
• Example Command: vmstat 1 3
o This command provides three lines of output over a 1-second delay.
o The first line gives the average number of context switches per second since the
system booted.
o The next two lines give the number of context switches over two 1-second
intervals.
o The output may look something like this:

css
Copy code
------cpu----
24
225
339

o These numbers indicate the frequency of context switches, helping to understand


system performance and the overhead caused by context switching.

Summary

25
• Dispatcher: The module responsible for handing over control of the CPU core to the
selected process, performing context switches, switching to user mode, and jumping to
the correct location in the user program.
• Dispatch Latency: The time taken to perform a context switch, which should be
minimized for efficient CPU utilization.
• Context Switch Frequency: Indicates how often context switches occur, which can be
monitored using tools like vmstat on Linux systems.

Understanding the dispatcher's role and how context switches work is crucial for optimizing
CPU scheduling and improving overall system performance.

Scheduling Criteria
1. CPU Utilization:
o Definition: The percentage of time the CPU is actively working (not idle).
o Objective: To keep the CPU as busy as possible.
o Range: Conceptually from 0% to 100%. In real systems, it ranges from 40%
(lightly loaded) to 90% (heavily loaded).
o Measurement Tool: Can be measured using the top command on Linux, macOS,
and UNIX systems.
2. Throughput:
o Definition: The number of processes completed per time unit.
o Objective: To maximize the number of processes completed in a given period.
o Variation: Long processes may result in lower throughput (e.g., one process
every few seconds), while short processes may result in higher throughput (e.g.,
tens of processes per second).
3. Turnaround Time:
o Definition: The total time taken for a process to complete from submission to
completion.
o Components: Sum of the periods spent waiting in the ready queue, executing on
the CPU, and performing I/O operations.
o Objective: To minimize the time taken to execute a process completely.
4. Waiting Time:
o Definition: The total time a process spends waiting in the ready queue.
o Objective: To minimize the time processes spend waiting to be executed.
o Note: Scheduling algorithms directly affect waiting time but do not affect the
actual time a process spends executing or performing I/O.
5. Response Time:
o Definition: The time from the submission of a request until the first response is
produced.
o Objective: To minimize the time it takes to start responding to a request.

26
o Relevance: Particularly important in interactive systems where immediate
feedback is crucial.
o Difference from Turnaround Time: Response time focuses on the time to start
producing output, whereas turnaround time focuses on the total time to complete
the process.

Objectives for Optimization

• Maximize:
o CPU Utilization: Keep the CPU busy as much as possible.
o Throughput: Increase the number of processes completed in a given time frame.
• Minimize:
o Turnaround Time: Reduce the total time taken for processes to complete.
o Waiting Time: Reduce the time processes spend waiting in the ready queue.
o Response Time: Decrease the time to start responding to requests.

Summary

When evaluating CPU scheduling algorithms, the goal is to choose one that maximizes CPU
utilization and throughput while minimizing turnaround time, waiting time, and response time.
The choice of algorithm can significantly impact the performance of a system, especially
depending on whether it is more CPU-bound or I/O-bound, and whether it is an interactive or
batch processing system.

Multilevel Queue Scheduling


Multilevel Queue Scheduling is a sophisticated CPU scheduling algorithm that organizes
processes into several distinct queues, each potentially using a different scheduling algorithm.
Here's a breakdown of how it works and its key concepts:

Basic Concept

• Multiple Queues: Processes are divided into different queues based on specific criteria like
process type or priority.
• Static Assignment: Once a process is assigned to a queue, it remains in that queue for its entire
runtime.
• Priority Levels: Each queue has a priority level, and processes in higher-priority queues are
scheduled before those in lower-priority queues.

Example Structure

Consider a system with four queues, each representing a different type of process:

1. Real-time processes: Highest priority

27
2. System processes
3. Interactive processes
4. Batch processes: Lowest priority

Scheduling within Queues

• Each queue can have its own scheduling algorithm. For example:
o Real-time queue: May use a fixed-priority preemptive scheduling algorithm.
o Interactive queue: May use a Round-Robin (RR) algorithm to ensure fair CPU time
allocation among interactive processes.
o Batch queue: May use a First-Come, First-Served (FCFS) algorithm since batch processes
can typically tolerate longer waits.

Scheduling among Queues

• Fixed-Priority Preemptive Scheduling: Queues are scheduled based on their priority.


For instance:
o Real-time processes have absolute priority over system, interactive, and batch
processes.
o If a real-time process arrives while a batch process is running, the batch process is
\preempted, and the real-time process is scheduled.
• Time-Slicing among Queues: CPU time is divided among the queues. For example:
o Foreground (interactive) processes might get 80% of the CPU time.
o Background (batch) processes might get 20% of the CPU time.
o Within each slice, the respective queue's scheduling algorithm determines which
process runs.

Example Scenario

• High-Priority Preemption: If an interactive process enters the ready queue while a batch
process is running, the batch process is immediately preempted to allow the interactive process
to run.
• Time-Slice Allocation: The foreground queue (interactive processes) receives 80% of the CPU
time, scheduled using RR. The background queue (batch processes) receives 20% of the CPU
time, scheduled using FCFS.

Advantages and Use Cases

• Customizable Scheduling: Allows different scheduling algorithms to be used for different types
of processes, optimizing for their specific needs.
• Priority Handling: Ensures high-priority processes (like real-time processes) get immediate CPU
access,. improving response times for critical tasks.
• Fair Allocation: Time-slicing ensures that lower-priority processes also get a share of the CPU,
preventing starvation.

Summary

28
Multilevel Queue Scheduling is a versatile and efficient method for managing diverse types of
processes in a system. By organizing processes into multiple queues with distinct scheduling
algorithms and priority levels, it balances the need for quick response times in interactive
applications with the efficient processing of batch jobs.

Multilevel Feedback Queue Scheduling


Multilevel Feedback Queue (MLFQ) scheduling is an advanced and flexible CPU scheduling
algorithm designed to handle processes with varying characteristics efficiently. Unlike the static
nature of traditional multilevel queue scheduling, MLFQ allows processes to move between
queues based on their behavior and CPU burst characteristics. Here's a detailed explanation of
MLFQ:

Key Features

1. Dynamic Queue Assignment: Processes can move between different priority queues
based on their CPU usage and waiting time.
2. Priority Adjustment: Processes that use too much CPU time are demoted to lower-
priority queues, while those that wait too long in lower-priority queues can be promoted
to higher-priority queues to prevent starvation.

Example Structure

Consider a MLFQ scheduler with three queues, numbered from 0 to 2:

• Queue 0: Highest priority, short time quantum (e.g., 8 milliseconds).


• Queue 1: Medium priority, longer time quantum (e.g., 16 milliseconds).
• Queue 2: Lowest priority, scheduled using First-Come, First-Served (FCFS).

Process Movement

1. Initial Placement: A new process is placed in the highest-priority queue (Queue 0).

29
2. Execution and Demotion:
o A process in Queue 0 is given a time quantum of 8 milliseconds.
o If it does not complete within this time, it is moved to the tail of Queue 1.
o Queue 1 processes are given a time quantum of 16 milliseconds.
o If a process in Queue 1 does not complete within this time, it is moved to Queue
2.
o Processes in Queue 2 are executed using FCFS but only when Queues 0 and 1 are
empty.
3. Promotion (Aging): A process waiting too long in a lower-priority queue may be
gradually moved to a higher-priority queue to prevent starvation.

Scheduling within and among Queues

• Priority Order Execution: The scheduler first executes processes in the highest-priority
queue (Queue 0). Only when Queue 0 is empty does it execute processes in Queue 1, and
so on.
• Preemption: A process in a higher-priority queue can preempt a running process in a
lower-priority queue. For instance, a process arriving in Queue 0 can preempt a process
running in Queue 1 or Queue 2.

Parameters for MLFQ

A multilevel feedback queue scheduler is defined by the following parameters:

1. Number of Queues: How many distinct priority levels (queues) exist.


2. Scheduling Algorithm for Each Queue: The specific algorithm (e.g., Round-Robin,
FCFS) used within each queue.
3. Upgrade Method: The criteria for promoting a process to a higher-priority queue.
4. Demote Method: The criteria for demoting a process to a lower-priority queue.
5. Entry Queue Method: The criteria for determining which queue a new or returning
process enters.

Example Scenario

Imagine a process that requires more CPU time than allocated by its current queue:

• Start in Queue 0: It runs for 8 milliseconds but doesn't finish, so it's moved to Queue 1.
• Move to Queue 1: It runs for 16 milliseconds but still doesn't finish, so it's moved to
Queue 2.
• Execution in Queue 2: Now it runs using FCFS but only when Queues 0 and 1 are
empty.
• Prevention of Starvation: If it waits too long in Queue 2, it may be moved back up to
Queue 1 to ensure it gets CPU time eventually.

Advantages and Complexity

30
• Flexibility: MLFQ can be tailored to match the specific needs of different systems by
adjusting its parameters.
• Efficiency: It optimizes CPU utilization by adjusting process priorities based on their
behavior.
• Prevention of Starvation: The aging mechanism ensures that processes are not
indefinitely deprived of CPU time.

However, the complexity of MLFQ lies in defining the optimal values for its parameters, which
requires a thorough understanding of the system's workload and behavior.

In summary, Multilevel Feedback Queue Scheduling is a powerful and adaptive CPU scheduling
algorithm that balances the need for responsive interactive processes and efficient batch
processing, making it suitable for a wide range of computing environments.

Multi-Processor Scheduling
Overview

• Multi-processor scheduling addresses the challenges of scheduling CPUs in systems with


multiple processing cores.
• Load sharing enables multiple threads to run in parallel, making scheduling more complex.
• No single best solution exists for multi-processor scheduling, similar to single-core CPU
scheduling.

Evolution of Multiprocessor Systems

• Traditional Definition: Multiple physical processors, each with a single-core CPU.


• Modern Definition: Includes various architectures:

31
o Multicore CPUs: Multiple cores on a single chip.
o Multithreaded Cores: Cores capable of running multiple threads simultaneously.
o NUMA (Non-Uniform Memory Access) Systems: Memory access time depends on
memory location relative to the processor.
o Heterogeneous Multiprocessing: Cores with varying clock speeds and power
management capabilities.

By understanding these key points, you can grasp the essentials of multi-processor scheduling,
its evolution, and the different system architectures involved.

Approaches to Multiple-Processor Scheduling

Asymmetric Multiprocessing (AMP)

• Description: All scheduling decisions, I/O processing, and system activities are managed by a
single processor (the master server). Other processors execute only user code.
• Advantages: Simplifies data structure access since only one core is involved.
• Disadvantages: The master server can become a bottleneck, reducing overall system
performance.

Symmetric Multiprocessing (SMP)

• Description: Each processor is self-scheduling, meaning every processor can handle its own
scheduling.
• Scheduling Strategies:
1. Common Ready Queue: All threads are in a single queue, and any processor can pick
the next thread to run.
2. Per-Core Private Queue: Each processor has its own queue of threads.

Load Balancing in SMP

• Balancing algorithms are used to distribute workloads evenly among processors.


• Necessary to prevent some processors from being overburdened while others are idle.

Operating System Support

• Modern operating systems like Windows, Linux, macOS, Android, and iOS support SMP, making
it the standard approach for multiprocessor systems.

Understanding these approaches helps manage CPU resources effectively in multiprocessor


systems, ensuring balanced workloads and efficient system performance.

32
Multicore Processors

Traditionally, symmetric multiprocessing (SMP) systems allowed multiple processes to run in


parallel by using several physical processors. Modern computer hardware, however, places
multiple computing cores on a single physical chip, resulting in multicore processors. Each core
maintains its architectural state and appears to the operating system as a separate logical CPU.
Multicore processors are faster and consume less power than systems where each CPU has its
own physical chip.

Memory Stall

A memory stall occurs when a processor spends significant time waiting for data to become
available from memory, often because modern processors operate much faster than memory.
This can also happen due to a cache miss. As illustrated in Figure 5.12, processors can spend up
to 50% of their time waiting for data from memory.

33
Chip Multithreading (CMT)

To address memory stalls, recent hardware designs have implemented multithreaded processing
cores where multiple hardware threads are assigned to each core. If one hardware thread stalls,
the core can switch to another thread. This is known as chip multithreading (CMT).

• Example: Intel's hyper-threading (also known as simultaneous multithreading or SMT)


allows multiple hardware threads per core. For instance, contemporary Intel processors
like the i7 support two threads per core, while the Oracle Sparc M7 processor supports
eight threads per core.
• Operating System View: Each hardware thread appears as a logical CPU to the
operating system, allowing it to manage multiple threads more effectively. As illustrated
in Figure 5.14, a processor with four cores and multiple hardware threads appears to the
OS as multiple logical CPUs.

34
Scheduling with Multicore Processors

Scheduling becomes more complex with multicore processors, especially when considering
resource sharing:

• Core Sharing: If two software threads run on the same core, they share processor resources and
may proceed more slowly compared to running on separate cores.
• Resource Awareness: The operating system scheduler can make more effective decisions if it is
aware of resource sharing levels. For instance, it can schedule software threads onto logical
processors that do not share resources, optimizing performance.

Load Balancing

In SMP systems, load balancing ensures that the workload is evenly distributed across all
processors. Without load balancing, some processors might sit idle while others have high
workloads and long ready queues. Load balancing is mainly necessary for systems where each
processor has its own private ready queue. In systems with a common run queue, load balancing
is automatic as idle processors can take tasks from the common queue.

35
Approaches to Load Balancing

1. Push Migration: A dedicated task periodically checks the load on each processor. If an
imbalance is detected, it redistributes threads from overloaded processors to idle or less-busy
processors.
2. Pull Migration: When a processor becomes idle, it pulls a waiting task from a busy processor to
balance th e workload.

Processor Affinity

Processor affinity refers to the tendency to keep a process running on the same processor to take
advantage of cache memory.

• Soft Affinity: The operating system tries to keep a process on the same processor but does not
guarantee it. This approach reduces the overhead of cache invalidation and repopulation.
• Hard Affinity: Allows a process to specify a subset of processors on which it can run, providing
more control over processor assignment.

Example in Operating Systems

• Linux: Implements soft affinity but also supports hard affinity through the
sched_setaffinity() system call, which allows a thread to specify the set of CPUs it can run
on.

By understanding load balancing and processor affinity, systems can optimize performance and
efficiency, ensuring that processors are utilized effectively and processes benefit from improved
cache performance.

Heterogeneous Multiprocessing (HMP)

In Heterogeneous Multiprocessing (HMP), systems are designed with cores that, while running
the same instruction set, differ in clock speed and power management capabilities. This includes
the ability to adjust the power consumption of a core, even to the point of idling it.

Key Points:

• Core Variability: Cores can vary in clock speed and power management features.
• Power Management: Ability to dynamically adjust power consumption of each core.
• Idle Cores: Can reduce power consumption to the point of idling a core when not in use.

Example Use Case:

• Mobile Systems: HMP is commonly used in mobile systems to balance performance and power
efficiency, enhancing battery life while maintaining adequate performance.

36
Understanding HMP allows for better optimization of resources, providing a balance between
performance and energy efficiency in modern computing systems.

Real-Time CPU Scheduling


Real-time CPU scheduling is critical for systems that need to respond to events within a strict
time frame. There are two types of real-time systems:

• Soft Real-Time Systems: These systems give preference to critical processes over non-critical
ones but do not guarantee when the process will be scheduled.
• Hard Real-Time Systems: These systems have strict deadlines. A task must be serviced by its
deadline; otherwise, it is considered as no service at all.

Minimizing Latency

Interrupt Latency

• Definition: The time from when an interrupt arrives at the CPU to the start of the interrupt
service routine (ISR).
• Process: Upon an interrupt, the operating system must:
1. Complete the current instruction.
2. Determine the type of interrupt.
3. Save the current process state.
4. Service the interrupt using the ISR.

Dispatch Latency

• Definition: The time required for the scheduler to stop one process and start another.
• Importance: Real-time operating systems aim to minimize dispatch latency to ensure timely task
execution.
• Technique: The most effective way to reduce dispatch latency is by using preemptive kernels.

Components of Dispatch Latency (Figure 5.19)

1. Preemption of Kernel Processes: Stopping any process running in the kernel.


2. Resource Release: Low-priority processes must release resources needed by high-priority
processes.

In hard real-time systems, dispatch latency is usually measured in microseconds to meet strict
timing requirements. By minimizing both interrupt and dispatch latency, real-time operating
systems can provide immediate CPU access to critical tasks, ensuring timely and reliable
execution.

37
Priority-Based Scheduling in Real-Time Systems

Key Features

• Immediate Response: The scheduler must respond immediately to a real-time process when it
requires the CPU.
• Priority-Based Algorithm: Real-time operating systems use a priority-based scheduling
algorithm.

Preemption

• Support for Preemption: If a higher-priority process becomes available, it preempts the


currently running process.

Process Characteristics

• Periodic Processes: Processes require the CPU at constant intervals.


o Processing Time (t): The time needed by the CPU.
o Deadline (d): The latest time by which the process must be completed.
o Period (p): The interval at which the process repeats.
• Relationship: 0≤t≤d≤p0 \leq t \leq d \leq p0≤t≤d≤p
• Rate of Periodic Task: 1/p1/p1/p

Understanding priority-based scheduling in real-time systems involves grasping how the


scheduler prioritizes and preempts processes to ensure timely execution of critical tasks.

38
5.6.3 Rate-Monotonic Scheduling (RMS)

• Static Priority Policy: Periodic tasks are scheduled using fixed priorities with preemption.
• Priority Assignment:
o Inversely based on period length.
o Shorter periods = Higher priority.
o Longer periods = Lower priority.
• Assumptions:
o The processing time for each CPU burst is constant.
• Example:
o Two processes, P1 and P2, with periods 50 and 100 respectively.
o Processing times: t1 = 20 for P1, t2 = 35 for P2.
o CPU utilization: P1 (20/50 = 0.40), P2 (35/100 = 0.35).
o Total CPU utilization = 75%.

5.6.4 Earliest-Deadline-First Scheduling (EDF)

• Dynamic Priority Policy:


o Priorities are assigned based on deadlines.
o Earlier deadlines = Higher priority.
o Later deadlines = Lower priority.
• Operation:
o Processes announce deadlines upon becoming runnable.
o Priorities are adjusted dynamically.

5.6.5 Proportional Share Scheduling

• Allocation of Shares:

39
o Total shares (T) are divided among applications.
o Each application receives N shares of time, ensuring it gets N/T of total processor time.
• Example:
o T = 100 shares.
o Process A: 50 shares (50% of CPU time).
o Process B: 15 shares (15% of CPU time).
o Process C: 20 shares (20% of CPU time).

5.6.6 POSIX Real-Time Scheduling

• Standard Extensions: POSIX provides real-time scheduling extensions.


• Scheduling Policies:
o SCHED_FIFO: First-Come, First-Served policy.
o SCHED_RR: Round-Robin policy.

chp 7:

40
41
42
43
Reader–writer locks are most useful in the following situations:
• In applications where it is easy to identify which processes only read shared data and which
processes only write shared data.
• In applications that have more readers than writers. This is because reader–writer locks generally
require more overhead to establish than semaphores or mutual-exclusion locks. The increased
concurrency of allowing multiple readers compensates for the overhead involved in setting up the
reader–writer lock.

7.1.3 The Dining-Philosophers Problem

Problem Description

• Scenario: Five philosophers sit around a circular table, alternating between thinking and eating.
• Resources: There is a bowl of rice and five chopsticks.
• Behavior:
o Philosophers think and do not interact with others while thinking.
o When hungry, a philosopher picks up the two chopsticks closest to them (one on the left
and one on the right).

44
o A philosopher can only pick up one chopstick at a time and cannot pick up a chopstick
already in use by a neighbor.
o Once both chopsticks are picked up, the philosopher eats. After eating, both chopsticks
are put down, and the philosopher resumes thinking.

Synchronization Problem

• Goal: Allocate resources (chopsticks) among philosophers without deadlock and starvation.

7.1.3.1 Semaphore Solution

Semaphore-Based Approach

• Chopsticks as Semaphores: Each chopstick is represented by a semaphore.


• Operations:
o wait(): A philosopher tries to grab a chopstick.
o signal(): A philosopher releases a chopstick.
• Initialization:

semaphore chopstick[5];
// All elements of chopstick are initialized to 1
Deadlock Issue

• Deadlock Example: If all philosophers grab their left chopstick simultaneously, all will wait
indefinitely for the right chopstick.

Remedies for Deadlock

1. Allow at most four philosophers at the table simultaneously.


2. Ensure a philosopher picks up chopsticks only if both are available (done in a critical section).
3. Use an asymmetric approach:
o Odd-numbered philosophers pick up the left chopstick first, then the right.
o Even-numbered philosophers pick up the right chopstick first, then the left.

7.1.3.2 Monitor Solution

Monitor-Based Approach

• States: Philosophers can be in one of three states: THINKING, HUNGRY, or EATING.


• State Declaration:

enum {THINKING, HUNGRY, EATING} state[5];


Conditions and Operations

• Condition Variable:

45
condition self[5];

• Rules: A philosopher can set state[i] = EATING only if neither of their neighbors is eating:

(state[(i+4) % 5] != EATING) && (state[(i+1) % 5] != EATING)


Functions

• Pickup and Putdown Operations:

DiningPhilosophers.pickup(i);
...
eat
...
DiningPhilosophers.putdown(i);

• Monitor Solution: This ensures a philosopher picks up both chopsticks only if they are
both available, preventing deadlock and ensuring fair resource allocation.

CHP 8:
In a multiprogramming environment, several threads may compete for a finite number of
resources. A thread requests resources; if the resources are not available at that time, the thread
enters a waiting state. Sometimes, a waiting thread can never again change state, because the
resources it has requested are held by other waiting threads. This situation is called a deadlock.

System Model in Multiprogramming

1.Overview
• System Resources: A system has a limited number of resources shared among multiple threads.
• Resource Types: Resources are grouped into types (or classes), each with identical instances.
Examples include CPU cycles, files, and I/O devices like network interfaces and DVD drives.

2.Resource Allocation
• Instance Example: If there are four CPUs, the CPU resource type has four instances.
• Request Handling: Any instance of a resource type should fulfill a thread's request. If not, the
resource types are not correctly defined.

3.Thread Resource Usage


• Request Before Use: A thread must request a resource before using it and release it after use.
• Request Limit: The total number of requested resources cannot exceed the available resources.

4.Sequence of Resource Usage


1. Request: The thread asks for a resource. If unavailable (e.g., a mutex lock held by another thread),
the thread waits.

46
2. Use: The thread uses the resource (e.g., accessing a critical section if the resource is a mutex lock).
3. Release: The thread releases the resource.

5.System Monitoring
• Resource Tracking: The OS checks and records resource allocation in a system table, noting
whether a resource is free or allocated and to which thread.

6. Deadlock Situation
• Definition: A deadlock occurs when every thread in a set waits for an event that only another
thread in the set can trigger.
• Example with Philosophers: If all philosophers (threads) grab the left chopstick (resource)
simultaneously, none can get the right chopstick, causing a deadlock as each waits for the right
chopstick to become available.

8.3.2 Resource-Allocation Graph

A Resource-Allocation Graph is a directed graph used to represent the allocation of resources


in a system. It helps in understanding deadlocks.

Components:

• Vertices (V): Two types of nodes


o T = {T1, T2, ..., Tn}: Active threads
o R = {R1, R2, ..., Rm}: Resource types
• Edges (E): Two types
o Request edge (Ti → Rj): Thread Ti requests an instance of resource type Rj.
o Assignment edge (Rj → Ti): An instance of resource type Rj has been allocated to thread
Ti.

Graph Representation:

• Threads (Ti): Represented as circles


• Resource Types (Rj): Represented as rectangles

47
Example from Figure 8.4:

• Vertices: T = {T1, T2, T3}, R = {R1, R2, R3, R4}


• Edges: E = {T1 → R1, T2 → R3, R1 → T2, R2 → T2, R2 → T1, R3 → T3}
• Resource Instances:
o R1: One instance
o R2: Two instances
o R3: One instance
o R4: Three instances
• Thread States:
o T1: Holding R2, waiting for R1
o T2: Holding R1 and R2, waiting for R3
o T3: Holding R3

Deadlock and Cycles:

• No Cycle: No deadlock.
• Cycle Present:
o If each resource type has one instance: Cycle implies deadlock.
o If resource types have multiple instances: Cycle does not always mean deadlock.

Example from Figure 8.5:

• T3 requests R2 (adding edge T3 → R2).


• Two cycles:
o T1 → R1 → T2 → R3 → T3 → R2 → T1
o T2 → R3 → T3 → R2 → T2
• Threads T1, T2, and T3 are deadlocked.
o T2 waits for R3 (held by T3)
o T3 waits for R2 (held by T1 or T2)
o T1 waits for R1 (held by T2)

48
Figure 8.6: Resource-Allocation Graph with a Cycle but No Deadlock

• Vertices:
o Threads: T1,T2,T3,T4
o Resources: R1,R2
• Edges:
o T1→R1: Thread T1 requests resource R1
o T1→R2: Thread T1 requests resource R2
o T2→R1: Thread T2 requests resource R1
o T3→R1: Thread T3 requests resource R1
o T3→R2: Thread T3requests resource R2
o T4→R2: Thread T4 requests resource R2
• Cycle:
o T1→R1→T3→R2→T1
• No Deadlock Explanation:
o Even though there is a cycle, there are enough instances of resources R1 and R2
to satisfy the requests. Thus, no thread is blocked indefinitely.

Summary:

• No Cycle: System is not deadlocked.


• Cycle: System may or may not be deadlocked.

8.4 Methods for Handling Deadlocks


There are three main ways to handle deadlocks in a system:

1. Ignore the Problem:


o Pretend that deadlocks never happen.
o This is the approach taken by most operating systems like Linux and Windows.
o Developers are responsible for writing programs that avoid or handle deadlocks.
2. Prevent or Avoid Deadlocks:

49
o Deadlock Prevention: Use methods to ensure that at least one of the necessary
conditions for deadlock (like mutual exclusion, hold and wait, no preemption, or
circular wait) cannot hold. This is discussed in more detail in Section 8.5.
o Deadlock Avoidance: The system requires extra information in advance about which
resources a thread will request and use during its lifetime. With this information, the
system can decide whether to grant or delay each request to avoid deadlock.
3. Detect and Recover from Deadlocks:
o Allow the system to enter a deadlocked state, detect it, and then recover.
o This approach is used by some systems, such as databases.
o If there are no algorithms to detect and recover from deadlocks, the system may enter
a state where performance deteriorates as resources are held by threads that cannot
proceed, leading to more threads becoming deadlocked. Eventually, the system may
stop functioning and need a manual restart.

By using these methods, systems can manage and mitigate the effects of deadlocks to ensure
smooth operation.

8.5 Deadlock Prevention


To prevent deadlocks, we need to ensure that at least one of the four necessary conditions for a
deadlock does not hold. Let's look at each condition:

8.5.1 Mutual Exclusion

• Condition: At least one resource must be non-sharable.


• Prevention: Use sharable resources when possible. For example, multiple threads can read the
same read-only file simultaneously without causing a deadlock.
• Limitations: Some resources, like mutex locks, cannot be shared and must be used exclusively
by one thread at a time.

8.5.2 Hold and Wait

• Condition: A thread holds at least one resource and waits to acquire additional resources.
• Prevention:
o Protocol 1: Require threads to request and be allocated all their resources before starting
execution.
o Protocol 2: Allow threads to request resources only when they hold no other resources.
• Limitations:
o Resource utilization may be low because resources might be held for a long time without
being used.
o Starvation can occur if a thread needs several popular resources and has to wait
indefinitely.

50
8.5.3 No Preemption

• Condition: Resources cannot be forcibly taken away from a thread once they have been
allocated.
• Prevention:
o If a thread holding resources requests another resource and must wait, preempt all its
currently held resources.
o Allocate resources only if they are available or can be preempted from other waiting
threads.
• Limitations:
o This protocol works well with resources that can be easily saved and restored, like CPU
registers.
o It is less effective for resources like mutex locks and semaphores, where deadlocks are
more common.

8.5.4 Circular Wait

• Condition: A circular chain of threads exists, where each thread holds at least one resource
needed by the next thread in the chain.
• Prevention:
o Assign a unique number to each resource.
o Ensure that threads request resources in an increasing order of these numbers.
o For example, if a thread wants to use both the first and second mutex, it must request the
first mutex before the second mutex.
o Alternatively, a thread can request resources only if it does not hold any resources with a
lower or equal number.
• Result: These protocols prevent the circular-wait condition, thereby avoiding deadlocks.

By using these methods, we can prevent deadlocks and ensure the system runs smoothly.

8.6 Deadlock Avoidance


To avoid deadlocks, we can use additional information about resource requests. Here's how it
works:

• Basic Idea: The system knows in advance the order in which threads will request and release
resources.
• Example: If the system knows that Thread P will request Resource R1 first and then R2, while
Thread Q will request R2 first and then R1, it can make decisions to avoid deadlocks.
• Decision Making: The system decides whether a thread should wait based on current and future
resource requests and allocations.

8.6.1 Safe State

• Definition: A state is safe if the system can allocate resources to each thread in some
order without causing a deadlock.

51
• Safe Sequence: A sequence of threads is safe if each thread can get the resources it
needs, either immediately or after some other threads have finished and released their
resources.
• Example:

o Suppose there are 12 resources and three threads: T0, T1, and T2.
o T0 may need 10 resources, T1 may need 4, and T2 may need 9.
o At a certain time, T0 holds 5 resources, T1 holds 2, and T2 holds 2, leaving 3 free
resources.
o The sequence <T1, T0, T2> is safe because:
▪ T1 can get all its needed resources and then release them.
▪ T0 can then get all its needed resources and release them.
▪ Finally, T2 can get all its needed resources.
• Transition from Safe to Unsafe:
o If T2 requests one more resource and is granted, the system might enter an unsafe
state.
o In this unsafe state, only T1 can be allocated all its resources. If T0 or T2 then request
more resources, it could lead to a deadlock.
• Prevention:
o Ensure the system remains in a safe state by only granting resource requests that keep
the state safe.
o This may cause some threads to wait, reducing resource utilization but avoiding
deadlocks.

8.6.2 Resource-Allocation-Graph Algorithm

• Graph Concept:
o Use a resource-allocation graph to represent resource requests and assignments.
o Introduce "claim edges" (dashed lines) to show potential future resource requests.
• Edge Types:
o Claim Edge: Indicates a thread may request a resource in the future (dashed line).
o Request Edge: Shows a thread is currently requesting a resource.
o Assignment Edge: Shows a resource is currently allocated to a thread.
• Safety Check:
o When a thread requests a resource, convert the claim edge to a request edge.
o Check for cycles in the graph using a cycle-detection algorithm.
o If converting the edge results in no cycles, the request is granted, keeping the state safe.
o If a cycle is detected, the request is denied, and the thread must wait.
• Cycle Detection:
o Detecting a cycle in the graph involves checking for paths and requires operations
proportional to the number of threads squared (n²).

52
By using these methods, we can ensure the system remains in a state where deadlocks are
avoided

Deadlock Detection:
In systems that do not use deadlock prevention or avoidance techniques, deadlocks can occur. To
handle deadlocks, the system can:

1. Use an algorithm to check if a deadlock has happened.


2. Use an algorithm to recover from a deadlock.

There are different approaches depending on whether resources have a single instance or
multiple instances.

Single Instance of Each Resource Type

For resources with only one instance, a special graph called a wait-for graph is used.

• Wait-for Graph: Derived from the resource-allocation graph by removing resource nodes. If T1
is waiting for a resource held by T2, it will show as an edge from T1 to T2.
• Deadlock Detection: If the wait-for graph has a cycle, a deadlock exists. The system periodically
checks for cycles in this graph.

The algorithm to find a cycle in a graph runs in O(n2) time, where n is the number of vertices.

For example, tools like the BCC toolkit can detect potential deadlocks by tracing calls to mutex
locks and constructing wait-for graphs.

Several Instances of a Resource Type

For resources with multiple instances, a different approach is used, similar to the banker's
algorithm.

53
• Data Structures:
o Available: Vector showing available resources.
o Allocation: Matrix showing currently allocated resources to each thread.
o Request: Matrix showing current requests of each thread.
• Detection Algorithm:

1. Initialize Work (available resources) and Finish (threads that can finish).
2. Find a thread that can get all its requested resources (Request[i] <= Work).
3. If found, allocate resources to this thread (Work = Work + Allocation[i]) and mark it as
finished.
4. Repeat until no more threads can be satisfied. If any thread is left unfinished, a deadlock exists.

The algorithm runs in O(m×n2) time, where m is the number of resource types and n is the
number of threads.

Example: Suppose we have 5 threads and 3 resource types with the following state:

• Resources:
o A has 7 instances.
o B has 2 instances.
o C has 6 instances.
• Snapshot:
o Allocation:
▪ T0: (0, 1, 0)
▪ T1: (2, 0, 0)
▪ T2: (3, 0, 3)
▪ T3: (2, 1, 1)
▪ T4: (0, 0, 2)
o Request:
▪ T0: (0, 0, 0)
▪ T1: (2, 0, 2)
▪ T2: (0, 0, 0)
▪ T3: (1, 0, 0)
▪ T4: (0, 0, 2)

By running the detection algorithm, we find that the sequence <T0, T2, T3, T1, T4> allows
all threads to finish, meaning no deadlock.

However, if T2 requests an additional instance of C, the system becomes deadlocked.

Detection Algorithm Usage

When to run the detection algorithm depends on:

1. How often deadlocks are likely.


2. How many threads will be affected.

54
If deadlocks are frequent, the detection algorithm should be run frequently. Running it every
time a resource request is denied is costly, so it might be run periodically, like once an hour or
when CPU utilization drops.

In summary, the detection approach varies based on whether resources have a single instance or
multiple instances, and different algorithms and tools can help detect and handle deadlocks
efficiently.

Recovery from Deadlock


When a deadlock is detected, there are several ways to handle it. The system can either inform an
operator to resolve it manually or try to recover automatically. There are two main strategies for
automatic recovery:

1. Terminate Processes/Threads
2. Preempt Resources

Process and Thread Termination

To break a deadlock by terminating processes or threads, there are two methods:

1. Abort All Deadlocked Processes: This guarantees that the deadlock is broken but can be very
costly, as all progress made by these processes is lost.
2. Abort One Process at a Time: This method aborts processes one by one until the deadlock is
resolved. After each termination, the system checks if the deadlock still exists. This approach
incurs significant overhead.

Choosing which process to terminate involves considering:

• Process priority
• How long the process has run and how much longer it needs
• Number and type of resources used
• Number of resources still needed
• How many processes need to be terminated

Resource Preemption

Breaking a deadlock by preempting resources involves three key issues:

1. Selecting a Victim: Decide which resources and processes to preempt. This decision
should minimize costs, considering factors like the number of resources held and the time
consumed by the process.
2. Rollback: If a resource is taken from a process, the process must be rolled back to a safe
state and restarted. The simplest way is to completely restart the process, although partial
rollbacks (only as far as needed to break the deadlock) are more efficient but require
more information about the process state.

55
3. Starvation: Ensure that the same process is not always chosen as the victim, leading to
starvation (never finishing its task). To prevent this, include the number of rollbacks in
the cost factor for victim selection.

By addressing these issues, the system can effectively recover from deadlocks without leading to
excessive costs or process starvation.

CHP 9

CPU Sharing and Memory Management


To improve CPU utilization and speed up response times, multiple processes share the CPU
through scheduling. To achieve this, many processes must be kept in memory simultaneously,
which requires effective memory management.

Memory Management Strategies

There are various memory management strategies, from simple approaches to complex ones like
paging. The choice of strategy depends on the system's hardware.

Basic Hardware (Section 9.1.1)

• Main Memory and Registers: The only storage directly accessible by the CPU.
• Cache: Fast memory between the CPU and main memory to reduce delays caused by frequent
memory access.

Address Binding (Section 9.1.2)

• Compile Time: If the memory location is known during compilation, absolute code is generated.
If the location changes, recompilation is needed.
• Load Time: If the memory location is unknown at compile time, relocatable code is generated.
Binding occurs when loading into memory.
• Execution Time: If a process can move during execution, binding happens at runtime using special
hardware.

Multistep Processing of a User Program (Figure 9.3)

1. Source Program: Initial code written by a programmer.


2. Compiler: Converts source code to an object file.
3. Object File: Intermediate code.
4. Linker: Combines object files into an executable file.
5. Executable File: Final program ready to run.
6. Loader: Loads the executable into memory.
7. Program in Memory: Running program, possibly using dynamically linked libraries.

56
Logical vs. Physical Address Space (Section 9.1.3)

• Logical Address: Generated by the CPU.


• Physical Address: Actual location in RAM.
• Memory Management Unit (MMU): Translates logical addresses to physical addresses
during runtime (Figure 9.4).
o CPU generates a logical address.
o MMU translates it to a physical address.
o Physical Memory: Actual RAM where the data is stored.

Summary

Understanding these concepts helps in managing how programs are processed, executed, and
how memory is efficiently utilized in a computer system.

Dynamic Loading (Section 9.1.4)

Dynamic loading allows parts of a program to be loaded into memory only when needed, instead
of loading the entire program at once.

• Process:
1. The main program is loaded and starts executing.

57
2. When a routine is called, the program checks if it is already in memory.
3. If not, the routine is loaded from disk, addresses are updated, and execution continues.
• Advantage:
1. Saves memory by only loading necessary routines.
2. Useful for large programs with infrequently used code, like error handling routines.

Dynamic Linking and Shared Libraries (Section 9.1.5)

Dynamic linking allows shared libraries (DLLs) to be linked to programs at runtime, not at
compile time.

• Static Linking:
o Libraries are combined with the program during compile time.
o Increases the size of the binary program image.
• Dynamic Linking:
o Libraries are linked during execution (runtime).
o Saves memory and disk space by sharing common libraries among multiple programs.

Summary

Dynamic loading and dynamic linking optimize memory usage and disk space, ensuring that
only necessary code is loaded and shared libraries are efficiently utilized.

Contiguous Memory Allocation (Section 9.2)

Memory is divided into two main partitions: one for the operating system and one for user
processes. In many systems, including Linux and Windows, the OS is placed in high memory.

Contiguous Memory Allocation:

• Each process occupies a single, continuous block of memory.

Memory Allocation (Section 9.2.2)

Memory allocation involves assigning processes to partitions in memory. Initially, all memory is
available and considered a single large block (hole). Processes are allocated to these holes using
various strategies:

• First Fit: Allocate the first hole large enough to accommodate the process.
• Best Fit: Allocate the smallest hole that is still large enough.
• Worst Fit: Allocate the largest available hole.

58
Simulations show that first fit and best fit are generally more efficient than worst fit.

Fragmentation (Section 9.2.3)

External Fragmentation:

• Occurs when there is enough total memory to satisfy a request, but the available spaces are not
contiguous, resulting in many small holes.
• Example: Free memory scattered between processes.

Internal Fragmentation:

• Occurs when allocated memory is slightly larger than the requested memory, leaving small,
unused spaces within the allocated block.

Solution to Fragmentation:

• Compaction: Reorganize memory contents to consolidate free memory into a single large block.

Swapping (Section 9.5)


Swapping:

• Temporarily moves a process or a portion of a process from main memory to a backing store
(secondary storage) and then brings it back into memory for execution (see Figure 9.19).
• Allows the total physical address space of all processes to exceed the system's physical memory,
increasing the degree of multiprogramming.

Standard Swapping (Section 9.5.1):

• Process: Moves entire processes between main memory and the backing store.
• Backing Store: Fast secondary storage large enough to hold parts of processes that need to be
swapped.
• Procedure: When swapping out, the OS writes process data structures and metadata to the
backing store. For multithreaded processes, per-thread data structures must also be swapped.

59
• Advantage: Enables physical memory to be oversubscribed, accommodating more processes
than the available physical memory.

Virtual Memory Overview

Virtual memory is a memory management technique that allows the execution of processes
without requiring the entire process to be in physical memory. Here’s a concise breakdown:

1. Goal: Enable multiprogramming by keeping many processes in memory, allowing them


to be larger than physical memory.
2. Key Advantage: Programs can exceed the size of physical memory, and programmers
don’t need to worry about memory limitations.
3. How It Works:
o Separates logical memory (as viewed by the programmer) from physical
memory.
o Provides an illusion of a large, uniform memory space.
o Loads only the necessary parts of a program into physical memory.
4. Benefits:
o Larger Programs: Programs are not constrained by physical memory size.
o Increased Multiprogramming: More programs can run simultaneously,
increasing CPU utilization and throughput.
o Efficiency: Reduces I/O operations needed to load/swaps parts of programs,
speeding up execution.
5. Challenges:
o Complex to implement.
o Can decrease performance if not managed properly.

Practical Examples

• Error Handling Code: Rarely executed error-handling code doesn’t need to be in


memory all the time.
60
• Large Data Structures: Arrays and tables often allocate more memory than used.
Virtual memory loads only the necessary parts.
• Optional Features: Infrequently used program features do not need constant memory
allocation.

Paging (Section 9.3)

Paging:

• A memory management scheme that allows a process's physical address space to be non-
contiguous.
• Advantages: Avoids external fragmentation and the need for compaction.

Basic Method (Section 9.3.1)

• Physical Memory: Divided into fixed-sized blocks called frames.


• Logical Memory: Divided into blocks of the same size called pages.
• Process Execution: Pages are loaded into any available frames in memory.

Address Translation

• Logical Address: Divided into two parts:


o Page Number (p)
o Page Offset (d)
• Translation Steps:
1. Extract the page number (p) and use it as an index into the page table.
2. Extract the corresponding frame number (f) from the page table.
3. Replace the page number (p) in the logical address with the frame number (f).
• Page Table: Contains the base address of each frame in physical memory. The page
offset (d) is combined with the frame base address to form the physical address.
• Page Size: Defined by the hardware, typically a power of 2, ranging from 4 KB to 1 GB.
• Logical Address: If the logical address space size is 2m2^m2m and the page size is
2n2^n2n bytes:
o High-order m−nm-nm−n bits: Page number
o Low-order nnn bits: Page offset

61

You might also like