JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Protection and the MMU

Four guides built the illusion: pages, the page table, demand faults, and the TLB that makes it fast. This last one names the hardware that runs the whole show — the MMU — and shows that virtual memory's deepest gift was never extra room, but a wall between programs that the machine itself enforces on every single access.

Meet the conductor: what the MMU actually is

Across this rung we kept gesturing at 'the hardware' that splits an address, walks the page table, checks the bits, and assembles a physical address. It is time to name it. The memory management unit (MMU) is a real block of logic on the chip, sitting on the path between the processor core and memory, and it translates every address the program ever uses before that address is allowed to touch RAM. Recall the house analogy from guide one: each program believes it owns the whole building, and the MMU is the quiet translator standing at the door, turning every imaginary room number into a real one — and refusing the request if the program had no business asking.

The MMU does two inseparable jobs at once, and grasping that they are one lookup is the heart of this guide. The first is translation: turning a virtual address into a physical address via the page table, with the fast path served by the TLB from guide four. The second is protection: reading the permission bits in the same page table entry and deciding whether this particular access — a read, a write, an instruction fetch — is even allowed. Because both answers come out of the same entry, the machine never has a translated-but-unchecked address. That is the whole reason virtual memory is honestly described as address translation plus protection, never as 'extra RAM'.

Protection bits: a wall enforced on every access

We met the permission bits in guide two as a list of flags inside each page table entry; now see what they buy. A typical entry carries a read bit, a write bit, and an execute bit, plus a valid bit saying the page is mapped at all. On every access the MMU compares the kind of access against these bits. A store to a read-only page, a jump into a page marked non-executable, a load from an unmapped page — each fails the check, and the MMU does not quietly continue. It raises a fault and traps to the operating system, which decides whether to kill the program, deliver a signal, or fix things up.

These bits are why a single buggy or malicious program cannot scribble over another's data, or over the operating system. This is process isolation, and it is enforced not by good manners but by hardware that physically refuses illegal accesses. The classic crash you may have caused — a 'segmentation fault' — is exactly this: a wild pointer reached a virtual address whose page is either unmapped (valid bit off) or write-protected, the MMU caught it on the very access, and the OS stopped the program before it could corrupt anything. Notice the contrast with a page fault from guide three: a page fault is a legal access to a page that merely is not in RAM yet, which the OS repairs by fetching it; a protection fault is an illegal access the OS usually answers by killing the program.

The user/kernel boundary and privilege levels

Protection bits keep programs out of each other's pages; one more bit keeps them out of the operating system's. Modern processors run in at least two privilege levels: user mode, where ordinary programs live, and a more powerful supervisor (kernel) mode, where the OS runs. Each page table entry carries a flag marking whether the page belongs to the kernel, and the MMU refuses any user-mode access to a kernel page. So even though the kernel's pages sit in the same address space the program can see, the program cannot read or write them — the hardware draws the line.

How does a program ever ask the kernel to do something — open a file, allocate memory, send on the network — if it cannot touch kernel pages? Through the one sanctioned doorway: a system call. A special instruction switches the processor into supervisor mode and jumps to a fixed, kernel-chosen entry point — the program cannot pick where it lands. The kernel does the privileged work, then drops back to user mode before returning. This controlled crossing is the only way up, which is exactly what makes the boundary trustworthy: privilege is not something a program can grant itself by jumping to clever code, because the only door into kernel mode also picks the destination.

Making it fast: the TLB, the cache, and the multilevel walk

All this checking would be worthless if it were slow, so the MMU leans hard on the TLB from guide four — a tiny, fully-associative cache holding the most recent page-to-frame translations and their protection bits. On a TLB hit, the MMU has the frame number and the permission check in a single cycle, and the access proceeds with no extra memory traffic at all. The translation and the protection both come for free out of the same TLB entry. This is why, in practice, virtual memory's per-access overhead is usually invisible: the common case is a hit, and we make the common case fast.

Translation and the data cache also dovetail neatly. A common trick is to start the cache lookup in parallel with the TLB lookup: the page offset bits — which translation never changes — are used to index the cache while the TLB simultaneously turns the page number into a frame number, and that frame number then supplies the tag for the comparison. The cache hit and the translation finish at roughly the same time, so address translation hides under the cache access instead of adding to it. (This 'virtually-indexed, physically-tagged' arrangement has constraints we will not unpack, but the idea — overlap the two lookups — is the part worth keeping.)

  1. Split the virtual address into page number and offset, and present the page number to the TLB.
  2. TLB hit (common case): read the frame number and the protection bits straight from the matching TLB entry — translation and permission check, both in about a cycle.
  3. TLB miss: the MMU walks the multilevel page table from guide two, slicing the page number into fields and following the tree level by level — several memory reads, since each level is itself in memory.
  4. If the final entry is present and the access is permitted, load the translation into the TLB and proceed; if the page is on disk, take a page fault; if the access is illegal, take a protection fault.

The honest catch: co-design, costs, and a real exploit

Step back and see the whole machine. Virtual memory is a hardware/OS co-design: the MMU is hardware, but the page tables it walks are built and maintained by the operating system, the faults it raises are handled by OS code, and the policies — what to evict, when to fetch, how to share — are pure software. Neither half works alone. The hardware is fast but dumb; it knows how to walk a table and check a bit, nothing more. The OS is smart but slow; it decides everything but touches no address directly. The elegance of the design is precisely this division of labour, and its fragility lives at the seam between them.

Be honest about the costs, too. A TLB hit is nearly free, but a TLB miss triggers a page-table walk of several dependent memory reads, and a true page fault that must reach the disk costs millions of cycles — a single fault can dwarf the work of the instruction that caused it. This is the recurring honest note of the whole rung: virtual memory's average overhead is tiny, but its tail is brutal, and a workload that thrashes the TLB or faults constantly can be dominated entirely by translation, not by its actual computation. The illusion is cheap only as long as locality holds — exactly the lesson the cache rung drilled in, now wearing a page-sized coat.