Assignment 06: Contiguous Memory Management
I. Objective & Theoretical Framework
Memory management is a primary function of an operating system, ensuring that multiple processes can reside in main memory simultaneously without interfering with one another. This assignment covers contiguous memory allocation techniques, where each process is contained in a single contiguous section of memory.
You will explore two foundational architectures:
- MFT (Multiprogramming with a Fixed number of Tasks): The memory is partitioned into fixed size partitions and each job is assigned to a partition[cite: 4]. The memory assigned to a partition does not change[cite: 4]. This technique heavily suffers from internal fragmentation[cite: 4].
- MVT (Multiprogramming with a Variable number of Tasks): The partitioning of memory is dynamic and changes as jobs enter and leave the system[cite: 4]. Each job gets just the amount of memory it needs, making it a more efficient user of resources[cite: 4]. However, MVT suffers from external fragmentation[cite: 4].
When allocating memory dynamically (as in MVT), the OS must decide which free block to allocate. You will simulate these strategies[cite: 4]:
- First-fit: Chooses the first available block that is large enough[cite: 4].
- Best-fit: Chooses the block that is closest in size to the request[cite: 4].
- Worst-fit: Chooses the largest available block[cite: 4].
II. Prerequisite Knowledge & Resources
- Fragmentation Concepts:
- Internal Fragmentation: Wasted space within an allocated block (common in MFT).
- External Fragmentation: Total free memory exists to satisfy a request, but it is not contiguous (common in MVT).
- Array Tracking: You will need to utilize parallel arrays to keep track of process sizes, block sizes, and boolean flags indicating whether a block is currently occupied.
III. Starter Code & Partial Implementations
The following skeleton code provides the setup for calculating fragmentation in an MFT (Fixed Partition) environment. Use this to structure your calculations for total internal and external fragmentation[cite: 4].
#include <stdio.h>
int main() {
int ms, bs, nob, ef, n, mp[10], tif = 0;
int i, p = 0;
printf("Enter the total memory available (in Bytes): ");
scanf("%d", &ms);
printf("Enter the block size (in Bytes): ");
scanf("%d", &bs);
nob = ms / bs; // Calculate Total Number of Blocks
ef = ms - nob * bs; // Calculate Initial External Fragmentation
printf("Enter the number of processes: ");
scanf("%d", &n);
for(i = 0; i < n; i++) {
printf("Enter memory required for process %d (in Bytes): ", i+1);
scanf("%d", &mp[i]);
}
printf("\nNo. of Blocks available in memory: %d\n", nob);
printf("\nPROCESS\tMEMORY REQUIRED\tALLOCATED\tINTERNAL FRAGMENTATION\n");
// [Insert Allocation and Internal Fragmentation Logic Here]
// [Insert Final Fragmentation Print Statements Here]
return 0;
}
IV. Step-by-Step Task List
MFT Simulation: Complete the starter code above. Iterate through the processes. If the memory required is $\le$ the block size, print “YES” for allocated, and calculate the internal fragmentation for that block. Accumulate this into the Total Internal Fragmentation variable (
tif).MVT Simulation: Write a new C program for MVT. Prompt for total memory. Use a
whileorforloop to continuously ask for new process memory requirements. If the requirement is $\le$ the remaining available memory, allocate it and subtract from the total. If not, declare the memory full and print the Total External Fragmentation.First-Fit Algorithm: Create a program that accepts an array of block sizes and an array of file (process) sizes. Loop through the files. For each file, loop through the blocks from the beginning and allocate the file to the very first block where
block_size - file_size >= 0. Mark that block as allocated using a flag array (e.g.,bf[j] = 1).Best-Fit Algorithm: Modify your First-Fit logic to create a Best-Fit simulation. Instead of stopping at the first fit, you must check every unallocated block to find the one that leaves the smallest positive remaining space.
V. Common Pitfalls & Debugging Strategies
Variable Initialization in Best-Fit: When writing the Best-Fit algorithm, you must initialize your
lowesttracking variable (used to find the smallest fragment) to a very high number (e.g.,10000) before checking the blocks for each file. If you initialize it to0, the algorithm will fail to find a valid block.State Management Failure: In the Fit algorithms, if you do not use a dedicated flag array (like
bf[max]) to mark a block as1(allocated), your algorithm will assign multiple processes to the exact same block of memory.Observing the Hardware: Before executing a memory-heavy allocation simulation, open a second terminal and run
free -morvmstat 1. Watch how the operating system manages available physical RAM and Swap space dynamically as your C program executes.
VI. Real-World Case Study
Modern general-purpose operating systems rely almost entirely on Paging (which we will cover next) to solve the external fragmentation issues inherent in MVT architectures. However, contiguous memory allocation algorithms are far from obsolete. They are heavily utilized in user-space memory allocators (like the implementation of malloc() and free() in the C standard library) and in kernel SLAB allocators. Furthermore, Real-Time Operating Systems (RTOS) used in embedded hardware often utilize strict Best-Fit or First-Fit contiguous allocation to guarantee deterministic memory access times without the overhead of hardware page tables.
VII. Advanced Variant Tasks
Worst-Fit Implementation: Write a C program to simulate the Worst-fit allocation technique. Design the logic to scan the array and always allocate the process to the largest available free block to intentionally leave large fragments behind.
Dynamic Compaction (Defragmentation): Extend your MVT simulation. If a process arrives and requires 500 bytes, but the system only has three separated 200-byte blocks (external fragmentation), simulate a “compaction” algorithm. Shift the allocated processes to one end of memory, merge the free blocks into a single 600-byte block, and successfully allocate the new process.
User-Space Memory Allocator (Malloc Clone): Bypass the standard C library. Use the Linux
sbrk()ormmap()system calls to request a large, raw block of memory directly from the kernel. Write your owncustom_malloc(size)andcustom_free(pointer)functions to manage this block, utilizing your First-Fit or Best-Fit logic and a linked list to track free partitions.
VIII. Resources & Further Reading
- OSTEP - Free Space Management: Read Chapter 17: Free-Space Management (PDF). This chapter covers the exact mechanics of Best-Fit, Worst-Fit, and First-Fit allocators, including how headers and free lists are structured in memory.
- Malloc Internals: For the advanced allocator capstone, read Dan Luu’s Guide to Malloc for a brilliant step-by-step breakdown of how to build a user-space memory allocator in C using
sbrk().