B.
TECH 3 rd year
SUBJECT : OPERATING SYSTEM
SUBJECT CODE :- BT CS 605A
Submitted To : Submitted By :
Dr. Benay Kumar Ray Parmod Kumar
(211572)
Program to Simulate Bankers Algorithm for Dead Lock
Avoidance and Prevention
def is_safe_state(available, allocation, max_need):
num_processes = len(allocation)
num_resources = len(available)
work = available[:]
finish = [False] * num_processes
safe_sequence = []
while True:
found = False
for i in range(num_processes):
if not finish[i] and all(need <= work for need, work in
zip(max_need[i], work)):
found = True
finish[i] = True
safe_sequence.append(i)
for j in range(num_resources):
work[j] += allocation[i][j]
break
if not found:
break
return all(finish), safe_sequence
if __name__ == "__main__":
allocation = [
[0, 1, 0],
[2, 0, 0],
[3, 0, 2],
[2, 1, 1],
[0, 0, 2]
]
max_need = [
[7, 5, 3],
[3, 2, 2],
[9, 0, 2],
[2, 2, 2],
[4, 3, 3]
]
available = [3, 3, 2]
safe, sequence = is_safe_state(available, allocation, max_need)
if safe:
print("The system is in a safe state.")
print("Safe sequence (processes in order):", [f"P{p}" for p in
sequence])
else:
print("The system is in an unsafe state. Deadlock may occur.")
# Finding the safe sequence
n = len(allocation)
m = len(available)
f = [0] * n
ans = [0] * n
ind = 0
for k in range(n):
f[k] = 0
need = [[0 for i in range(m)] for i in range(n)]
for i in range(n):
for j in range(m):
need[i][j] = max_need[i][j] - allocation[i][j]
for k in range(n):
for i in range(n):
if f[i] == 0:
flag = 0
for j in range(m):
if need[i][j] > available[j]:
flag = 1
break
if flag == 0:
ans[ind] = i
ind += 1
for y in range(m):
available[y] += allocation[i][y]
f[i] = 1
print("\nExplaining the Safe Sequence:")
print("The safe sequence of processes ensures that the system can
avoid deadlock and complete execution.")
print("Safe sequence (processes in order):", [f"P{p}" for p in
ans])
Output:
Program to simulate MVT and MFT memory
management techniques
MVT:
class MVTMemory:
def __init__(self, total_memory):
self.total_memory = total_memory
self.memory_map = [(0, total_memory)] # Initial memory map
with one free block spanning the entire memory
def allocate_memory(self, process_id, size):
"""Allocate memory for a process."""
for i, (start, end) in enumerate(self.memory_map):
if end - start >= size:
self.memory_map[i] = (start + size, end) # Update
free block
return start, start + size, True
return -1, -1, False # Allocation failed
def deallocate_memory(self, start):
"""Deallocate memory for a process."""
for i, (block_start, block_end) in enumerate(self.memory_map):
if block_start == start:
if i > 0 and block_start == self.memory_map[i - 1][1]:
# Merge with previous block if adjacent
self.memory_map[i - 1] = (self.memory_map[i - 1]
[0], block_end)
del self.memory_map[i]
else:
self.memory_map[i] = (block_start, block_start +
(block_end - block_start))
return True
return False # Deallocation failed
def print_memory_map(self):
"""Print the current memory map."""
print("Memory Map:")
print("Start\tEnd\tProcess ID")
for start, end in self.memory_map:
print(start, "\t", end)
# Example usage for MVT
if __name__ == "__main__":
total_memory = 1000
mvt_memory = MVTMemory(total_memory)
processes = [(1, 200), (2, 300), (3, 400)]
for process_id, size in processes:
start, end, success = mvt_memory.allocate_memory(process_id,
size)
if success:
print("Process", process_id, "allocated from", start,
"to", end)
else:
print("Failed to allocate memory for Process", process_id)
mvt_memory.print_memory_map()
mvt_memory.deallocate_memory(200)
mvt_memory.deallocate_memory(400)
mvt_memory.print_memory_map()
OUTPUT:
MFT:
class MFTMemory:
def __init__(self, total_memory, block_size):
self.total_memory = total_memory
self.block_size = block_size
self.num_blocks = total_memory // block_size
self.memory_map = [(i * block_size, (i + 1) * block_size,
None) for i in range(self.num_blocks)]
def allocate_memory(self, process_id, size):
"""Allocate memory for a process."""
required_blocks = size // self.block_size + (1 if size %
self.block_size != 0 else 0)
for i, (start, end, pid) in enumerate(self.memory_map):
if pid is None and i + required_blocks <= self.num_blocks:
allocated_blocks = []
for j in range(i, i + required_blocks):
allocated_blocks.append(j)
self.memory_map[j] = (self.memory_map[j][0],
self.memory_map[j][1], process_id)
return allocated_blocks, True
return [], False # Allocation failed
def deallocate_memory(self, allocated_blocks):
"""Deallocate memory for a process."""
for i in allocated_blocks:
self.memory_map[i] = (self.memory_map[i][0],
self.memory_map[i][1], None)
def print_memory_map(self):
"""Print the current memory map."""
print("Memory Map:")
print("Start\tEnd\tProcess ID")
for start, end, pid in self.memory_map:
print(start, "\t", end, "\t", pid if pid is not None else
"Free")
# Example usage for MFT
if __name__ == "__main__":
total_memory = 1000
block_size = 100
mft_memory = MFTMemory(total_memory, block_size)
processes = [(1, 200), (2, 300), (3, 400)]
for process_id, size in processes:
allocated_blocks, success =
mft_memory.allocate_memory(process_id, size)
if success:
print("Process", process_id, "allocated to blocks:",
allocated_blocks)
else:
print("Failed to allocate memory for Process", process_id)
mft_memory.print_memory_map()
mft_memory.deallocate_memory([0, 1, 2, 3, 4])
mft_memory.deallocate_memory([5, 6, 7])
mft_memory.print_memory_map()
Output:
Program to simulate page replacement algorithms
FIFO:
class FIFOPageReplacement:
def __init__(self, capacity):
self.capacity = capacity
self.pages = []
def page_fault(self, page):
"""Handle page faults using FIFO page replacement
algorithm."""
if page not in self.pages:
if len(self.pages) == self.capacity:
self.pages.pop(0) # Remove the oldest page
self.pages.append(page) # Add the new page
return True # Page fault occurred
return False # Page is already in memory
def display_pages(self):
"""Display pages currently in memory."""
print("Pages in memory:", self.pages)
# Example usage for FIFO
if __name__ == "__main__":
fifo = FIFOPageReplacement(3) # Capacity of 3 pages
pages = [1, 2, 3, 4, 1, 2]
for page in pages:
if fifo.page_fault(page):
print("Page", page, "caused a page fault")
else:
print("Page", page, "is already in memory")
fifo.display_pages()
OUTPUT:
LRU:
class LRUPageReplacement:
def __init__(self, capacity):
self.capacity = capacity
self.pages = []
def page_fault(self, page):
"""Handle page faults using LRU page replacement algorithm."""
if page not in self.pages:
if len(self.pages) == self.capacity:
self.pages.pop(0) # Remove the least recently used
page
self.pages.append(page) # Add the new page
return True # Page fault occurred
else:
self.pages.remove(page) # Move the accessed page to the
end (most recently used)
self.pages.append(page)
return False # Page is already in memory
def display_pages(self):
"""Display pages currently in memory."""
print("Pages in memory:", self.pages)
# Example usage for LRU
if __name__ == "__main__":
lru = LRUPageReplacement(3) # Capacity of 3 pages
pages = [1, 2, 3, 4, 1, 2]
for page in pages:
if lru.page_fault(page):
print("Page", page, "caused a page fault")
else:
print("Page", page, "is already in memory")
lru.display_pages()
OUTPUT:
LFU:
from collections import defaultdict
class LFUPageReplacement:
def __init__(self, capacity):
self.capacity = capacity
self.pages = []
self.frequency = defaultdict(int)
def page_fault(self, page):
"""Handle page faults using LFU page replacement algorithm."""
if page not in self.pages:
if len(self.pages) == self.capacity:
min_frequency_page = min(self.pages, key=lambda x:
self.frequency[x])
self.pages.remove(min_frequency_page)
del self.frequency[min_frequency_page]
self.pages.append(page)
self.frequency[page] += 1
return page not in self.pages
def display_pages(self):
"""Display pages currently in memory."""
print("Pages in memory:", self.pages)
# Example usage for LFU
if __name__ == "__main__":
lfu = LFUPageReplacement(3) # Capacity of 3 pages
pages = [1, 2, 3, 4, 1, 2]
for page in pages:
if lfu.page_fault(page):
print("Page", page, "caused a page fault")
else:
print("Page", page, "is already in memory")
lfu.display_pages()
OUTPUT:
ROUND ROBIN:
def round_robin(processes, burst_time, quantum):
n = len(processes)
remaining_burst_time = burst_time[:]
time = 0
waiting_time = [0] * n
turnaround_time = [0] * n
timeline = [] # Timeline to store the sequence of processes
executed
while True:
done = True
for i in range(n):
if remaining_burst_time[i] > 0:
done = False
if remaining_burst_time[i] > quantum:
timeline.append(processes[i]) # Add process to
timeline
time += quantum
remaining_burst_time[i] -= quantum
else:
timeline.append(processes[i]) # Add process to
timeline
time += remaining_burst_time[i]
waiting_time[i] = time - burst_time[i]
remaining_burst_time[i] = 0
if done:
break
for i in range(n):
turnaround_time[i] = burst_time[i] + waiting_time[i]
return waiting_time, turnaround_time, timeline
# Example usage:
processes = [1, 2, 3, 4]
burst_time = [8, 6, 1, 9]
quantum = 2
waiting_time, turnaround_time, timeline = round_robin(processes,
burst_time, quantum)
print("Round Robin Waiting Time:", waiting_time)
print("Round Robin Turnaround Time:", turnaround_time)
# Print Gantt Chart
print("\nGantt Chart:")
print("-" * 30)
print("|", end="")
for process in timeline:
print(f" P{process} |", end="")
print("\n" + "-" * 30)
print("|", end="")
for i in range(len(timeline)):
print(f" {i * quantum:2d} |", end="")
print("\n" + "-" * 30)
OUTPUT:
Shortest Job First(SJF):
def sjf(processes, burst_time):
n = len(processes)
waiting_time = [0] * n
turnaround_time = [0] * n
timeline = [] # Timeline to store the sequence of processes
executed
# Sort processes based on burst time
sorted_processes = sorted(range(n), key=lambda i: burst_time[i])
# Calculate waiting time for each process
for i in range(1, n):
waiting_time[sorted_processes[i]] = sum(burst_time[:i])
# Calculate turnaround time for each process
for i in range(n):
turnaround_time[sorted_processes[i]] =
burst_time[sorted_processes[i]] + waiting_time[sorted_processes[i]]
# Generate simplified timeline
timeline.append(processes[sorted_processes[0]])
for i in range(1, n):
if processes[sorted_processes[i]] !=
processes[sorted_processes[i-1]]:
timeline.append(processes[sorted_processes[i]])
return waiting_time, turnaround_time, timeline
def print_table(processes, burst_time, waiting_time, turnaround_time):
print("Process\tBurst Time\tWaiting Time\tTurnaround Time")
for i in range(len(processes)):
print(f"P{processes[i]}\t{burst_time[i]}\t\t{waiting_time[i]}\
t\t{turnaround_time[i]}")
# Example usage:
processes = [1, 2, 3, 4]
burst_time = [8, 6, 1, 9]
waiting_time, turnaround_time, timeline = sjf(processes, burst_time)
print("SJF Waiting Time:", waiting_time)
print("SJF Turnaround Time:", turnaround_time)
print("\nProcess Table:")
# Print table of processes, burst time, waiting time, and turnaround
time
print_table(processes, burst_time, waiting_time, turnaround_time)
# Print Simplified Gantt Chart
print("\nSimplified Gantt Chart:")
print("-" * 30)
print("|", end="")
prev_process = timeline[0]
for process in timeline:
if process != prev_process:
print(f" P{prev_process} |", end="")
prev_process = process
print(f" P{prev_process} |", end="")
print("\n" + "-" * 30)
print("|", end="")
for i in range(len(timeline)):
print(f" {i:2d} |", end="")
print("\n" + "-" * 30)
OUTPUT:
First Come First Serve(FCFS):
def fcfs(processes, burst_time):
n = len(processes)
waiting_time = [0] * n
turnaround_time = [0] * n
timeline = [] # Timeline to store the sequence of processes
executed
# Calculate waiting time for each process
for i in range(1, n):
waiting_time[i] = sum(burst_time[:i])
# Calculate turnaround time for each process
for i in range(n):
turnaround_time[i] = burst_time[i] + waiting_time[i]
# Generate simplified timeline
timeline.append(processes[0])
for i in range(1, n):
if processes[i] != processes[i-1]:
timeline.append(processes[i])
return waiting_time, turnaround_time, timeline
# Example usage:
processes = [1, 2, 3, 4]
burst_time = [8, 6, 1, 9]
waiting_time, turnaround_time, timeline = fcfs(processes, burst_time)
print("FCFS Waiting Time:", waiting_time)
print("FCFS Turnaround Time:", turnaround_time)
# Print Simplified Gantt Chart
print("\nSimplified Gantt Chart:")
print("-" * 30)
print("|", end="")
prev_process = timeline[0]
for process in timeline:
if process != prev_process:
print(f" P{prev_process} |", end="")
prev_process = process
print(f" P{prev_process} |", end="")
print("\n" + "-" * 30)
print("|", end="")
for i in range(len(timeline)):
print(f" {i:2d} |", end="")
print("\n" + "-" * 30)
OUTPUT:
PRIORITY:
def priority_non_preemptive(processes, burst_time, priority):
n = len(processes)
waiting_time = [0] * n
turnaround_time = [0] * n
timeline = [] # Timeline to store the sequence of processes
executed
# Sort processes based on priority
sorted_processes = sorted(range(n), key=lambda x: priority[x])
# Calculate waiting time for each process
waiting_time[sorted_processes[0]] = 0
for i in range(1, n):
waiting_time[sorted_processes[i]] =
burst_time[sorted_processes[i - 1]] + waiting_time[sorted_processes[i
- 1]]
# Calculate turnaround time for each process
for i in range(n):
turnaround_time[i] = burst_time[i] + waiting_time[i]
# Generate simplified timeline
timeline.append(processes[sorted_processes[0]])
for i in range(1, n):
if processes[sorted_processes[i]] !=
processes[sorted_processes[i - 1]]:
timeline.append(processes[sorted_processes[i]])
return waiting_time, turnaround_time, timeline
def print_table(processes, burst_time, waiting_time, turnaround_time):
print("Process\tBurst Time\tWaiting Time\tTurnaround Time")
for i in range(len(processes)):
print(f"P{processes[i]}\t{burst_time[i]}\t\t{waiting_time[i]}\
t\t{turnaround_time[i]}")
# Example usage:
processes = [1, 2, 3, 4]
burst_time = [8, 6, 1, 9]
priority = [3, 1, 2, 4]
waiting_time, turnaround_time, timeline =
priority_non_preemptive(processes, burst_time, priority)
print("Priority Scheduling (Non-preemptive) Waiting Time:",
waiting_time)
print("Priority Scheduling (Non-preemptive) Turnaround Time:",
turnaround_time)
# Print Simplified Gantt Chart
print("\nSimplified Gantt Chart:")
print("-" * 30)
print("|", end="")
prev_process = timeline[0]
for process in timeline:
if process != prev_process:
print(f" P{prev_process} |", end="")
prev_process = process
print(f" P{prev_process} |", end="")
print("\n" + "-" * 30)
print("|", end="")
for i in range(len(timeline)):
print(f" {i:2d} |", end="")
print("\n" + "-" * 30)
# Print table of processes, burst time, waiting time, and turnaround
time
print("\nProcess Table:")
print_table(processes, burst_time, waiting_time, turnaround_time)
OUTPUT:
1. Write a program to simulate all File Organization Techniques
a. Single level directory
class SingleLevelDirectory:
def __init__(self):
self.directory = {}
def add_file(self, file_name, file_type):
if file_type not in self.directory:
self.directory[file_type] = []
self.directory[file_type].append(file_name)
def list_files(self):
for file_type, files in self.directory.items():
print(f"{file_type}:")
for file_name in files:
print(f" - {file_name}")
# Create an instance of SingleLevelDirectory
directory = SingleLevelDirectory()
# Add files to the directory
directory.add_file("essay.docx", "Documents")
directory.add_file("report.pdf", "Documents")
directory.add_file("vacation.jpg", "Images")
directory.add_file("family_photo.png", "Images")
directory.add_file("calculator.exe", "Programs")
directory.add_file("text_editor.exe", "Programs")
# List all files in the directory
print("Files in Single-Level Directory:")
directory.list_files()
Output:
b. Two Level
class TwoLevelDirectory:
def __init__(self):
self.directory = {}
def add_file(self, file_name, directory_name):
if directory_name not in self.directory:
self.directory[directory_name] = []
self.directory[directory_name].append(file_name)
def list_files(self):
for directory_name, files in self.directory.items():
print(f"{directory_name}:")
for file_name in files:
print(f" - {file_name}")
# Create an instance of TwoLevelDirectory
directory = TwoLevelDirectory()
# Add files to the directory
directory.add_file("essay.docx", "Documents")
directory.add_file("report.pdf", "Documents")
directory.add_file("vacation.jpg", "Images")
directory.add_file("family_photo.png", "Images")
directory.add_file("calculator.exe", "Programs")
directory.add_file("text_editor.exe", "Programs")
# List all files in the directory
print("Files in Two-Level Directory:")
directory.list_files()
Output:
c. Tree-Structured Directories
class Directory:
def __init__(self, name):
self.name = name
self.subdirectories = {}
self.files = []
def add_file(self, file_name):
self.files.append(file_name)
def add_subdirectory(self, directory_name):
if directory_name not in self.subdirectories:
self.subdirectories[directory_name] =
Directory(directory_name)
return self.subdirectories[directory_name]
def list_contents(self, indent=0):
print(" " * indent + self.name + "/")
for file_name in self.files:
print(" " * (indent + 1) + "- " + file_name)
for subdirectory in self.subdirectories.values():
subdirectory.list_contents(indent + 1)
# Create the root directory
root = Directory("Root")
# Add files directly to the root directory
root.add_file("essay.docx")
root.add_file("report.pdf")
# Create subdirectories and add files to them
documents = root.add_subdirectory("Documents")
documents.add_file("vacation.jpg")
documents.add_file("family_photo.png")
programs = root.add_subdirectory("Programs")
programs.add_file("calculator.exe")
programs.add_file("text_editor.exe")
# List contents of the root directory
print("Contents of Hierarchical Directory:")
root.list_contents()
Output:
d. Acyclic-Graph Directories
class File:
def __init__(self, name):
self.name = name
self.children = []
def add_child(self, child_file):
self.children.append(child_file)
def list_contents(self, indent=0):
print(" " * indent + self.name)
for child in self.children:
child.list_contents(indent + 1)
# Create files
file1 = File("File1")
file2 = File("File2")
file3 = File("File3")
file4 = File("File4")
file5 = File("File5")
# Build DAG structure
file1.add_child(file2)
file1.add_child(file3)
file2.add_child(file4)
file3.add_child(file5)
# List contents
print("Contents of DAG Directory:")
file1.list_contents()
Output:
2. Write a program to simulate all file allocation strategies
a. Contiguous allocation
Dk
class ContiguousAllocation:
def __init__(self, total_blocks):
self.total_blocks = total_blocks
self.disk = [None] * total_blocks
def allocate_file(self, file_name, start_block, num_blocks):
for i in range(start_block, start_block + num_blocks):
if self.disk[i] is not None:
print(f"Error: Block {i} is already allocated.")
return False
for i in range(start_block, start_block + num_blocks):
self.disk[i] = file_name
return True
def deallocate_file(self, file_name):
for i in range(self.total_blocks):
if self.disk[i] == file_name:
self.disk[i] = None
def display_disk_status(self):
print("Disk Status:")
for i, block in enumerate(self.disk):
if block is None:
print(f"Block {i}: Free")
else:
print(f"Block {i}: Allocated to {block}")
# Create an instance of ContiguousAllocation with 20 blocks
allocation = ContiguousAllocation(20)
# Allocate files
allocation.allocate_file("file1", 2, 3)
allocation.allocate_file("file2", 8, 4)
# Display disk status after allocation
allocation.display_disk_status()
# Deallocate file1
allocation.deallocate_file("file1")
# Display disk status after deallocation
allocation.display_disk_status()
b. Indexed
class IndexedAllocation:
def __init__(self, total_blocks):
self.total_blocks = total_blocks
self.disk = [None] * total_blocks
self.index_table = {}
def allocate_file(self, file_name, num_blocks):
if len(self.index_table) >= self.total_blocks:
print("Error: Not enough space on disk.")
return False
if file_name in self.index_table:
print(f"Error: File '{file_name}' already exists.")
return False
allocated_blocks = []
for i in range(num_blocks):
free_block = self._find_free_block()
if free_block is None:
print("Error: Not enough contiguous space on disk.")
self._deallocate_blocks(allocated_blocks)
return False
allocated_blocks.append(free_block)
self.disk[free_block] = file_name
self.index_table[file_name] = allocated_blocks
return True
def deallocate_file(self, file_name):
if file_name not in self.index_table:
print(f"Error: File '{file_name}' not found.")
return False
allocated_blocks = self.index_table[file_name]
self._deallocate_blocks(allocated_blocks)
del self.index_table[file_name]
return True
def _find_free_block(self):
for i in range(self.total_blocks):
if self.disk[i] is None:
return i
return None
def _deallocate_blocks(self, blocks):
for block in blocks:
self.disk[block] = None
def display_disk_status(self):
print("Disk Status:")
for i, block in enumerate(self.disk):
if block is None:
print(f"Block {i}: Free")
else:
print(f"Block {i}: Allocated to {block}")
print("Index Table:")
for file_name, allocated_blocks in self.index_table.items():
print(f"{file_name}: {allocated_blocks}")
# Create an instance of IndexedAllocation with 10 blocks
allocation = IndexedAllocation(10)
# Allocate files
allocation.allocate_file("file1", 3)
allocation.allocate_file("file2", 2)
allocation.allocate_file("file3", 4)
# Display disk status after allocation
allocation.display_disk_status()
# Deallocate file2
allocation.deallocate_file("file2")
# Display disk status after deallocation
allocation.display_disk_status()
c. Linked
class DiskBlock:
def __init__(self, block_number):
self.block_number = block_number
self.next_block = None
class LinkedAllocation:
def __init__(self, total_blocks):
self.total_blocks = total_blocks
self.disk = [None] * total_blocks
self.free_blocks = set(range(total_blocks))
def allocate_file(self, file_name, num_blocks):
if len(self.free_blocks) < num_blocks:
print("Error: Not enough space on disk.")
return False
if file_name in self.disk:
print(f"Error: File '{file_name}' already exists.")
return False
head_block = None
prev_block = None
for i in range(num_blocks):
free_block = self.free_blocks.pop()
self.disk[free_block] = file_name
current_block = DiskBlock(free_block)
if prev_block:
prev_block.next_block = current_block
else:
head_block = current_block
prev_block = current_block
self.disk[file_name] = head_block
return True
def deallocate_file(self, file_name):
if file_name not in self.disk:
print(f"Error: File '{file_name}' not found.")
return False
head_block = self.disk[file_name]
current_block = head_block
while current_block:
self.free_blocks.add(current_block.block_number)
self.disk[current_block.block_number] = None
current_block = current_block.next_block
del self.disk[file_name]
return True
def display_disk_status(self):
print("Disk Status:")
for i, block in enumerate(self.disk):
if isinstance(block, DiskBlock):
print(f"Block {block.block_number}: Allocated to
{self.disk[block.block_number]}")
else:
print(f"Block {i}: Free")
# Create an instance of LinkedAllocation with 10 blocks
allocation = LinkedAllocation(10)
# Allocate files
allocation.allocate_file("file1", 3)
allocation.allocate_file("file2", 2)
allocation.allocate_file("file3", 4)
# Display disk status after allocation
allocation.display_disk_status()
# Deallocate file2
allocation.deallocate_file("file2")
# Display disk status after deallocation
allocation.display_disk_status()
Simulate paging technique of memory management
Let's implement a basic simulation of memory paging with the following
components:
1. Page Table: A data structure to map virtual pages to physical
frames.
2. Memory Management Unit (MMU): Simulates the translation of
virtual addresses to physical addresses using the page table.
3. Page Replacement Algorithm: We'll use a simple FIFO (First In,
First Out) algorithm for replacing pages when there's a page
fault.
class PageTable:
def __init__(self, size):
self.size = size
self.table = {}
self.free_frames = list(range(size)) # Initialize free frames
def page_fault_handler(self, virtual_page):
if virtual_page in self.table:
# Page is already in memory, return corresponding frame
return self.table[virtual_page]
else:
# Page fault: Page is not in memory, allocate a frame
if len(self.free_frames) > 0:
frame = self.free_frames.pop(0) # Get a free frame
self.table[virtual_page] = frame # Map virtual page
to frame
return frame
else:
raise Exception("Out of memory: Cannot handle page
fault")
class MemoryManagementUnit:
def __init__(self, page_table):
self.page_table = page_table
def translate_address(self, virtual_address):
virtual_page = virtual_address // PAGE_SIZE
offset = virtual_address % PAGE_SIZE
try:
frame = self.page_table.page_fault_handler(virtual_page)
physical_address = frame * PAGE_SIZE + offset
return physical_address
except Exception as e:
print(f"Error: {e}")
return None
class Simulation:
def __init__(self, memory_size, page_size):
self.memory_size = memory_size
self.page_size = page_size
self.num_frames = memory_size // page_size
self.page_table = PageTable(self.num_frames)
self.mmu = MemoryManagementUnit(self.page_table)
def access_memory(self, virtual_address):
physical_address = self.mmu.translate_address(virtual_address)
if physical_address is not None:
print(f"Accessing virtual address {virtual_address} ->
Physical address {physical_address}")
def display_page_table(self):
print("Page Table:")
for virtual_page, frame in
sorted(self.page_table.table.items()):
print(f"Virtual Page {virtual_page} -> Frame {frame}")
print(f"Free Frames: {self.page_table.free_frames}")
# Constants
MEMORY_SIZE = 1024 # Total memory size in bytes
PAGE_SIZE = 128 # Page size in bytes
# Initialize simulation
sim = Simulation(MEMORY_SIZE, PAGE_SIZE)
# Access virtual memory addresses
addresses = [0, 256, 512, 100, 384, 768, 512]
for address in addresses:
sim.access_memory(address)
# Display page table after accessing addresses
sim.display_page_table()