5.4.11 · D2Memory Hierarchy & Caches

Visual walkthrough — Virtual memory and paging

1,957 words9 min readBack to topic

Step 1 — What an address even is (a ruler of bytes)

WHAT. Memory is one gigantic row of numbered boxes, each holding one byte (8 bits). An address is just the box number. Box 0, box 1, box 2, … all the way up.

WHY. Before we can "translate" an address, we must agree it is nothing mystical — it is a position on a ruler. If your addresses are bits wide, the biggest number you can write is , so you can name boxes total.

PICTURE. Below, the black ruler is memory. The red tick is one address pointing at one box.

Figure — Virtual memory and paging

Step 2 — Chop the ruler into equal pages

WHAT. Take that long ruler and cut it into equal-length segments. Each segment is a page. If a page is bytes long and , then every page holds exactly boxes.

WHY. We choose the length to be a power of two, , on purpose — this is the single most important decision in the whole derivation. In binary, dividing by is just "ignore the lowest bits," and the remainder is "the lowest bits by themselves." We will lean on that in Step 4. If were, say, , none of this bit-magic would work.

PICTURE. Same ruler, now sliced. Each slice (page) is the same width. The red slice is page number 2, and inside it the boxes are numbered again, locally.

Figure — Virtual memory and paging

Step 3 — Split the address: VPN and offset

WHAT. Write the address in binary. The high bits answer "which page?" — call them the Virtual Page Number (VPN). The low bits answer "how far in?" — call them the offset.

  • — the full virtual address (the box number the program uses).
  • — "written next to," i.e. concatenation of bit-groups, not multiplication.
  • VPN — the page's index in the sliced ruler of Step 2.
  • offset — the local position inside that page, always in .

WHY. Because Step 2 sliced along power-of-two boundaries, the bits literally fall into two clean groups. No arithmetic tricks needed to separate "which page" from "where inside" — the split is just cutting the bit-string.

PICTURE. The bit-string with a red divider dropped between bit and bit . Everything left of the line is VPN; everything right is offset.

Figure — Virtual memory and paging

Step 4 — Extract each part with shift and mask

WHAT. We now compute VPN and offset with real operations:

  • — bitwise AND: keeps a bit only where both inputs are 1.
  • — a number that is ones in binary (e.g. ). ANDing with it keeps exactly the low bits and zeroes the rest → that is the offset.
  • — shift right by : slide every bit down places, so the low bits fall off the end and the VPN lands at the bottom.

WHY these two tools and not division? is and is — but shift and mask are single, one-cycle hardware operations. We picked in Step 2 precisely so the expensive division becomes a free wire-shift. That is the payoff of the power-of-two choice.

PICTURE. Left: the mask (a red band of ones) sitting under , keeping only the offset. Right: the arrow of a right-shift sliding the VPN down into place.

Figure — Virtual memory and paging

Step 5 — Look up the frame in the page table

WHAT. Feed the VPN into the page table — a per-process lookup array — and read out the Physical Frame Number (PFN):

  • PageTable — an array indexed by VPN; entry number tells you where virtual page really lives.
  • — "the entry at row VPN," ordinary array indexing.
  • PFN — the physical frame's index (a slot in real RAM), the same size as a page.

WHY. This is the only step where fiction becomes fact. The offset is never touched (Step 6 explains why); only the page's identity is remapped. This indirection is what lets each process own its own map — see Multi-level page tables for how the table itself is stored cheaply.

PICTURE. The VPN points into a black table; the red arrow leaves the matched row carrying the PFN out to the physical side.

Figure — Virtual memory and paging

Step 6 — Rebuild the physical address

WHAT. Glue the frame number back onto the untouched offset:

  • — shift the frame number left by , i.e. multiply by . This lands PFN in the high bits and creates empty low bits.
  • — bitwise OR: drops the offset into those freshly-emptied low bits.
  • Result: PA — the real box number RAM will access.

WHY the left shift? Frame begins at physical byte (frames are bytes apart, back to back). So the frame's starting byte is , and adding the offset walks us the rest of the way in. Because the low bits of are guaranteed zero, OR and add give the identical result — no carry can collide.

PICTURE. The offset from Step 4 (kept in red the whole time) reunites with the shifted PFN to form PA. Notice the red offset bits are bit-for-bit the same as in Step 3.

Figure — Virtual memory and paging

Step 7 — The degenerate & edge cases

Never leave the reader in an unshown scenario. Four corners of this machine:

  1. Offset = 0. The address sits exactly on a page boundary — the first byte of a page. Then , a clean frame base. Nothing breaks; the OR just adds zero.
  2. Offset = . The last byte of a page (all offset bits 1). Still stays inside the same frame — the offset can never overflow into the PFN because it is only bits wide. This is the guarantee that makes "only the VPN differs" true.
  3. VPN not in the table (or marked "not present"). The lookup in Step 5 fails → a page fault fires. This is not a crash: the OS fetches the page from disk, fills the table entry, and re-runs the same instruction. Then Steps 1–6 succeed.
  4. VPN genuinely invalid (out of the process's allowed range). Now it is a real error — a segfault. Compare with Segmentation, where whole regions, not fixed pages, are checked.

PICTURE. Two tiny rulers side by side: the leftmost box (offset 0, red) and the rightmost box (offset , red) both land safely inside one frame — the offset never spills over the frame's edge.

Figure — Virtual memory and paging

Step 8 — Worked number, end to end


The one-picture summary

Everything above, compressed: an arrow flows from the virtual address, splits into VPN + offset, the VPN detours through the page table to become a PFN, and the two rejoin as the physical address. Follow the red offset — it rides straight through, untouched, from VA to PA.

Figure — Virtual memory and paging
Recall Feynman retelling — the whole walk in plain words

Memory is one long shelf of numbered boxes (Step 1). We tape it into equal-size pages, each a power-of-two long (Step 2), so an address naturally reads as "which page, then how far in" — VPN and offset (Step 3). Splitting is free: shift right for the page number, mask for the how-far-in (Step 4). Only the page number visits the page table, which swaps it for the real frame number (Step 5). We shove that frame number up top and drop the untouched offset back underneath — that's the real address (Step 6). Edge boxes (first and last of a page) still stay in the frame, missing pages just cause the OS to fetch them and retry, and truly-bad addresses are the only real error (Step 7). Run it on real hex and the offset comes out unchanged, proving the rigid-slide idea (Step 8).

Recall Quick self-check

VA→PA in one sentence? ::: Split off the offset, translate only the VPN through the page table into a PFN, then recombine PFN‹‹p with the untouched offset. Why must the page size be a power of two? ::: So the VPN/offset split is a free shift-and-mask instead of a division. Does the offset ever change during translation? ::: No — it is copied bit-for-bit from VA to PA. What happens if the VPN's entry is "not present"? ::: A page fault; the OS loads the page from disk and re-runs the instruction — not a crash.

Related: 5.4.11 Virtual memory and paging (Hinglish) · CPU caches · Locality of reference · Page replacement algorithms · Multi-level page tables