Operating System
Operating System
1. Machine Language
2. Assembly Language
3. High-Level Languages
● Definition: Programming languages that are closer to human languages and abstract
away hardware-specific details.
● Characteristics:
○ Examples include C, Python, Java, and C++.
○ Focus on problem-solving rather than hardware specifics.
○ Portability: Can run on different hardware with minimal changes.
● Advantages:
○ Easier to learn, write, and maintain.
○ Supports complex programming paradigms (e.g., object-oriented programming).
● Translation:
○ Requires a compiler or interpreter to convert high-level code into machine
language.
Summary Table
Language Level Examples Translation Use Case
System software such as operating systems and compilers often involves all three levels of
languages:
Compilers and interpreters are essential tools in programming that translate high-level
programming languages into machine language that a computer can execute. Here's a detailed
comparison and explanation:
Compilers
1. Definition: A compiler is a program that translates the entire source code written in a
high-level language into machine code or an intermediate format (e.g., bytecode) before
execution.
2. Process:
2. Process:
○ Execution: The program cannot run independently; the interpreter must execute
it every time.
○ Speed: Slower execution because translation happens on the fly.
○ Error Detection: Stops immediately when an error is encountered, making
debugging easier.
○ Examples of Interpreted Languages: Python, JavaScript, Ruby, PHP.
4. Advantages:
Comparison Table
Feature Compiler Interpreter
Error Detection Detects all errors at compilation. Detects errors during execution.
Loading, linking, and relocation are critical processes in the execution of a program. They
involve preparing the program for execution by managing its code, data, and memory
requirements.
1. Loading
2. Linking
● Definition: Linking is the process of combining multiple object files and libraries to
create a single executable file.
● Purpose:
○ Resolves references between program modules.
○ Connects function calls in one module to the corresponding function definitions in
another.
● Types of Linking:
○ Static Linking:
■ Combines all required code and libraries into a single executable during
the compile/link stage.
■ Advantages:
■ No external dependencies during execution.
■ Faster runtime since all code is preloaded.
■ Disadvantages:
■ Larger executable file size.
■ Less flexible for updates.
○ Dynamic Linking:
■ References to external libraries (dynamic link libraries or shared objects)
are resolved at runtime.
■ Advantages:
■ Smaller executable size.
■ Easier to update libraries without recompiling the program.
■ Disadvantages:
■ Requires the libraries to be present at runtime.
■ May introduce runtime errors if libraries are missing or
incompatible.
● Linker Tools:
○ Linker: Combines object files and libraries.
○ Dynamic Linker: Resolves references at runtime.
3. Relocation
Process Flow
1. Compilation:
○ High-level source code is converted into object files (machine code fragments).
2. Linking:
○ Object files and libraries are combined to resolve symbols and create an
executable.
3. Loading:
○ The executable is transferred into memory.
4. Relocation:
○ Memory addresses are adjusted to ensure correct execution.
Summary Table
Process Definition Key Purpose When It Occurs
Linking Combine code and resolve Creates an executable file. Before or during
references. execution.
Relocatio Adjust memory addresses Ensures proper memory During linking or
n for execution. referencing. loading.
By combining these processes, modern systems ensure efficient and flexible program execution
while optimizing memory and resource usage.
Macros
Macros
Macros are a feature in assembly language (and some high-level programming languages) that
allow programmers to define reusable code blocks. They enable the substitution of a sequence
of instructions or operations for a single name or label, simplifying repetitive tasks and improving
code readability.
1. Definition
2. Features
4. Macro Syntax
In Assembly Language:
MACRO_NAME MACRO ARGUMENTS
; Instructions or code using ARGUMENTS
; ...
ENDM
Example:
SUM MACRO X, Y
MOV AX, X ; Move X into AX
ADD AX, Y ; Add Y to AX
ENDM
Usage:
In C (Preprocessor Directive):
#define MACRO_NAME(arguments) (code using arguments)
Example:
#define SQUARE(x) ((x) * (x))
int main() {
int result = SQUARE(5); // Expands to: ((5) * (5))
return 0;
}
5. Types of Macros
Example:
HELLO MACRO
MOV DX, "Hello, World!"
CALL PRINT
ENDM
○
2. Parameterized Macros:
Example:
MULTIPLY MACRO A, B
MOV AX, A
MUL B
ENDM
○
3. Nested Macros:
6. Advantages
● Code Bloat: Excessive macro usage can lead to larger executable sizes.
● Limited Debugging: Errors in macros are harder to trace.
● Lack of Type Checking: Particularly in languages like C, macros do not enforce strict
type safety.
● Reduced Readability: Overuse can make code harder to understand.
8. Use Cases
● Assembly Language:
○ Simplifying repetitive operations (e.g., arithmetic or I/O).
○ Defining hardware-specific operations.
● High-Level Languages:
○ Replacing constants and formulas (#define in C).
○ Simplifying code patterns.
By strategically using macros, developers can improve efficiency and readability in their
programs while reducing errors in repetitive tasks.
Debuggers
Debuggers
A debugger is a specialized software tool used to test, analyze, and debug programs during
development. It helps identify and fix errors (bugs) by allowing programmers to inspect and
control the execution of a program.
1. Purpose of a Debugger
1. Breakpoints:
○ Step Into: Executes the next line of code, diving into functions if they are called.
○ Step Over: Executes the next line of code but skips over function details.
○ Step Out: Completes the current function and returns to the calling code.
3. Variable Inspection:
○ Displays the sequence of function calls leading to the current execution point.
○ Useful for tracing execution flow.
5. Memory Inspection:
○ Triggers a pause only when specific conditions are met (e.g., a variable reaches
a certain value).
7. Watchpoints (Data Breakpoints):
3. Types of Debuggers
1. Source-Level Debuggers:
○ Operate on the source code level (e.g., line numbers, variable names).
○ Examples: GDB (GNU Debugger), Visual Studio Debugger.
2. Machine-Level Debuggers:
○ Built into Integrated Development Environments (IDEs) like Eclipse, IntelliJ IDEA,
and Xcode.
1. Setup:
6. Debugging Techniques
○ Trace back from the point of failure to identify the root cause.
4. Divide and Conquer:
8. Limitations of Debuggers
Debuggers are indispensable for modern software development, enabling developers to write
robust and error-free code efficiently.
An Operating System (OS) is system software that manages hardware and software
resources, providing a user-friendly interface and ensuring efficient execution of applications. Its
structure defines how its components are organized and interact to achieve its functions.
1. Kernel:
○ Programs that perform specific tasks like file management, disk cleanup, and
system monitoring.
4. System Libraries:
○ Provide APIs for applications to interact with the OS without direct hardware
access.
5. File System:
Operating systems can have different architectures or structures based on how their
components are organized.
A. Monolithic Architecture
● Description: The OS is divided into layers, each providing services to the layer above
and receiving services from the layer below.
● Advantages:
○ Modularity simplifies debugging and updates.
○ Improved security through controlled access between layers.
● Disadvantages:
○ Reduced performance due to overhead of layer-to-layer communication.
○ Designing layered systems is challenging.
● Examples: THE Operating System, Multics.
C. Microkernel Architecture
D. Modular Architecture
E. Client-Server Model
4. Comparison of OS Structures
Structure Performance Modularity Ease of Scalability Reliability
Debugging
6. Trends in OS Design
● Hybrid Kernels: Combines the efficiency of monolithic kernels with the modularity of
microkernels (e.g., Windows NT, macOS).
● Virtualization Support: Modern OSes integrate features for running virtual machines
(e.g., VMware, Hyper-V).
● Containerization: Lightweight OS features for containerized applications (e.g., Docker,
Kubernetes).
By understanding the structure of operating systems, developers can choose or design systems
tailored to specific requirements, balancing performance, reliability, and maintainability.
Basics of Operating Systems: Operations and
Services
An Operating System (OS) is system software that acts as an interface between computer
hardware and users. It manages hardware resources and provides services for application
programs.
1. OS Operations
The primary operations of an operating system include:
● I/O Operations: Manages input and output devices (keyboard, mouse, printer, etc.).
● Drivers: Provides device drivers to interact with hardware.
● Buffering and Caching: Enhances device performance by temporarily storing data.
2. OS Services
An OS provides various services to make it easier for users and programs to operate.
● Manages input and output devices for user interaction and data transfer.
● Supports communication over networks, such as file sharing and remote access.
Summary
The operating system acts as a resource manager and service provider. Its operations manage
processes, memory, and devices, while its services offer interfaces and tools for users and
programs to function efficiently.
2. File Management
3. Device Management
4. Information Maintenance
5. Communication
#include <unistd.h>
#include <fcntl.h>
int main() {
int file = open("[Link]", O_WRONLY | O_CREAT, 0644); // Open/Create file
write(file, "Hello, World!\n", 14); // Write to file
close(file); // Close file
return 0;
}
Explanation:
#include <stdio.h>
#include <unistd.h>
int main() {
int pid = fork(); // Create a new process
if (pid == 0) {
printf("Child Process\n");
} else {
printf("Parent Process\n");
}
return 0;
}
Explanation:
5. Key Notes
● User Mode vs Kernel Mode:
○ User programs run in user mode with limited privileges.
○ The OS kernel runs in kernel mode with full access to hardware.
● Interrupts and Traps:
○ System calls are implemented using software interrupts or traps to switch to
kernel mode.
● Portability:
○ System calls may vary across operating systems but are generally abstracted
through APIs like POSIX or WinAPI.
System calls are crucial for secure and efficient interaction between user applications and the
operating system.
Operating System Design and Implementation
The design and implementation of an operating system (OS) involve defining its structure,
functionalities, and mechanisms for interacting with hardware and software. The process
requires careful consideration of system performance, security, portability, and scalability.
1. Goals of OS Design
1. Efficiency: Optimize resource utilization (CPU, memory, storage).
2. Security and Protection: Safeguard data and resources from unauthorized access.
3. Flexibility: Adaptability to changing hardware and software requirements.
4. Portability: Support different hardware platforms with minimal changes.
5. Reliability and Fault Tolerance: Ensure consistent performance and recover from
failures.
6. User-Friendly Interface: Provide intuitive interfaces (CLI/GUI) for end-users.
2. OS Design Approaches
1. Monolithic Architecture
● All OS components (e.g., file system, memory management) are integrated into a single
kernel.
● Advantages: High performance due to fewer context switches.
● Disadvantages: Hard to modify or debug, less modular.
● Example: Linux, Unix
2. Layered Architecture
3. Microkernel Architecture
● Minimal kernel with only core functions (e.g., process and memory management).
● Other services (e.g., device drivers, file systems) run as user-level processes.
● Advantages: Better security, easier maintenance.
● Disadvantages: Slower performance due to message passing overhead.
● Example: MINIX, QNX
4. Hybrid Kernel
5. Virtual Machines
3. OS Implementation
1. Programming Languages
● C and C++: Most operating systems (e.g., Linux, Windows) are implemented using
these languages due to their low-level hardware control and high performance.
● Assembly Language: Used for critical hardware interactions.
● Higher-Level Languages: Python, Java, and Rust are occasionally used for OS
components and applications.
2. Development Process
4. OS Components
1. Kernel (Core of the OS)
Manages hardware resources and provides essential services:
2. Shell
3. System Programs
4. Libraries
APIs (e.g., POSIX) that provide standardized system calls for application developers.
6. Example OS Development
Basic Steps for OS Development
Summary
Designing and implementing an OS involves careful planning of its architecture, kernel, and
services. It requires expertise in low-level programming and system development. Modern
operating systems balance performance, security, and scalability to meet user and application
demands.
1. Goals of OS Design
1. Efficiency: Optimize resource utilization (CPU, memory, storage).
2. Security and Protection: Safeguard data and resources from unauthorized access.
3. Flexibility: Adaptability to changing hardware and software requirements.
4. Portability: Support different hardware platforms with minimal changes.
5. Reliability and Fault Tolerance: Ensure consistent performance and recover from
failures.
6. User-Friendly Interface: Provide intuitive interfaces (CLI/GUI) for end-users.
2. OS Design Approaches
1. Monolithic Architecture
● All OS components (e.g., file system, memory management) are integrated into a single
kernel.
● Advantages: High performance due to fewer context switches.
● Disadvantages: Hard to modify or debug, less modular.
● Example: Linux, Unix
2. Layered Architecture
3. Microkernel Architecture
● Minimal kernel with only core functions (e.g., process and memory management).
● Other services (e.g., device drivers, file systems) run as user-level processes.
● Advantages: Better security, easier maintenance.
● Disadvantages: Slower performance due to message passing overhead.
● Example: MINIX, QNX
4. Hybrid Kernel
5. Virtual Machines
● C and C++: Most operating systems (e.g., Linux, Windows) are implemented using
these languages due to their low-level hardware control and high performance.
● Assembly Language: Used for critical hardware interactions.
● Higher-Level Languages: Python, Java, and Rust are occasionally used for OS
components and applications.
2. Development Process
4. OS Components
1. Kernel (Core of the OS)
2. Shell
3. System Programs
Includes utilities like compilers, editors, and network tools.
4. Libraries
APIs (e.g., POSIX) that provide standardized system calls for application developers.
6. Example OS Development
Basic Steps for OS Development
Designing and implementing an OS involves careful planning of its architecture, kernel, and
services. It requires expertise in low-level programming and system development. Modern
operating systems balance performance, security, and scalability to meet user and application
demands.
● Presents a login prompt (Command Line Interface - CLI) or loads a Graphical User
Interface (GUI) for user interaction.
2. Types of Booting
1. Cold Boot (Hard Boot):
○ Booting the system after it is completely powered off.
2. Warm Boot (Soft Boot):
○ Restarting the system without turning off the power (e.g., pressing Ctrl + Alt +
Del).
4. Boot Configurations
4.1 Boot Configuration in Linux
● Configuration files:
○ /boot/grub/[Link] - Configures GRUB bootloader.
○ /etc/fstab - Defines filesystems to mount during boot.
Key Takeaways
● The boot process initializes hardware, loads the operating system, and prepares the
system for user interaction.
● It involves multiple stages, including POST, bootloader execution, kernel loading, and
process initialization.
● Modern OS boot systems use tools like GRUB and Systemd for faster and more flexible
operation.
● Troubleshooting boot failures requires analyzing logs, repairing configuration files, and
sometimes reinstalling components.
1. Process Scheduling
1.1 Process Scheduling Definition
Process scheduling is the mechanism by which the operating system decides the order in which
processes are executed by the CPU. It aims to maximize CPU utilization, reduce waiting time,
and ensure fairness.
○ Selects processes from the job pool and loads them into memory for execution.
○ Controls the degree of multiprogramming (number of processes in memory).
○ Frequency: Runs less frequently.
○ Example: Loads batch jobs into memory.
2. Short-Term Scheduler (CPU Scheduler)
2. Process Operations
2.1 Process States
A PCB is a data structure maintained by the OS to store information about each process.
Contents of PCB:
Processes often need to communicate and share data. IPC mechanisms include:
● Definition: Switching the CPU from one process to another by saving and restoring
process states.
● Overhead: Context switching adds overhead, as saving and loading process states take
time.
● Importance: Enables multitasking and process scheduling.
3. Key Challenges in Process Management
1. Deadlocks:
Summary
Process scheduling and operations are critical for efficient process management in operating
systems. Scheduling algorithms ensure fairness and optimize CPU utilization, while process
operations like creation, termination, and communication enable multitasking and resource
sharing. Modern OS implementations rely on advanced techniques to handle challenges like
deadlocks, starvation, and context switching effectively.
Example (Linux):
$ mkfifo mypipe # Create a named pipe
$ echo "Hello" > mypipe # Write data
$ cat < mypipe # Read data
●
Example (Linux):
msgget(), msgsnd(), msgrcv() // C functions for message queue operations
●
●
1.4 Semaphores
Example (Linux):
semget(), semop(), semctl() // Semaphore operations in C
●
1.5 Signals
Example (Linux):
kill -9 1234 # Sends SIGKILL to process ID 1234
●
1.6 Sockets
Example (Python):
import socket
s = [Link]()
[Link](("localhost", 8080))
[Link](5)
conn, addr = [Link]()
print("Connected to:", addr)
●
● Definition: Maps files or devices directly into memory space, enabling sharing between
processes.
● Usage: Often used for large data sharing in multimedia applications.
Example (Python):
import mmap
with open("[Link]", "r+b") as f:
mm = [Link]([Link](), 0)
print([Link]())
●
4. Applications of IPC
1. Operating Systems
○ Web servers and database servers use sockets and message queues.
3. Distributed Systems
Key Takeaways
1. Basic Concepts
Client
● Definition: A process or device that sends requests for services (e.g., accessing web
pages or databases).
● Examples: Web browsers, email clients, and mobile apps.
Server
Communication Model
● Request-Response Protocol:
1. Client sends a request to the server.
2. Server processes the request and sends a response.
2. Communication Techniques
2.1 Sockets
● Definition: An endpoint for sending and receiving data between client and server
processes.
● Types:
1. Stream Sockets (TCP) – Reliable, connection-oriented communication.
2. Datagram Sockets (UDP) – Unreliable, connectionless communication.
import socket
# Server
server = [Link]()
[Link](("localhost", 8080))
[Link](1)
conn, addr = [Link]()
print("Connected by", addr)
[Link](b"Hello Client")
[Link]()
import socket
# Client
client = [Link]()
[Link](("localhost", 8080))
print([Link](1024))
[Link]()
● Definition: Used for communication over the web. Clients send HTTP requests to web
servers, and servers respond with resources.
● Security: HTTPS (HTTP Secure) encrypts data using SSL/TLS.
● Examples: RESTful and SOAP APIs for web services.
● Definition: Communication through message queues where messages are stored and
processed asynchronously.
● Examples: RabbitMQ, Apache Kafka.
● Use Cases: Distributed systems and microservices where real-time processing is
required.
4. Client-Server Architectures
4.1 1-Tier Architecture
● Description: Both client and server functionalities reside on the same machine.
● Example: Local database applications.
● Description: Extends 3-tier with multiple layers for scalability and modularity.
● Example: Cloud-based applications (frontend, backend, databases, APIs).
5. Security in Client-Server Communication
1. Authentication: Verifies the identity of clients and servers (e.g., username-password,
OAuth).
2. Authorization: Ensures clients can access only authorized resources.
3. Encryption: Protects data in transit (e.g., HTTPS, TLS/SSL).
4. Firewalls: Blocks unauthorized access to the server.
5. Session Management: Tracks user sessions to prevent attacks like session hijacking.
7. Real-World Examples
1. Web Browsing:
Process Synchronization
Process synchronization is a mechanism to ensure that multiple processes can execute
concurrently without interfering with each other when accessing shared resources. It prevents
race conditions and ensures data consistency in multithreading and multiprocessing
environments.
● Definition: A part of a process where shared resources (e.g., variables, files) are
accessed.
● Problem: If multiple processes enter their critical sections simultaneously, data
inconsistency may occur.
● Goal: Ensure mutual exclusion, progress, and bounded waiting.
1. Mutual Exclusion: Only one process can access the critical section at a time.
2. Progress: If no process is in the critical section, others should be able to enter without
indefinite delay.
3. Bounded Waiting: Every process should eventually get a turn to enter the critical
section (no starvation).
2. Synchronization Mechanisms
2.1 Locks (Mutex)
● Definition: A binary flag that ensures only one process enters the critical section at a
time.
● Operation:
1. Lock before entering the critical section.
2. Unlock after exiting.
Example (Pseudocode):
[Link]()
# Critical Section
[Link]()
●
2.2 Semaphores
● Types:
1. Binary Semaphore (Mutex): Allows 0 or 1 as values, used for mutual exclusion.
2. Counting Semaphore: Allows multiple processes up to a limit.
● Operations:
Example (C):
sem_wait(&sem); // Wait operation
// Critical Section
sem_post(&sem); // Signal operation
●
2.3 Monitors
● Definition: A higher-level synchronization construct that groups variables and methods
to control access.
● Features:
Example (Java):
synchronized void criticalSection() {
// Critical Section
}
●
2.5 Spinlocks
● Problem:
● Problem:
● Problem:
○ Philosophers sit around a table with one fork between each pair.
○ They need two forks to eat, leading to potential deadlocks.
● Solution:
● Definition: A situation where two or more processes are waiting indefinitely for
resources held by each other.
4.2 Starvation
7. Key Takeaways
● Process synchronization is crucial to ensure data consistency and prevent conflicts in
concurrent programming.
● Synchronization techniques include mutexes, semaphores, monitors, and spinlocks.
● Classical problems like producer-consumer, readers-writers, and dining
philosophers illustrate synchronization challenges.
● Handling deadlocks and starvation is vital to avoid indefinite blocking and ensure
fairness.
● Distributed systems use specialized synchronization techniques like distributed locks
and clock synchronization.
○ A process must not wait forever to enter its critical section, preventing
starvation.
3. Structure of a Process
do {
Entry Section; // Request access to the critical section
Critical Section; // Code that accesses shared resources
Exit Section; // Release the lock and exit
Remainder Section; // Non-critical code
} while (true);
do {
flag[i] = true; // Process i requests access
turn = j; // Give priority to process j
while (flag[j] && turn == j); // Wait until process j finishes
// Critical Section
...
flag[i] = false; // Exit critical section
// Remainder Section
} while (true);
Example:
do {
while (TestAndSet(lock)); // Busy waiting
// Critical Section
lock = false;
} while (true);
○
2. Compare-and-Swap (CAS)
○
2. Semaphores
Example (C):
sem_wait(&sem); // Wait operation
// Critical Section
sem_post(&sem); // Signal operation
○
3. Monitors
Example (Java):
synchronized void criticalSection() {
// Critical Section
}
○
○ Manages access when multiple readers can read concurrently, but only one
writer can write.
3. Dining Philosophers Problem
○ Models resource sharing with multiple processes using limited resources (e.g.,
forks).
6. Challenges
1. Deadlock
7. Key Takeaways
● The Critical Section Problem arises in concurrent programming when multiple
processes access shared resources.
● Solutions must satisfy mutual exclusion, progress, and bounded waiting.
● Methods include software solutions (e.g., Peterson’s Algorithm), hardware solutions
(e.g., Test-and-Set), and high-level synchronization tools (e.g., semaphores and
monitors).
● Addressing challenges like deadlocks and starvation ensures fair and efficient
synchronization.
Peterson's Solution
Peterson's Solution is a software-based algorithm designed to solve the Critical Section
Problem for two processes. It ensures mutual exclusion, progress, and bounded waiting by
using shared memory (variables) and a specific protocol.
It was introduced by Gary Peterson in 1981 and is one of the simplest solutions for
synchronization between two processes.
1. Problem Setup
● Two processes, P0 and P1, share the following resources:
○ Critical Section (CS): A section of code that accesses shared resources.
○ Entry Section: Where processes request permission to enter the CS.
○ Exit Section: Where processes release the lock after exiting the CS.
Both processes need to request access to the Critical Section in such a way that:
1. flag[i] (for each process, where i is the process number, i.e., 0 or 1):
○ Indicates whether the process wants to enter its critical section (true means
wanting to enter, false means not).
2. turn:
○ Specifies whose turn it is to enter the critical section.
○ If turn == i, then process i has permission to enter its critical section. If turn
!= i, the process will wait for the other process.
Pseudocode
// Shared variables
flag[0] = flag[1] = false;
turn = 0;
do {
// Entry Section
flag[i] = true; // Process i wants to enter the critical section
turn = j; // Give priority to process j (the other process)
// Wait until process j either exits the critical section or gives up the turn
while (flag[j] && turn == j);
// Critical Section
// Process i can now safely access shared resources
// Exit Section
flag[i] = false; // Process i leaves the critical section
Explanation:
○ Process i sets flag[i] = true to indicate its intention to enter the critical
section.
○ Process i sets turn = j to give process j a chance to execute, enforcing
alternating access to the CS.
2. Wait Condition:
○ The while loop checks if the other process (j) is currently interested in entering
the critical section (flag[j] == true).
○ It also checks whether it's still process j's turn to enter the CS (turn == j).
○ If both conditions are true, process i waits (i.e., it "spins" in the loop) until
process j either exits the CS or relinquishes the turn.
3. Critical Section:
○ Once the process enters the CS (i.e., the while loop exits), it can safely perform
operations on shared resources.
4. Exit Section:
○ Process i sets flag[i] = false to indicate it has finished its critical section
and releases the lock.
○ This allows process j to potentially enter the CS if it is still interested.
○ Only one process can enter the critical section at a time because the turn
variable ensures that only one process has permission to enter.
○ If both processes attempt to enter at the same time, the turn variable gives
precedence to one process, thereby avoiding simultaneous access.
2. Progress:
○ If no process is in the critical section, the algorithm guarantees that one of the
waiting processes will be allowed to enter the critical section. This is ensured by
the turn-based mechanism and the fact that if flag[i] is false, then process
i doesn't want to enter.
3. Bounded Waiting:
○ Each process can only be blocked at most once, and the other process must
eventually relinquish its turn, thus ensuring bounded waiting (no starvation).
4. Example Walkthrough
Let’s walk through a simple example with two processes, P0 and P1.
Step-by-Step Execution:
4. Once one of the processes exits the critical section, the other process is allowed to
enter.
5. Advantages and Disadvantages
Advantages:
1. Simple and elegant: Peterson’s Solution is conceptually simple, using only two
variables.
2. Guarantees mutual exclusion: The algorithm ensures no two processes can enter the
critical section simultaneously.
3. No hardware support required: Works purely in software using shared variables.
Disadvantages:
1. Busy-waiting: The solution involves busy waiting in the while loop, which wastes
CPU cycles when the process is waiting.
2. Limited to two processes: The solution is only applicable to two processes. Extending
it to more processes would require more complex mechanisms.
3. Performance issues: Due to busy-waiting, the algorithm might be inefficient in
high-contention environments (i.e., when many processes are competing for the critical
section).
6. Conclusion
Peterson’s Solution is a fundamental synchronization algorithm that demonstrates how software
mechanisms can be used to solve the Critical Section Problem. Though it is efficient for two
processes, its practical usage is limited in modern systems due to the inefficiency of
busy-waiting and its lack of scalability. However, it remains an important concept for
understanding synchronization in concurrent systems.
Semaphores
A semaphore is a synchronization tool used to manage concurrent access to shared resources
in multi-threaded or multi-process environments. Semaphores help prevent race conditions,
ensuring mutual exclusion and coordination among processes or threads. They are typically
used for process synchronization and communication between processes.
Semaphores are an essential part of inter-process communication (IPC) and can be thought
of as an abstraction over low-level locking mechanisms.
1. Types of Semaphores
There are two main types of semaphores:
● Usage: This type is used for mutual exclusion (mutex), ensuring that only one process
or thread can access the critical section at a time.
● Operations:
sem_t mutex;
sem_init(&mutex, 0, 1); // Binary semaphore initialized to 1 (unlocked)
●
● Definition: A counting semaphore can hold any integer value, typically used for
managing access to a pool of resources (e.g., a fixed number of printers or database
connections).
● Operations:
sem_t availableResources;
sem_init(&availableResources, 0, 5); // Counting semaphore initialized to 5
// Access resource
sem_wait(&availableResources); // Wait for an available resource (decrement)
// Critical Section
sem_post(&availableResources); // Release resource (increment)
●
2. Semaphore Operations
The two basic operations on semaphores are:
3.2 Synchronization
● Semaphores can synchronize processes in different stages of execution. For example,
one process may need to wait for another to finish before proceeding, and semaphores
can be used to control this flow.
Example:
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0; // Points to the next empty slot
int out = 0; // Points to the next filled slot
int main() {
pthread_t producerThread, consumerThread;
// Initialize semaphores
sem_init(&empty, 0, BUFFER_SIZE); // All buffer slots are initially empty
sem_init(&full, 0, 0); // No full slots at the beginning
sem_init(&mutex, 0, 1); // Mutex is initially unlocked
return 0;
}
Explanation:
1. The producer waits for an empty slot (sem_wait(&empty)), enters the critical section
(sem_wait(&mutex)), adds an item to the buffer, and signals that a slot is full
(sem_post(&full)).
2. The consumer waits for a full slot (sem_wait(&full)), enters the critical section
(sem_wait(&mutex)), removes an item from the buffer, and signals that a slot is empty
(sem_post(&empty)).
1. Simple and Effective: Semaphores are relatively simple to understand and implement,
providing a powerful tool for synchronizing processes and threads.
2. No Busy-Waiting: Unlike locks, semaphores allow for blocking (waiting), meaning
processes do not consume CPU cycles while waiting.
3. Versatility: They can be used for a wide variety of synchronization tasks, including
mutual exclusion, signaling, and resource counting.
Disadvantages:
1. Complexity in Usage: While simple, improper use of semaphores (e.g., forgetting to
signal or posting when a resource is released) can lead to issues such as deadlocks or
race conditions.
2. No Priority Handling: Semaphores do not offer an inherent mechanism for priority
scheduling, which can lead to issues like starvation if some processes are continually
blocked.
3. Potential for Deadlocks: If semaphores are not managed properly (e.g., circular wait
conditions), they can cause deadlocks, where processes wait forever.
6. Key Takeaways
● Semaphores are a synchronization tool used to control access to shared resources and
avoid race conditions.
● They come in two types: binary semaphores (mutexes) and counting semaphores.
● Semaphore operations (P() and V()) are used to decrement and increment semaphore
values, respectively, ensuring coordination between processes or threads.
● Semaphores are widely used in scenarios like the Producer-Consumer problem,
Readers-Writers problem, and Resource Allocation.
Solution Criteria:
1. Mutual Exclusion: Only one process can execute in the critical section at a time.
2. Progress: Processes outside the critical section should not block others unnecessarily.
3. Bounded Waiting: No process should have to wait indefinitely to enter the critical
section.
○
■ Without synchronization, both threads might read the same value and
write the same result, leading to incorrect updates.
3. Synchronization Mechanisms
3.1 Locks
Example:
3.2 Semaphores
● Semaphores are integer variables used for signaling and controlling access to
resources.
● Binary Semaphore: Ensures mutual exclusion (acts like a lock).
● Counting Semaphore: Allows a certain number of processes to access resources
concurrently.
Example:
pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
// Critical section
pthread_mutex_unlock(&lock);
3.4 Monitors
Example:
synchronized(obj) {
while (!condition) {
[Link](); // Wait until condition is true
}
// Critical section
[Link](); // Notify waiting threads
}
3.6 Barriers
● Barriers force all threads to reach a certain point in execution before any thread can
proceed further.
● Example: Synchronize threads at the end of a computation step before moving to the
next step.
● Producers generate data and place it in a buffer, while consumers remove data from the
buffer.
● Synchronization is needed to:
1. Prevent producers from adding items when the buffer is full.
2. Prevent consumers from removing items when the buffer is empty.
● Readers can read shared data concurrently, but writers require exclusive access.
● Synchronization ensures that:
1. Multiple readers can read simultaneously.
2. Only one writer can write at a time.
● Philosophers sit around a table and share chopsticks. Each philosopher needs two
chopsticks to eat.
● Problem: Avoid deadlocks where each philosopher grabs one chopstick and waits
forever for the second one.
5. Synchronization Issues
1. Deadlock:
○ Occurs when processes are waiting indefinitely for resources held by each other.
○ Example: Process A locks Resource 1 and waits for Resource 2, while Process B
locks Resource 2 and waits for Resource 1.
2. Starvation:
7. Key Takeaways
● Synchronization is crucial for maintaining data consistency and avoiding conflicts in
concurrent programming.
● Mechanisms like locks, semaphores, mutexes, monitors, and message passing are
widely used.
● Classical problems such as Producer-Consumer, Readers-Writers, and Dining
Philosophers demonstrate the challenges of synchronization.
● Proper synchronization prevents deadlocks, race conditions, and starvation in
resource-sharing systems.
By leveraging these techniques, systems ensure efficient and safe concurrent operations,
making them essential for modern operating systems and distributed computing.
1. Threads: Basics
A thread is the smallest unit of execution within a process. Each thread:
Types of Threads:
2. Multicore Programming
What is Multicore Programming?
3. Multithreading Models
3.1 Many-to-One Model:
● Multiple user threads map to one kernel thread.
● Limitation: Cannot utilize multicore systems effectively.
#include <pthread.h>
#include <stdio.h>
int main() {
pthread_t thread;
pthread_create(&thread, NULL, printMessage, "Hello, World!");
pthread_join(thread, NULL);
return 0;
}
4.2 OpenMP
int main() {
#pragma omp parallel
{
printf("Hello from thread %d\n", omp_get_thread_num());
}
return 0;
}
● Java provides built-in support for multithreading using the Thread class.
● Ensure mutual exclusion by allowing only one thread to access the critical section at a
time.
pthread_mutex_t lock;
pthread_mutex_lock(&lock);
// Critical section
pthread_mutex_unlock(&lock);
5.2 Semaphores:
● Used for signaling and resource management.
5.3 Barriers:
○ Proper scheduling algorithms are required to allocate CPU time among threads
fairly and efficiently.
#define NUM_THREADS 4
#define ARRAY_SIZE 16
int arr[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int sum = 0; // Shared variable
pthread_mutex_t lock;
pthread_mutex_lock(&lock);
sum += local_sum;
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
int thread_ids[NUM_THREADS];
pthread_mutex_init(&lock, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
Output:
Total Sum: 136
9. Key Takeaways
● Multithreading enables better performance and responsiveness by dividing tasks into
multiple threads.
● Multicore programming leverages modern CPUs with multiple cores to execute
threads in parallel, increasing throughput and scalability.
● Synchronization mechanisms like locks, semaphores, and condition variables ensure
data consistency in concurrent environments.
● Libraries like Pthreads, OpenMP, and Java Threads simplify multicore programming in
different languages.
Multicore programming is vital for high-performance computing, AI, big data, and modern
application development. Proper synchronization and resource management are essential to
prevent deadlocks, race conditions, and performance bottlenecks.
Multithreading Models
Multithreading models define the relationship between user-level threads and kernel-level
threads in an operating system. They determine how threads are mapped and managed,
enabling efficient utilization of CPU resources, especially in multicore processors.
2. Multithreading Models
Multithreading models define how user threads are mapped to kernel threads. There are three
primary models:
Diagram:
User Threads: T1 T2 T3 T4
Kernel Threads: |
|
K1
Diagram:
User Threads: T1 T2 T3 T4
Kernel Threads: K1 K2 K3 K4
Diagram:
User Threads: T1 T2 T3 T4 T5 T6
Kernel Threads: K1 K2 K3
3. Comparison of Models
Feature Many-to-One One-to-One Many-to-Many
Blocking Issues Entire process Only one thread Only one thread
blocks blocks blocks
5. Hybrid Approaches
Modern operating systems often use hybrid threading models combining features of the
above approaches.
● Example:
○ Windows OS uses the One-to-One model by default but supports thread pools
to optimize performance.
○ Java Virtual Machine (JVM) maps Java threads to native OS threads.
6. Key Takeaways
● Multithreading models define how user threads interact with kernel threads, enabling
effective CPU utilization.
● Many-to-One is simple but lacks parallelism and scalability.
● One-to-One supports true parallelism but can be resource-intensive.
● Many-to-Many combines efficiency and scalability, making it ideal for high-performance
applications.
● Multithreading plays a crucial role in modern programming, especially for multicore
systems and distributed applications requiring concurrency.
Thread Libraries
Thread libraries provide APIs (Application Programming Interfaces) to create, manage, and
synchronize threads in applications. They allow developers to write multithreaded programs
by abstracting thread operations, including creation, synchronization, and termination.
Key Features:
Example:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pthread_t thread;
return 0;
}
Key Features:
● Highly integrated with the Windows operating system.
● Supports thread management, synchronization, and signaling through Win32 APIs.
● Allows direct interaction with Windows GUI applications.
Example:
#include <windows.h>
#include <stdio.h>
int main() {
HANDLE thread;
DWORD threadID;
CloseHandle(thread);
return 0;
}
Key Features:
Example:
Key Features:
Example:
#include <omp.h>
#include <stdio.h>
int main() {
#pragma omp parallel
{
printf("Thread %d says: Hello, OpenMP!\n", omp_get_thread_num());
}
return 0;
}
Key Features:
Example:
#include <tbb/tbb.h>
#include <iostream>
void parallel_task(int i) {
std::cout << "Processing task " << i << "\n";
}
int main() {
tbb::parallel_for(0, 10, parallel_task); // Parallel loop
return 0;
}
Key Features:
Example (Swift):
[Link]().async {
print("Task executed in background thread")
}
4. Key Takeaways
1. Pthreads and WinThreads are low-level, system-specific libraries suited for
fine-grained thread management.
2. Java Threads provide platform independence and ease of use but may lack low-level
control.
3. OpenMP and Intel TBB simplify parallel programming for high-performance
computing.
4. Grand Central Dispatch (GCD) is ideal for Apple devices, focusing on simplicity and
performance with minimal thread management.
Choosing the right library depends on the programming language, platform, and application
requirements (e.g., responsiveness, parallelism, or scalability).
Implicit Threading
Implicit threading refers to a programming approach where the responsibility of managing
threads (creation, scheduling, and synchronization) is delegated to libraries or compilers
rather than being explicitly handled by the programmer. It simplifies parallel programming by
hiding low-level thread management details, enabling efficient utilization of multicore and
multiprocessor systems.
1. Why Implicit Threading?
● Manual threading is complex: Programmers must explicitly create, manage, and
synchronize threads, which increases the chances of errors like deadlocks and race
conditions.
● Scalability: Implicit threading allows systems to dynamically optimize thread scheduling
based on available resources (multicore CPUs).
● Ease of development: Programmers focus on the task logic rather than low-level
threading details.
import [Link]
def task(n):
return n * n
#include <omp.h>
#include <stdio.h>
int main() {
#pragma omp parallel for
for (int i = 0; i < 10; i++) {
printf("Thread %d processing index %d\n", omp_get_thread_num(), i);
}
return 0;
}
[Link]().async {
print("Executed on a background thread")
}
[Link] {
print("Executed on the main thread")
}
● Concept:
○ The parent thread creates multiple child threads (fork) to execute subtasks in
parallel.
○ After task completion, the threads join back into the parent thread.
● Advantages:
○ Allows splitting tasks into smaller parallel subtasks, improving performance.
● Example in Java:
import [Link].*;
Task(int n) {
this.n = n;
}
#include <tbb/tbb.h>
#include <iostream>
int main() {
tbb::parallel_for(0, 10, [](int i) {
std::cout << "Task " << i << "\n";
});
return 0;
}
3. Comparison of Approaches
Approach Features Platform
Fork-Join Model Recursive parallel tasks with join points Java, JVM-based
languages
5. Key Takeaways
● Implicit threading abstracts thread management, allowing programmers to focus on
task decomposition rather than low-level synchronization.
● Approaches like Thread Pools, OpenMP, GCD, and Fork-Join Models enable
parallelism and multithreading efficiently.
● These methods are widely used in high-performance computing, data analytics, and
distributed systems to optimize performance on multicore processors.
Threading Issues
Multithreading can improve performance and responsiveness, but it also introduces several
complex issues related to synchronization, resource sharing, and deadlocks. Proper
handling of these problems is crucial for developing robust multithreaded programs.
● Definition: Occurs when multiple threads access shared data simultaneously, and the
final outcome depends on the execution order of threads.
● Problem: Unpredictable behavior and data corruption.
● Example:
import threading
counter = 0
def increment():
global counter
for _ in range(100000):
counter += 1
t1 = [Link](target=increment)
t2 = [Link](target=increment)
[Link]()
[Link]()
[Link]()
[Link]()
● Definition: Occurs when two or more threads are waiting for each other to release
resources, leading to a circular wait where no thread can proceed.
● Problem: The program freezes and fails to make progress.
● Example:
import threading
lock1 = [Link]()
lock2 = [Link]()
def thread1():
[Link]()
[Link]() # Thread 1 waits for lock2
[Link]()
[Link]()
def thread2():
[Link]()
[Link]() # Thread 2 waits for lock1
[Link]()
[Link]()
t1 = [Link](target=thread1)
t2 = [Link](target=thread2)
[Link]()
[Link]()
[Link]()
[Link]()
● Solution:
○ Avoid Nested Locks: Minimize lock dependencies.
○ Lock Ordering: Acquire locks in a consistent order.
○ Timeouts: Use timeouts to break deadlock cycles.
1.3 Starvation
● Definition: Occurs when a thread is perpetually denied access to a resource because
other threads with higher priority monopolize it.
● Problem: Lower-priority threads never execute.
● Example:
○ High-priority threads continuously acquire locks, preventing low-priority threads
from running.
● Solution:
○ Use priority inversion techniques like priority inheritance to temporarily boost
the priority of waiting threads.
1.4 Livelock
● Definition: Similar to deadlock, but threads keep changing their state in response to
each other without making progress.
● Problem: Threads appear active, but no meaningful work is done.
● Example:
○ Two threads continuously release and reacquire locks without resolving
resource contention.
● Solution:
○ Introduce random delays or timeouts to prevent continuous retries.
● Definition: Frequent switching between threads causes overhead due to saving and
restoring states, leading to performance degradation.
● Problem: High switching costs in CPU-bound tasks.
● Solution:
○ Use thread pooling and minimize unnecessary thread creation.
○ Optimize thread scheduling.
import threading
lock = [Link]()
counter = 0
def increment():
global counter
for _ in range(100000):
with lock: # Enforcing synchronization
counter += 1
● Solution:
○ Use locks, semaphores, or condition variables to enforce synchronization.
lock = [Link]()
with lock: # Ensures only one thread accesses critical section
shared_data += 1
2.2 Semaphores
def worker():
[Link]()
print("Working")
[Link]()
● Use condition variables to allow threads to wait and signal each other.
● Example:
import threading
cond = [Link]()
data_ready = False
def producer():
global data_ready
with cond:
data_ready = True
[Link]() # Signal waiting threads
def consumer():
global data_ready
with cond:
while not data_ready:
[Link]() # Wait until signaled
def task(n):
return n * n
with ThreadPoolExecutor(max_workers=4) as executor:
results = [Link](task, [1, 2, 3, 4])
print(list(results))
lock = Lock()
counter = 0
def increment():
global counter
with lock:
counter += 1
3. Key Takeaways
1. Common threading issues like race conditions, deadlocks, and priority inversion
require careful handling.
2. Use synchronization tools (locks, semaphores, condition variables) to ensure mutual
exclusion and thread coordination.
3. Thread pools and implicit threading models simplify thread management and reduce
overhead.
4. Proper debugging techniques, such as logging and thread analyzers, help identify and
resolve threading issues efficiently.
○ Time from process submission to the first response (i.e., start of execution).
○ Goal: Minimize response time for interactive systems.
6. Fairness
● Principle:
○ Processes are scheduled in the order of arrival (FIFO queue).
● Advantages:
○ Simple and easy to implement.
● Disadvantages:
○ Non-preemptive, leading to convoy effects (shorter processes waiting for
longer ones).
● Example:
Process | Arrival Time | Burst Time | Completion Time | Waiting Time | Turnaround Time
P1 |0 |5 |5 |0 |5
P2 |1 |3 |8 |4 |7
P3 |2 |8 | 16 |6 | 14
● Principle:
○ Selects the process with the shortest burst time.
● Types:
○ Non-preemptive: Executes until completion.
○ Preemptive (SRTF - Shortest Remaining Time First): Can be interrupted by a
shorter process.
● Advantages:
○ Optimal for minimizing waiting time.
● Disadvantages:
○ Requires knowledge of burst time (difficult in real systems).
○ May cause starvation for long processes.
● Example (Non-preemptive):
● Principle:
○ Processes are assigned priorities, and the highest priority process executes
first.
● Types:
○ Preemptive: Higher-priority processes can interrupt lower-priority ones.
○ Non-preemptive: Once started, a process cannot be interrupted.
● Advantages:
○ Suitable for time-critical tasks.
● Disadvantages:
○ May cause starvation for low-priority processes.
● Solution:
○ Use aging to gradually increase the priority of waiting processes.
● Principle:
○ Each process gets a fixed time slice (quantum) in a cyclic order.
● Preemptive:
○ A process is interrupted if it exceeds the time quantum.
● Advantages:
○ Ideal for time-sharing systems.
○ Ensures fairness among processes.
● Disadvantages:
○ Performance depends on the time quantum.
○ Too small quantum → High context switching overhead.
○ Too large quantum → Behaves like FCFS.
● Example: Quantum = 4
Process | Arrival Time | Burst Time | Completion Time | Waiting Time | Turnaround Time
P1 |0 | 10 | 20 | 10 | 20
P2 |1 |5 | 11 |5 | 10
P3 |2 |8 | 18 |8 | 16
● Principle:
○ Processes are classified into multiple queues based on priority (foreground,
background).
○ Each queue has its own scheduling algorithm (e.g., RR for foreground, FCFS
for background).
● Advantages:
○ Suitable for systems with different types of tasks (interactive vs batch).
● Disadvantages:
○ Can lead to starvation for lower-priority queues.
● Principle:
○ Preemptive version of SJF where the process with the smallest remaining
burst time is executed.
● Advantages:
○ Minimizes waiting time and turnaround time.
● Disadvantages:
○ Requires precise burst time estimation.
3. Comparison of Algorithms
Algorithm Preemptive Starvation Suitable for
? Possible?
4. Key Takeaways
● CPU scheduling optimizes process execution by prioritizing tasks based on specific
criteria.
● Algorithms like FCFS, SJF, and Priority Scheduling suit batch processing, while
Round Robin is ideal for interactive systems.
● Multilevel Feedback Queues adapt to dynamic workloads, providing flexibility and
fairness.
● The choice of algorithm depends on system requirements, including response time,
throughput, and fairness.
Thread Scheduling
Thread Scheduling determines the execution order of threads in a multithreaded
environment. It involves deciding which thread should execute at any given time, ensuring
efficient CPU utilization and responsiveness.
● Scope: Scheduling decisions are made across all threads in the system.
● Purpose: Competes for CPU among all threads in the system, not just threads in a
single process.
● Implementation: Managed by the operating system (kernel) for kernel-level threads.
● Example: Kernel-level threads in Linux and Windows.
2. Scheduling Models
2.1 Preemptive Scheduling
● Once a thread starts execution, it cannot be interrupted until it voluntarily yields control.
● Suitable for batch processing systems and environments where task switching costs
must be minimized.
● Threads are assigned priorities, and the highest-priority thread executes first.
● Can be preemptive or non-preemptive.
● Pros: Suitable for time-critical tasks.
● Cons: May lead to starvation for low-priority threads.
● Threads are divided into multiple queues based on their priority or type.
● Each queue can have its own scheduling algorithm (e.g., RR for interactive threads,
FCFS for background tasks).
● Pros: Efficient handling of different thread types.
● Cons: May cause starvation in lower-priority queues.
● Similar to multilevel queue scheduling, but threads can move between queues based
on behavior (e.g., CPU-bound or I/O-bound).
● Pros: Highly adaptive and prevents starvation.
● Cons: Complex to implement and manage.
5. Real-World Implementations
5.1 Windows Thread Scheduling
○ Threads may interfere with each other while accessing shared resources.
○ Solution: Use mutex locks and semaphores for synchronization.
2. Deadlocks
7. Key Takeaways
● Thread scheduling improves CPU utilization and responsiveness in multithreaded
systems.
● Algorithms like Round Robin, Priority Scheduling, and Multilevel Feedback Queues
cater to different workloads and priorities.
● Kernel-level scheduling handles threads at the system level, while user-level
scheduling works within processes.
● Modern OS schedulers like Linux CFS and Windows priority-based schedulers
ensure fairness, efficiency, and real-time support.
● All processors share a common memory and run identical copies of the operating
system.
● Scheduling:
○ Any processor can schedule any thread or process.
○ Threads may migrate between processors.
● Advantages:
○ Balanced load and high availability.
● Disadvantages:
○ Requires complex scheduling algorithms to prevent contention for shared
resources.
● A single physical chip contains multiple cores, each capable of executing threads.
● Challenges:
○ Requires fine-grained scheduling algorithms to exploit parallelism.
○ Must handle shared cache and memory access effectively.
2. Scheduling Approaches
2.1 Load Sharing
● Concept: Groups of related threads (e.g., threads of the same process) are scheduled
together on multiple processors.
● Goal: Minimize context switching and synchronization overhead.
● Advantage: Suitable for parallel applications.
● Disadvantage: Complex to implement in heterogeneous systems.
● A global queue maintains tasks, and any available processor can pick a task.
● Advantages:
○ Improves load balancing.
● Disadvantages:
○ Introduces task migration overhead and cache performance issues.
Round Robin Each process gets a time Fair and avoids High context-switching
(RR) quantum in a cyclic manner. starvation. overhead.
Shortest Job Selects the process with the Minimizes Needs accurate
Next (SJN) shortest burst time. waiting time. burst-time prediction.
5. Key Challenges
1. Load Balancing
○ Ensuring all processors see the same data in shared memory, preventing stale
data issues.
3. Synchronization
6. Key Takeaways
● Multiple processor scheduling optimizes CPU utilization by distributing workloads
across multiple processors or cores.
● Techniques like processor affinity, load balancing, and gang scheduling improve
performance and fairness.
● Real-time systems rely on partitioned and global scheduling approaches to meet
deadlines.
● Modern OS schedulers, such as Linux CFS and Windows Priority Scheduling, handle
parallelism effectively while addressing scalability and synchronization challenges.
● Static priority algorithm where shorter period tasks have higher priority.
● Assumptions:
○ Tasks are periodic and independent.
○ Deadlines are equal to periods.
● Pros: Simple and widely used for hard real-time systems.
● Cons: Assumes fixed priorities and may lead to low CPU utilization.
● Dynamic priority algorithm where tasks with earliest deadlines get higher priority.
● Pros: Maximizes CPU utilization and supports dynamic workloads.
● Cons: Overhead due to dynamic priority calculations.
● Similar to RMS, but assigns priorities based on shortest deadlines instead of periods.
● Pros: Suitable for tasks with unequal deadlines.
● Cons: Requires careful analysis of deadlines and resource usage.
○ Separates real-time tasks and non-real-time tasks into different queues for
better performance.
6. Key Challenges in Real-Time Scheduling
1. Priority Inversion
7. Real-World Applications
1. Automotive Systems – Engine control units (ECUs) use RMS for task scheduling.
2. Aerospace Systems – EDF ensures safety-critical systems meet deadlines.
3. Multimedia Systems – Soft real-time algorithms prioritize video and audio playback.
4. Industrial Automation – Robots and sensors rely on RTOS-based scheduling for
precise timing.
5. Medical Devices – Life-critical equipment like pacemakers use priority scheduling.
8. Key Takeaways
● Real-time CPU scheduling ensures processes meet strict deadlines for critical tasks.
● Static algorithms like Rate-Monotonic Scheduling (RMS) are suitable for predictable
workloads, while dynamic algorithms like EDF handle dynamic environments.
● Handling issues like priority inversion and overhead is critical to maintaining system
reliability.
● Modern RTOS platforms (e.g., FreeRTOS, RTLinux) implement preemptive
priority-based scheduling to meet real-time requirements.
1. Deadlock Characterization
To better understand deadlocks, we need to characterize the following conditions that must be
present for a deadlock to occur. These conditions are mutually exclusive, meaning all must be
true simultaneously for deadlock to arise.
There are four necessary conditions for a deadlock to occur, commonly referred to as the
Coffman conditions (after the researchers who identified them):
○ At least one resource is held in a non-shareable mode. Only one process can
use a resource at any given time.
○ If another process requests the same resource, it must wait until the resource is
released.
2. Hold and Wait
○ A set of processes exists, where each process is waiting for a resource that is
held by the next process in the cycle.
○ This creates a circular chain of dependencies, with no process being able to
proceed.
● Nodes:
○ Processes: Represented by circles.
○ Resources: Represented by squares.
● Edges:
○ Request Edge: A directed edge from process P to resource R, indicating that P
is requesting R.
○ Assignment Edge: A directed edge from resource R to process P, indicating that
R is allocated to P.
A deadlock occurs when there is a cycle in the resource allocation graph (RAG), where
processes and resources are mutually waiting for each other in a circular fashion.
3. Example of Deadlock
Consider a system with two processes and two resources:
To detect deadlocks, the system can periodically check the state of the resource allocation
graph and look for cycles.
4.2 Recovery
Once deadlock is detected, the system needs to take action to recover from it:
1. Process Termination: Kill one or more processes involved in the deadlock.
○ Abort all processes: This ensures that no other process is affected, but may be
expensive.
○ Abort one process at a time: Gradually reduce the deadlock, but may lead to
some resources being wasted.
2. Resource Preemption: Take resources away from one or more processes.
5. Deadlock Avoidance
Deadlock avoidance strategies aim to prevent the system from entering a deadlock state by
dynamically checking resource allocation requests.
The Banker's Algorithm is a deadlock avoidance algorithm used in systems where the
maximum resource requirements are known in advance. It checks whether the system is in a
safe state before allocating resources to a process.
● Safe State: A state is considered safe if there exists a sequence of processes such that
each process can obtain all the resources it needs without causing a deadlock.
● Unsafe State: A state is unsafe if no such sequence exists.
● Before granting a resource request, the system checks whether granting the request will
keep the system in a safe state.
● If the system would enter an unsafe state, the request is denied until resources become
available.
6. Deadlock Prevention
Deadlock prevention strategies aim to eliminate one of the Coffman conditions to prevent
deadlocks from occurring. These strategies include:
○ Processes must request all resources they need at once, before beginning
execution.
○ This approach can lead to inefficiencies, as processes might end up waiting for
resources they may never use.
3. Eliminate No Preemption:
○ Resources can be preempted from processes that hold them. However, this can
lead to complex recovery mechanisms and increased overhead.
4. Eliminate Circular Wait:
○ Impose a linear ordering of resources, where each process can only request
resources in an increasing order of their resource identifiers.
○ This prevents a circular wait but may result in less efficient resource usage.
7. Key Takeaways
● Deadlock occurs when all four Coffman conditions are met: mutual exclusion, hold
and wait, no preemption, and circular wait.
● Resource Allocation Graphs (RAG) are a powerful tool for detecting deadlocks.
● Deadlock can be managed using detection, recovery, avoidance, or prevention
techniques.
● Banker's Algorithm is a well-known deadlock avoidance method based on safe state
analysis.
● Deadlock prevention strategies involve eliminating one of the four necessary conditions
for deadlock.
Each of these approaches has its strengths and trade-offs depending on the application and
system requirements.
1. Deadlock Prevention
Deadlock prevention seeks to ensure that at least one of the Coffman conditions (the four
necessary conditions for deadlock) is violated, thereby guaranteeing that deadlock cannot
occur.
1.1 Strategies for Deadlock Prevention
○ A process must request all the resources it needs at once before it begins
execution. If a process is holding resources and needs additional resources, it
must release the ones it holds before requesting more.
○ Problem: This approach can be inefficient, as processes may end up waiting for
resources they may never use.
● Eliminate No Preemption
○ If a process holds a resource and is waiting for another resource, the system can
preempt the resource from the process and allocate it to another process. The
preempted process can later resume execution.
○ Problem: Preempting resources can be complex, especially if it involves rolling
back processes or managing data consistency.
● Eliminate Circular Wait
2. Deadlock Avoidance
Deadlock avoidance attempts to ensure that the system never enters an unsafe state. It
requires knowledge of the future resource requirements of processes, allowing the system
to make decisions that avoid deadlocks.
The Banker's Algorithm is the most commonly used deadlock avoidance algorithm. It works by
simulating the allocation of resources and checking if granting the request will leave the system
in a safe state. A safe state is one where there is a sequence of processes that can complete
without leading to a deadlock.
● Safe State: A state is considered safe if there exists a sequence of processes such that
each process can eventually obtain the resources it needs, execute, and then release
the resources.
● Unsafe State: A state is unsafe if no such sequence exists, potentially leading to
deadlock.
The Banker’s Algorithm checks whether a request can be granted without violating the
system's safety, using the following steps:
1. Check if the request can be granted without leaving the system in an unsafe state.
2. If the request can be granted, simulate the resource allocation and check if the system
remains in a safe state.
3. If it does not remain in a safe state, deny the request.
Pros:
Cons:
In systems where deadlock prevention or avoidance is not feasible, deadlock detection and
recovery strategies are used.
Deadlock detection algorithms periodically check the system for deadlocks. The most common
method for detecting deadlocks is using the Resource Allocation Graph (RAG).
● Resource Allocation Graph: It models the system’s processes and resources. The
system is in a deadlock state if there is a cycle in the graph, i.e., a set of processes that
are cyclically waiting for each other.
● Detection Algorithm: The system checks the graph at regular intervals for cycles. If a
cycle is found, a deadlock has occurred.
In addition to RAG, some systems use wait-for graphs or resource allocation matrices to
track processes and resources.
Pros:
● Can be used in dynamic systems where resource needs are unpredictable.
● Suitable for soft real-time systems where some deadlocks can be tolerated.
Cons:
Once deadlock is detected, the system must recover from it. Two primary methods are used for
recovery:
○ One or more processes involved in the deadlock are terminated to break the
circular wait.
■ Abort all processes: This is the simplest approach but may lead to
significant overhead.
■ Abort one process at a time: Processes are aborted in stages until the
deadlock is resolved. This approach minimizes the impact on the system
but requires careful management to avoid starving important processes.
2. Resource Preemption
○ Resources are preempted from one or more processes and given to others.
Preempted processes are then resumed later.
○ This can involve rolling back a process to a safe state to ensure consistency.
3. Problems:
In some cases, particularly in systems where deadlocks are rare or unlikely to cause serious
issues, the system may ignore the possibility of deadlocks entirely. This approach is called the
Ostrich Algorithm, inspired by the behavior of an ostrich, which buries its head in the sand to
avoid danger.
Pros:
Cons:
● Not suitable for critical applications where deadlocks can lead to major issues.
Key Takeaways
● Deadlock Prevention: Aims to prevent deadlock by violating at least one of the four
Coffman conditions.
● Deadlock Avoidance: Dynamically checks resource allocation to ensure that deadlock
is never allowed to happen (e.g., Banker's Algorithm).
● Deadlock Detection and Recovery: Periodically checks for deadlocks and takes action
(e.g., terminating processes or preempting resources).
● Ignore the Problem: In some cases, especially in non-critical applications, deadlocks
are simply ignored due to their rare occurrence or low impact.
Each method has trade-offs in terms of system complexity, performance, and safety, and the
appropriate choice depends on the specific application and the resources available.
Each process is assigned a starting address and a length (size) for the block, and the
operating system keeps track of this assignment.
● In fixed partitioning, the memory is divided into fixed-size partitions, and each partition
holds exactly one process.
● Drawback: This can lead to internal fragmentation when processes do not exactly fit
into the partitions.
● If the size of a process changes dynamically (due to growing stack or heap), it may
overflow the allocated memory block.
● Relocation becomes necessary if a process needs to grow in size, requiring memory
reallocation and possible migration to a different block, which can be inefficient.
While contiguous memory allocation suffers from fragmentation and inflexibility, several methods
can help mitigate these issues:
5.1 Compaction
● Paging involves dividing memory into fixed-sized pages and processes into fixed-sized
page frames. Instead of allocating contiguous blocks of memory, pages can be
scattered across physical memory. This avoids external fragmentation and simplifies
memory allocation.
● Note: Paging, though often considered in contrast to contiguous allocation, is a more
advanced memory management technique that eliminates fragmentation by allowing
non-contiguous allocation.
5.3 Segmentation
● Segmentation divides a process’s memory into logical segments (such as code, data,
stack), and each segment may be allocated as a contiguous block. This method is more
flexible than contiguous memory allocation but still suffers from fragmentation if not
handled properly.
Let’s consider a system with 3 processes (P1, P2, and P3) and 10 KB of memory.
● First Fit:
○ Allocates the first available block of memory that is large enough to satisfy the
request.
○ Advantage: Simple and fast.
○ Disadvantage: Can lead to fragmentation if small, unused memory blocks are
scattered around.
● Best Fit:
○ Allocates the smallest available block of memory that is large enough to satisfy
the request.
○ Advantage: Reduces wasted space and may lead to fewer fragmentation
problems.
○ Disadvantage: Can lead to small unusable fragments that are hard to allocate.
● Worst Fit:
○ Allocates the largest available block, leaving the remaining unused space as
large as possible.
○ Advantage: Attempts to minimize the number of small, unused blocks.
○ Disadvantage: Can result in larger gaps in memory, leading to inefficient usage
of space.
8. Key Takeaways
Despite its simplicity and efficiency in certain situations, contiguous memory allocation is
typically replaced by more sophisticated memory management techniques in modern operating
systems, especially as systems grow more complex.
1. Overview of Swapping
In a system that supports swapping, the operating system can move processes between RAM
and disk. When a process is swapped out to disk, it is saved to a swap space, and when
needed, it can be swapped back into main memory.
● Swap Space: A reserved area on the secondary storage (hard drive or SSD) where
processes are stored when they are not in use.
● Swap Out: Moving a process from RAM to the swap space.
● Swap In: Moving a process from swap space back into RAM when it is needed.
Swapping is particularly useful in systems with limited physical memory where the system needs
to support multiple processes that exceed the available RAM.
2. Types of Swapping
In simple swapping, the entire process is swapped in and out of memory as a single block.
When a process is swapped out, it is saved in swap space, and when it is swapped in, it is
moved back into memory.
Advantages:
Disadvantages:
● High I/O overhead because large portions of memory are moved in and out of storage.
● Can lead to significant performance degradation due to the need to frequently swap
processes.
In demand paging, only the pages (smaller units of a process's memory) that are needed at a
particular time are swapped in or out, rather than the entire process. This allows more
fine-grained control over memory allocation and can improve system performance.
● Pages: The memory is divided into fixed-sized blocks called pages. Only the pages that
are actively used are swapped in and out.
● Page Faults: A page fault occurs when a process tries to access a page that is not
currently in memory, triggering the operating system to swap the page in from the disk.
Advantages:
● Reduces the overhead compared to simple swapping by moving only the necessary
parts of the process.
● Can improve memory utilization and reduce the amount of swapping required.
Disadvantages:
Operating systems that support swapping allow the system to run processes that require more
memory than is physically available by swapping out less important or idle processes. Here's
how the basic swapping mechanism works in these systems:
1. Process Selection: When there is not enough free memory, the operating system
selects a process to swap out. The process that is swapped out is typically the one that
is not actively using the CPU or has the lowest priority.
2. Save Process State: The state of the process (e.g., CPU registers, program counter,
memory contents) is saved to disk in the swap space.
3. Free Memory: The memory occupied by the swapped-out process is now available for
other processes that need it.
4. Swap In: When a swapped-out process is required (for example, it is scheduled to run),
it is loaded back into memory from the swap space.
5. Process Resumption: Once the process is back in memory, the operating system
restores its state, and the process resumes execution from where it left off.
4. Advantages of Swapping
● Efficient Use of Memory: Swapping allows the system to handle more processes than
can fit in physical memory at once, increasing the overall utilization of memory
resources.
● Multiprogramming: It supports multiprogramming, enabling the system to run multiple
processes concurrently, even if their collective memory requirements exceed available
RAM.
● Flexibility: The system can prioritize which processes remain in memory, swapping out
less important ones to keep more critical processes running.
5. Disadvantages of Swapping
● I/O Overhead: The swapping process involves significant disk I/O operations, as data
must be written to and read from slower secondary storage (e.g., hard disk or SSD). This
can cause performance degradation, particularly when the system is swapping
frequently.
● Thrashing: If the system spends too much time swapping processes in and out of
memory instead of executing them, it is called thrashing. Thrashing occurs when there
is not enough free memory available, and the operating system continuously swaps
processes, causing a significant slowdown in system performance.
● Fragmentation: Over time, swap space can become fragmented, leading to inefficient
use of disk space.
While both swapping and paging are methods of moving data between main memory and
secondary storage, they differ in the level of granularity:
● Swapping moves entire processes in and out of memory, whereas paging involves
moving smaller units (pages) of a process in and out of memory.
● Paging allows more flexibility and more efficient memory utilization since only parts of a
process that are actively being used are moved, while swapping may involve moving
large blocks of memory unnecessarily.
Thrashing occurs when the operating system spends the majority of its time swapping
processes in and out of memory instead of executing them. This can happen when the system
doesn't have enough physical memory to meet the demands of active processes, leading to
excessive paging or swapping.
● Symptoms of Thrashing: The system becomes unresponsive, the CPU usage may
drop, and disk activity (especially swap space usage) increases significantly.
● Causes:
○ Too many processes are competing for memory.
○ The system is overloaded with processes that have large memory requirements.
○ Insufficient swap space to accommodate the processes.
To avoid thrashing, operating systems use various techniques, such as memory management
policies, optimal process scheduling, and dynamic adjustment of the number of
processes in the system.
8. Key Takeaways
● Swapping allows the system to handle processes whose memory requirements exceed
the available physical memory by moving processes between RAM and swap space on
disk.
● It can lead to performance degradation due to the high I/O overhead associated with
moving large blocks of memory between RAM and secondary storage.
● Demand Paging is a more efficient form of swapping, where only the pages that are
needed by the process are moved in and out of memory.
● Thrashing can occur if the system is constantly swapping processes and is a significant
issue to manage in memory-constrained systems.
Swapping is an important technique for enabling multitasking and supporting processes that
require more memory than the available physical resources. However, its effectiveness depends
on efficient memory management strategies and avoiding excessive swapping or thrashing.
1. Overview of Paging
Paging divides the memory into equal-sized blocks or pages, with each page having a
corresponding page frame in the physical memory. The size of a page is typically a power of 2
(e.g., 4 KB, 8 KB, etc.).
In a system that uses paging, a logical address generated by the CPU is divided into two parts:
● Page Number (p): Identifies which page within the process’s logical address space the
address belongs to.
● Offset (d): Identifies the exact location within the page where the data is located.
The logical address is then translated to a physical address using a page table. This
translation is done as follows:
1. Page Table Lookup: The page number is used as an index into the page table, which
contains the base address of each page frame in physical memory.
2. Physical Address Calculation: Once the page frame is found, the offset is added to the
base address of the page frame to generate the physical address.
For example:
The page table is a data structure that holds the mapping between a process’s logical pages
and the corresponding physical page frames in memory. Each entry in the page table typically
contains:
3. Advantages of Paging
● Paging allows easy swapping of pages between main memory and secondary storage
(swap space or disk), as each page is independent. This is especially useful for systems
with limited memory.
4. Disadvantages of Paging
● Internal fragmentation occurs if a process does not completely fill its last page. In this
case, the remaining space on the last page is wasted.
● Example: If a process requires 20 KB of memory, and the page size is 8 KB, it will be
allocated three pages (24 KB), leaving 4 KB of unused space within the last page.
● The operating system must maintain the page table, which consumes additional
memory. For large processes, this can become a significant overhead.
● Multi-level page tables and other advanced techniques are used to handle large page
tables more efficiently, but these add complexity.
● Memory access can be slower due to the need for two memory accesses for each
read/write operation:
1. First Access: To look up the frame number in the page table.
2. Second Access: To access the actual data in the frame.
To mitigate this, Translation Lookaside Buffers (TLBs) are used to cache recent page table
lookups, improving performance.
5. Types of Paging
● In simple paging, the process is divided into pages, and these pages are mapped to
physical memory using a page table. This is the most basic form of paging.
● In multi-level paging, large page tables are broken down into multiple levels (e.g., a
first-level page table, second-level page table, etc.). Each level of the page table points
to another page table or to the actual frame in memory. This reduces the amount of
memory needed to store the page tables for large address spaces.
Example: A 32-bit virtual address space may use a two-level page table:
In virtual memory systems that use paging, a page replacement algorithm is needed to
manage which pages are loaded into physical memory and which are swapped out when
memory becomes full. Common page replacement algorithms include:
● Pages are replaced in the order they were loaded into memory. The first page to enter
memory is the first to be swapped out.
● Disadvantage: FIFO may not always result in the best performance, as it doesn't take
into account which pages are being used frequently.
● The optimal algorithm replaces the page that will not be used for the longest time in the
future.
● Disadvantage: This is theoretical and cannot be implemented in practice because the
operating system cannot predict future memory accesses.
● The clock algorithm is a more efficient approximation of LRU. It uses a circular queue
with a pointer that moves around the pages in memory and replaces the first page that
has not been referenced recently.
Let’s consider a system with a page size of 4 KB and a process that needs 12 KB of memory.
8. Key Takeaways
● Paging allows efficient memory allocation by breaking the memory into fixed-size pages
and mapping them to available page frames in physical memory.
● It eliminates external fragmentation and reduces the complexity of memory allocation.
● Internal fragmentation can still occur due to incomplete filling of the last page.
● Page tables are used to map logical pages to physical frames, and algorithms like LRU
and FIFO manage page replacement when physical memory is full.
● TLBs (Translation Lookaside Buffers) are used to speed up address translation and
reduce memory access time.
Paging is widely used in modern operating systems, especially for virtual memory systems,
and plays a crucial role in enabling the execution of processes that require more memory than
the available physical RAM.
1. Overview of Segmentation
In a segmented memory system, a process is divided into different segments based on its
logical divisions. Each segment can be allocated a different length and can be placed anywhere
in physical memory. Segmentation is useful because it reflects the logical structure of a
program, which may consist of:
● Code Segment: The part of the process containing the executable instructions.
● Data Segment: The part of the process containing global and static variables.
● Stack Segment: The part of the process used for the stack, which stores function calls,
local variables, and return addresses.
● Heap Segment: The part of the process used for dynamically allocated memory (e.g.,
malloc() in C or new in C++).
Each segment has a segment base (starting address in memory) and a segment limit (the
length or size of the segment).
In segmentation, the logical address generated by the CPU is divided into two parts:
● Segment Number (s): Identifies which segment of the process the address belongs to.
● Offset (d): Identifies the exact location within the segment.
The logical address is translated into a physical address by the operating system. The
translation works as follows:
1. Segment Table Lookup: The segment number is used to look up the segment table.
The segment table contains an entry for each segment, with the base address of the
segment (where it is loaded in physical memory) and the limit (the size of the segment).
2. Physical Address Calculation: Once the segment is found, the physical address is
calculated by adding the offset to the base address of the segment.
If the offset exceeds the segment limit, a segmentation fault occurs, indicating that the
process has accessed memory outside its allocated segment.
3. Segment Table
The segment table is a data structure used to map logical segments to physical memory
addresses. Each entry in the segment table contains:
4. Advantages of Segmentation
● Segments are variable-sized, so they can grow or shrink based on the program’s needs
(e.g., dynamic memory allocation via the heap).
● Unlike paging, which causes fragmentation within fixed-size pages, segmentation avoids
internal fragmentation by allocating only the necessary amount of memory for each
segment.
4.3 Efficient Sharing
● Segments can be shared between processes more easily, as different processes can
map the same segment (e.g., shared libraries) into their own memory spaces.
● Segmentation allows fine-grained memory protection. For example, the code segment
can be marked as read-only, preventing accidental modification, while the stack
segment can be read/write.
5. Disadvantages of Segmentation
● Since segments are of variable size, memory becomes fragmented as segments are
allocated and deallocated. Over time, this can lead to external fragmentation, where
there are not enough contiguous blocks of free memory to allocate a new segment.
● The system may need to perform compaction to free up contiguous space, which is
time-consuming and inefficient.
● The operating system must maintain a segment table for each process. For processes
with many segments, the segment table can become large and incur overhead in
memory and processing time.
Segmentation
● Divides memory into variable-sized segments based on logical divisions of the program
(e.g., code, data, stack).
● Segments are allocated in non-contiguous blocks.
● Provides a logical view of memory.
● More flexible and natural for programming structures.
● Can suffer from external fragmentation.
Paging
● Divides memory into fixed-size pages and allocates fixed-size page frames in physical
memory.
● Pages are allocated in non-contiguous blocks.
● Provides a physical view of memory.
● Eliminates external fragmentation, but can suffer from internal fragmentation.
● Simpler to manage and more efficient for systems with many processes.
The segment table for this process might look like this:
Assume that a logical address generated by the CPU is (Segment Number: 1, Offset: 5 KB),
and the segment table shows that the base address for segment 1 (Data) is 2000 and the limit is
8 KB.
If the offset exceeds the segment limit (e.g., accessing 8.5 KB in the Data segment), a
segmentation fault occurs, as it exceeds the segment size.
8. Key Takeaways
● Segmentation divides a process’s memory into logically related segments (e.g., code,
data, stack), making memory allocation more flexible and efficient.
● Segment Tables map logical segments to physical memory addresses, providing a
logical view of memory.
● Advantages: Segmentation matches the natural structure of programs, offers easier
memory protection, and allows for flexible memory allocation.
● Disadvantages: Segmentation suffers from external fragmentation, can be complex to
manage, and incurs overhead due to the maintenance of segment tables.
● Comparison with Paging: While paging divides memory into fixed-size pages,
segmentation divides memory into variable-sized segments, which is more intuitive for
programmers but can lead to fragmentation.
Segmentation is used in systems where it is important to treat different parts of a process (such
as code, stack, and heap) as separate entities, and it is a useful mechanism for memory
protection and sharing in certain types of operating systems.
Demand Paging
Demand paging is a memory management scheme in which pages of a program are loaded
into memory only when they are needed (i.e., when the program accesses them). In this
system, the operating system does not load the entire process into memory at once. Instead, it
loads pages on demand, when the process references a page that is not currently in memory,
which can significantly reduce the amount of memory required to run a program.
Demand paging is commonly used in systems that implement virtual memory, where the total
amount of physical memory is less than the total memory required by the processes running on
the system.
Demand paging works in a way that when a process is started, only a few pages (typically, a
small initial portion) are loaded into physical memory. The rest of the pages are loaded only
when the process accesses them, via a mechanism called a page fault.
○ When a process is created, only the initial pages of the program (often the code
segment) are loaded into memory. The rest of the pages are left on secondary
storage (e.g., the disk).
2. Accessing a Page:
○ If the process attempts to access a page that is not currently in memory, a page
fault occurs. This is an event that signals to the operating system that a
requested page is not in memory.
3. Page Fault Handling:
○ The operating system handles the page fault by loading the required page from
secondary storage (e.g., disk) into memory. The OS also updates the page table
to reflect the new location of the page in memory.
4. Page Replacement:
○ If there is not enough free space in physical memory to load the page, the
operating system will use a page replacement algorithm (e.g., FIFO, LRU) to
swap out a page that is already in memory and free up space for the new page.
5. Resuming Execution:
○ Once the page has been loaded into memory, the CPU can resume execution of
the program, and the instruction that caused the page fault is retried, this time
successfully.
2. Page Faults
A page fault occurs when a program attempts to access a page that is not currently loaded into
memory. When a page fault happens, the operating system must load the page from secondary
storage (disk) into memory.
● Minor Page Fault: The page is not in physical memory, but it is found in swap space (a
designated area on disk where pages are stored when not in use). The page is loaded
quickly.
● Major Page Fault: The page is not in swap space either, so it must be read from disk
into memory, which is a slower operation.
The process of handling page faults can add overhead to a system, as reading from disk is
much slower than reading from memory.
3. Advantages of Demand Paging
● Demand paging reduces the memory required to run a program because not all pages
are loaded into memory at once. Only the pages that are actually used by the process
are brought into memory.
● This allows for better memory utilization and enables the system to run larger
applications than would be possible if all pages were loaded upfront.
● The program doesn't need to load all of its pages before execution. The system can start
executing the program with just a few pages, making the initial load time shorter.
● Demand paging is a key feature of virtual memory systems. It allows processes to use
more memory than is physically available in the system by swapping pages between
disk and memory as needed.
3.4 Flexibility
● Processes can be larger than physical memory and still run, as only parts of the
process are loaded at any given time. The system can manage multiple processes, even
if their combined memory requirements exceed the physical RAM.
● When a page fault occurs, there is a significant overhead due to the need to read the
page from disk. Disk I/O is much slower than memory access, so frequent page faults
can severely degrade system performance.
4.2 Thrashing
● If a system is constantly swapping pages in and out of memory (because the working set
of pages required by processes does not fit in physical memory), it can lead to
thrashing. In this condition, the system spends most of its time swapping pages rather
than executing processes, leading to very poor performance.
4.3 Increased Complexity
● Implementing demand paging requires careful management of page tables, page faults,
and page replacement algorithms. It introduces additional complexity for the operating
system, especially in terms of handling large numbers of processes and managing page
swapping efficiently.
In demand paging, when a page is needed but there is not enough space in physical memory to
load the new page, the operating system must choose an existing page to swap out of memory
to make room for the new page. The choice of which page to swap out is governed by a page
replacement algorithm. Common page replacement algorithms include:
● Pages are replaced in the order in which they were loaded into memory. The oldest page
(the one that has been in memory the longest) is swapped out first.
● The page that has not been used for the longest time is replaced. This algorithm
assumes that pages that have been recently used are more likely to be used again soon.
● The optimal algorithm selects the page that will not be needed for the longest period of
time in the future. While this algorithm offers the best possible performance, it is
impractical because it requires knowledge of future memory accesses, which is not
available.
● The clock algorithm is an approximation of the LRU algorithm. It uses a circular queue
and a pointer to keep track of the pages in memory, making it more efficient than LRU
while still maintaining a reasonable level of page replacement accuracy.
1. The process has 4 pages, and physical memory can hold 3 pages.
2. The operating system initially loads pages 1, 2, and 3 into memory.
3. The process accesses page 4, which is not in memory. A page fault occurs, and the OS
swaps out one of the existing pages (say, page 1) to make room for page 4.
4. The process accesses page 1 again (which was swapped out earlier). A page fault
occurs again, and the OS swaps another page out (say, page 2) to load page 1 back into
memory.
This process continues as more pages are accessed, with the operating system swapping
pages in and out of memory.
7. Key Takeaways
● Demand paging allows for efficient memory usage by loading pages into memory only
when they are needed, rather than loading the entire process into memory at once.
● Page faults are a critical part of demand paging. They occur when a process accesses
a page that is not in memory, and the OS must load the page from secondary storage
(disk).
● Demand paging is a core component of virtual memory systems, enabling processes to
run even if their total memory requirement exceeds the available physical memory.
● The main disadvantages of demand paging are the overhead of handling page faults
and the potential for thrashing if page swapping is excessive.
● Page replacement algorithms are essential for managing memory in demand paging
systems, ensuring that the right pages are kept in memory to optimize performance.
Demand paging allows systems to run programs larger than the available physical memory,
making it a fundamental technique in modern operating systems. However, careful management
of page faults and memory is essential to avoid performance degradation.
When multiple processes run in a system, the operating system must allocate frames
(fixed-size blocks of memory) to them efficiently. Frame allocation strategies help determine how
many frames each process gets.
1. Equal Allocation
2. Proportional Allocation
● Frames are allocated based on the size of each process.
● Larger processes get more frames; smaller ones get fewer.
● Example:
○ Total frames: 100
○ Process A (size: 10MB), Process B (size: 30MB), Process C (size: 60MB)
○ Frames allocated: A = 10, B = 30, C = 60
3. Priority-Based Allocation
● Global Allocation: A process can take frames from another process when needed.
More flexible but can lead to performance issues for some processes.
● Local Allocation: A process can only use the frames initially allocated to it. More
predictable but less adaptable.
● Allocates frames dynamically based on the working set (pages a process is actively
using).
● Helps reduce thrashing (excessive page faults).
● The OS enforces a minimum number of frames per process to avoid excessive page
faults.
● Example: If a system uses 4-page instructions, a process needs at least 4 frames to
execute without errors.
Thrashing occurs when a system spends more time swapping pages in and out of memory
than executing processes. This leads to severe performance degradation because the CPU
remains idle while waiting for memory operations to complete.
Causes of Thrashing:
1. High Multiprogramming Level: Too many processes in memory lead to frequent page
replacements.
2. Insufficient Frames: Each process gets fewer frames, causing frequent page faults.
3. Poor Page Replacement Strategy: Inefficient algorithms like FIFO can lead to
unnecessary page replacements.
4. Increase in Working Set Size: If processes demand more memory than available,
constant page faults occur.
Effects of Thrashing:
○ Keep track of the working set (recently used pages) of each process.
○ Ensure that enough frames are allocated to avoid excessive page faults.
2. Page Fault Frequency (PFF) Control
○ LRU (Least Recently Used) and OPT (Optimal) help in reducing unnecessary
replacements.
Thrashing Example:
Imagine a system with 100 frames and 10 processes, each requiring 15 frames to work
efficiently. Since the total requirement is 150 frames but only 100 are available, frequent page
faults occur as processes continuously replace each other's pages, leading to thrashing.
Mass storage refers to large-capacity non-volatile storage devices used to store vast
amounts of data permanently. The Operating System (OS) manages mass storage by
handling read/write operations, file organization, and performance optimization.
2. Storage Hierarchy in an OS
Mass storage is part of the storage hierarchy, which balances speed, cost, and capacity:
Since HDDs and SSDs handle multiple requests at once, disk scheduling optimizes data
retrieval:
FCFS (First Come, First Processes requests in arrival Simple, but slow
Served) order
SSTF (Shortest Seek Time Moves to the closest request Reduces seek time
First) first
SCAN (Elevator Algorithm) Moves back and forth across Good for heavy loads
the disk
🔹 SSDs vs HDDs: SSDs do not use moving parts, so disk scheduling is less critical in
SSDs. Instead, wear-leveling algorithms optimize SSD lifespan.
6. Storage Virtualization
● Logical Volume Management (LVM): Combines multiple storage devices into a single
logical unit.
● Cloud Storage: Uses remote servers for mass data storage (e.g., Google Drive, AWS
S3).
7. Conclusion
Mass storage is essential for long-term data retention. The OS manages mass storage using
disk scheduling, file systems, caching, and storage virtualization. Choosing the right
storage technology and management techniques improves system efficiency and
performance.