virtual memory · paging translation
A virtual address gets split into a page number and an offset. The MMU looks up the page in the page table, finds the frame in physical memory, and assembles the physical address. If the page isn't mapped, the kernel handles a page fault.
Source
// VM_addr_map.c · lab 8
page_num = logical_addr >> page_size_bits;
offset = logical_addr & ((1U << page_size_bits) - 1);
if (page_table[page_num] == -1) {
fprintf(stdout, "Page Fault!\n");
// pick the first free frame, install it
for (int i = 0; i < num_frames; i++) {
if (mem_map[i] == 0) { mem_map[i] = 1; page_table[page_num] = i; break; }
}
}
frame_num = page_table[page_num];
physical_addr = (frame_num << page_size_bits) | offset;What's really happening
Every memory access your program makes goes through this translation. The CPU's MMU does the shift-and-mask in hardware, looks the page up in a tiny on-chip cache (the TLB), and only walks the full page table when the TLB misses.
A page fault isn't an error — it's a feature. It hands control to the kernel, which decides whether to read the page from disk (paging), allocate a fresh zero page (anonymous), or actually kill the process (segfault for unmapped or protected memory). On modern systems most page faults are minor: just allocate a new frame and resume.
This lab uses a flat page table for clarity. Real systems use multi-level page tables (x86-64 uses four levels) so the table itself is paged too. The cost: every address translation walks four levels on a TLB miss. The TLB is why your laptop isn't hopelessly slow.