Non-Contiguous Allocation Management
Non-contiguous allocation lets the memory space of a process be scattered across physical memory, which removes the external fragmentation that plagues contiguous allocation.
Paging
Basic Idea
Both the logical address space of a process and the physical memory are divided into fixed-size pages and frames, and a page table maps each logical page to a physical frame.
Address Structure
| Address | Composition |
|---|---|
| Logical Address | Page number + page offset |
| Physical Address | Frame number + page offset |
The translation process:
- Extract the page number from the logical address
- Look up the corresponding frame number in the page table
- Combine the frame number with the page offset to form the physical address
Page Table Structure
typedef struct {
int frame_number; // physical frame number
int valid; // valid bit: 1-in memory, 0-on disk
int dirty; // dirty bit: 1-modified, 0-not modified
int referenced; // reference bit: 1-recently accessed, 0-not accessed
int protection; // protection bits: read/write/execute permission
} PageTableEntry;
#define PAGE_SIZE 4096
#define MAX_PAGES 1024
PageTableEntry page_table[MAX_PAGES];
int page_table_size;
Address Translation Implementation
int page_address_translation(int logical_addr) {
int page_number = logical_addr / PAGE_SIZE;
int page_offset = logical_addr % PAGE_SIZE;
// Check whether the page number is valid
if (page_number >= page_table_size) {
return -1; // page number out of bounds
}
// Check whether the page is in memory
if (!page_table[page_number].valid) {
// The page is not in memory and has to be brought in
if (page_fault_handler(page_number) != 0) {
return -1; // failed to bring the page in
}
}
// Update the reference bit
page_table[page_number].referenced = 1;
// Compute the physical address
int frame_number = page_table[page_number].frame_number;
int physical_addr = frame_number * PAGE_SIZE + page_offset;
return physical_addr;
}
Page Table Optimizations
1. Multilevel Page Table
// Two-level page table structure
typedef struct {
int page_directory[1024]; // page directory
PageTableEntry page_tables[1024][1024]; // page tables
} TwoLevelPageTable;
int two_level_address_translation(int logical_addr, TwoLevelPageTable *pt) {
int page_directory_index = (logical_addr >> 22) & 0x3FF;
int page_table_index = (logical_addr >> 12) & 0x3FF;
int page_offset = logical_addr & 0xFFF;
// Check the page directory entry
if (!pt->page_directory[page_directory_index]) {
return -1; // the page table does not exist
}
// Access the page table
PageTableEntry *entry = &pt->page_tables[page_directory_index][page_table_index];
if (!entry->valid) {
return -1; // the page is not in memory
}
return entry->frame_number * PAGE_SIZE + page_offset;
}
2. Inverted Page Table
typedef struct {
int process_id; // process id
int page_number; // page number
int frame_number; // frame number
} InvertedPageTableEntry;
InvertedPageTableEntry inverted_table[MAX_FRAMES];
int frame_count;
int inverted_address_translation(int logical_addr, int process_id) {
int page_number = logical_addr / PAGE_SIZE;
int page_offset = logical_addr % PAGE_SIZE;
// Search the inverted page table
for (int i = 0; i < frame_count; i++) {
if (inverted_table[i].process_id == process_id &&
inverted_table[i].page_number == page_number) {
return inverted_table[i].frame_number * PAGE_SIZE + page_offset;
}
}
return -1; // the page is not in memory
}
3. TLB (Translation Lookaside Buffer)
typedef struct {
int page_number; // page number
int frame_number; // frame number
int valid; // valid bit
int process_id; // process id
} TLBEntry;
#define TLB_SIZE 64
TLBEntry tlb[TLB_SIZE];
int tlb_address_translation(int logical_addr, int process_id) {
int page_number = logical_addr / PAGE_SIZE;
int page_offset = logical_addr % PAGE_SIZE;
// Search the TLB
for (int i = 0; i < TLB_SIZE; i++) {
if (tlb[i].valid && tlb[i].process_id == process_id &&
tlb[i].page_number == page_number) {
// TLB hit
return tlb[i].frame_number * PAGE_SIZE + page_offset;
}
}
// TLB miss, go to the page table
int physical_addr = page_address_translation(logical_addr);
if (physical_addr != -1) {
// Update the TLB
update_tlb(page_number, physical_addr / PAGE_SIZE, process_id);
}
return physical_addr;
}
Segmentation
Basic Idea
The address space of a process is divided into segments along the logical structure of the program, and each segment corresponds to a logical module such as the code segment, the data segment, or the stack segment.
Address Structure
| Address | Composition |
|---|---|
| Logical Address | Segment number + segment offset |
| Physical Address | Segment base + segment offset |
Segment Table Structure
typedef struct {
int base; // segment base address
int limit; // segment length
int valid; // valid bit
int protection; // protection bits
int accessed; // accessed bit
int modified; // modified bit
} SegmentTableEntry;
#define MAX_SEGMENTS 16
SegmentTableEntry segment_table[MAX_SEGMENTS];
int segment_count;
Address Translation Implementation
int segment_address_translation(int logical_addr, int segment_number) {
// Check whether the segment number is valid
if (segment_number >= segment_count) {
return -1; // segment number out of bounds
}
SegmentTableEntry *entry = &segment_table[segment_number];
// Check whether the segment is in memory
if (!entry->valid) {
return -1; // the segment is not in memory
}
// Check whether the offset inside the segment is out of bounds
int segment_offset = logical_addr;
if (segment_offset >= entry->limit) {
return -1; // segment offset out of bounds
}
// Update the accessed bit
entry->accessed = 1;
// Compute the physical address
int physical_addr = entry->base + segment_offset;
return physical_addr;
}
Comparing Segmentation and Paging
| Feature | Paging | Segmentation |
|---|---|---|
| Address space | One-dimensional linear address space | Two-dimensional address space |
| Size | Fixed size | Variable size |
| Fragmentation | Internal fragmentation | External fragmentation |
| Sharing | Easy to share pages | Easy to share segments |
| Protection | Page-level protection | Segment-level protection |
| Implementation | Supported by hardware | Supported by software |
Segmented Paging
Basic Idea
Segmented paging combines the advantages of segmentation and paging: the address space is first split by logical structure, and then every segment is divided into fixed-size pages.
Address Structure
| Address | Composition |
|---|---|
| Logical Address | Segment number + segment offset |
| Segment Offset | Page number + page offset |
| Physical Address | Frame number + page offset |
Address Translation Process
- Segment table lookup: use the segment number to find the segment table and obtain the segment base and the segment length
- Offset check: check whether the offset inside the segment is out of bounds
- Page table lookup: use the page number inside the segment to find the page table and obtain the frame number
- Physical address computation: combine the frame number with the page offset
Segmented Paging Address Translation
typedef struct {
int base; // segment base address
int limit; // segment length
int valid; // valid bit
int page_table_base; // page table base address
} SegmentTableEntry;
typedef struct {
int frame_number; // frame number
int valid; // valid bit
int protection; // protection bits
} PageTableEntry;
int segment_page_address_translation(int logical_addr, int segment_number) {
// Step 1: segment table lookup
if (segment_number >= segment_count) {
return -1; // segment number out of bounds
}
SegmentTableEntry *seg_entry = &segment_table[segment_number];
if (!seg_entry->valid) {
return -1; // the segment is not in memory
}
// Step 2: check the offset inside the segment
int segment_offset = logical_addr;
if (segment_offset >= seg_entry->limit) {
return -1; // segment offset out of bounds
}
// Step 3: page table lookup
int page_number = segment_offset / PAGE_SIZE;
int page_offset = segment_offset % PAGE_SIZE;
PageTableEntry *page_entry = &page_tables[seg_entry->page_table_base][page_number];
if (!page_entry->valid) {
return -1; // the page is not in memory
}
// Step 4: compute the physical address
int physical_addr = page_entry->frame_number * PAGE_SIZE + page_offset;
return physical_addr;
}
Memory Sharing
Sharing in Paging Systems
// Implementation of shared pages
typedef struct {
int frame_number; // frame number
int reference_count; // reference count
int shared; // shared flag
} SharedPageInfo;
SharedPageInfo shared_pages[MAX_FRAMES];
int share_page(int process_id, int page_number, int frame_number) {
// Check whether the page has already been shared
for (int i = 0; i < MAX_FRAMES; i++) {
if (shared_pages[i].frame_number == frame_number) {
shared_pages[i].reference_count++;
return i;
}
}
// Create a new shared page
int index = find_free_shared_slot();
shared_pages[index].frame_number = frame_number;
shared_pages[index].reference_count = 1;
shared_pages[index].shared = 1;
return index;
}
void unshare_page(int process_id, int page_number) {
// Decrease the reference count
for (int i = 0; i < MAX_FRAMES; i++) {
if (shared_pages[i].frame_number == get_frame_number(process_id, page_number)) {
shared_pages[i].reference_count--;
if (shared_pages[i].reference_count == 0) {
// Free the page
free_frame(shared_pages[i].frame_number);
shared_pages[i].shared = 0;
}
break;
}
}
}
Sharing in Segmentation Systems
// Implementation of shared segments
typedef struct {
int segment_number; // segment number
int base; // segment base address
int limit; // segment length
int reference_count; // reference count
int shared; // shared flag
} SharedSegmentInfo;
SharedSegmentInfo shared_segments[MAX_SEGMENTS];
int share_segment(int process_id, int segment_number) {
// Look for an existing shared segment with the same properties
for (int i = 0; i < MAX_SEGMENTS; i++) {
if (shared_segments[i].shared &&
shared_segments[i].limit == get_segment_limit(process_id, segment_number)) {
shared_segments[i].reference_count++;
return i;
}
}
// Create a new shared segment
int index = find_free_shared_slot();
shared_segments[index].segment_number = segment_number;
shared_segments[index].base = get_segment_base(process_id, segment_number);
shared_segments[index].limit = get_segment_limit(process_id, segment_number);
shared_segments[index].reference_count = 1;
shared_segments[index].shared = 1;
return index;
}
Memory Protection
Protection in Paging Systems
// Page-level protection
typedef struct {
int frame_number; // frame number
int read; // read permission
int write; // write permission
int execute; // execute permission
int user; // user permission
} PageProtection;
int check_page_protection(int logical_addr, int access_type, int user_mode) {
int page_number = logical_addr / PAGE_SIZE;
PageProtection *prot = &page_protection[page_number];
switch (access_type) {
case READ_ACCESS:
if (!prot->read) return -1;
break;
case WRITE_ACCESS:
if (!prot->write) return -1;
break;
case EXECUTE_ACCESS:
if (!prot->execute) return -1;
break;
}
// Check the user permission
if (user_mode && !prot->user) {
return -1;
}
return 0;
}
Protection in Segmentation Systems
// Segment-level protection
typedef struct {
int base; // segment base address
int limit; // segment length
int read; // read permission
int write; // write permission
int execute; // execute permission
int user; // user permission
} SegmentProtection;
int check_segment_protection(int logical_addr, int segment_number,
int access_type, int user_mode) {
SegmentProtection *prot = &segment_protection[segment_number];
// Check the offset inside the segment
int offset = logical_addr;
if (offset >= prot->limit) {
return -1; // segment offset out of bounds
}
// Check the access permissions
switch (access_type) {
case READ_ACCESS:
if (!prot->read) return -1;
break;
case WRITE_ACCESS:
if (!prot->write) return -1;
break;
case EXECUTE_ACCESS:
if (!prot->execute) return -1;
break;
}
// Check the user permission
if (user_mode && !prot->user) {
return -1;
}
return 0;
}
Practical Applications
Memory Management in Linux
Linux uses segmented paging, but it relies mainly on paging:
// Memory management in the Linux kernel
struct mm_struct {
struct vm_area_struct *mmap; // list of memory regions
struct rb_root mm_rb; // red-black tree
unsigned long start_code, end_code; // code segment
unsigned long start_data, end_data; // data segment
unsigned long start_brk, brk; // heap
unsigned long start_stack; // stack
unsigned long arg_start, arg_end; // arguments
unsigned long env_start, env_end; // environment variables
};
Memory Management in Windows
Windows uses paging:
// Windows memory management API
HANDLE CreateFileMapping(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName
);
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap
);
Summary
Non-contiguous allocation, through paging, segmentation, and segmented paging, removes the fragmentation problems of contiguous allocation and raises memory utilization. Modern operating systems mainly use segmented paging, which keeps the logical structure of the program while offering a flexible memory management mechanism.
| Term | Abbreviation | Explanation |
|---|---|---|
| Non-Contiguous Allocation | - | A process occupies non-contiguous memory |
| Paging | - | Dividing memory into fixed-size frames |
| Page | - | A fixed-size block of the address space of a process |
| Page Frame | - | A fixed-size block of physical memory |
| Page Table | - | The table mapping pages to frames |
| Page Table Entry | PTE | One entry of a page table |
| Page Table Base Register | PTBR | Holds the start address of the page table |
| Translation Lookaside Buffer | TLB | A fast cache that speeds up address translation |
| Multilevel Page Table | - | A page table organized in several levels |
| Inverted Page Table | - | A page table organized by physical frame |
| Hashed Page Table | - | A page table organized with hashing |
| Segmentation | - | Managing memory by logical segments |
| Segment | - | A variable-length block with a logical meaning |
| Segment Table | - | The table mapping segments to physical addresses |
| Segment Table Entry | STE | One entry of a segment table |
| Segment Base | - | The start address of a segment in memory |
| Segment Limit | - | The length of a segment |
| Segmented Paging | - | Segmentation first, then paging |
| Address Space | - | The range of addresses a process may reach |
| Logical Address | - | The address used by the program |
| Physical Address | - | The address in the real memory |
| Address Translation | - | The mapping from a logical to a physical address |
| Effective Access Time | EAT | The average memory access time including the TLB |
