Explain the concept of address translation in the context of virtual memory

Explain the concept of address translation in the context of virtual memory

Address translation is the quiet mechanism that makes almost everything else in modern computing possible — process isolation, memory protection, running more programs than physical RAM could hold at once, even something as basic as every process believing it has its own private, contiguous address space starting near zero. I want to walk through what address translation actually is, how it works mechanically, and why the whole concept exists in the first place.

Virtual Addresses vs Physical Addresses

Every running process operates entirely in terms of virtual addresses — numbers a program uses to reference memory, generated by compiled code, pointers, the stack, all of it. These numbers have no direct relationship to where data actually sits in physical RAM. The actual RAM is addressed using physical addresses, and it’s the job of address translation to convert one into the other, transparently, on every single memory access.

Process's view:                Reality:
+-------------------+          +----------------------+
| 0x0000 - code      |          | Physical RAM          |
| 0x1000 - data       |  ---->  | scattered frames,      |
| 0x7fff.... - stack  |          | shared with every      |
+-------------------+          | other running process  |
                                +----------------------+

Two processes can both believe they own address 0x400000, and both can be right, simultaneously, without conflict — because that virtual address gets translated to two completely different physical addresses depending on which process is running. This is the foundational trick that makes process isolation possible without every program needing to know or care about what else is running on the machine.

Why Address Translation Exists At All

It would be simpler, in a sense, for programs to just address physical RAM directly — and very early computing systems, and today’s simplest microcontrollers, do exactly that. But direct physical addressing creates serious problems the moment you want to run more than one program safely:

Virtual memory, powered by address translation, solves all three at once: each process gets an isolated, private-feeling address space; the OS is free to place physical pages wherever convenient (and move them later); and programs can reference far more virtual address space than physical RAM actually provides, with the OS filling in physical backing only for the parts actually in use.

How Translation Actually Happens: Paging

The dominant mechanism (used by essentially every general-purpose OS today — Linux, Windows, macOS, and their mobile counterparts) is paging: dividing both virtual and physical address space into fixed-size chunks (commonly 4KB) called pages and frames, respectively, and maintaining a mapping table between them.

A virtual address is split into two components:

+---------------------------+------------------+
|      Virtual Page Number   |   Page Offset     |
+---------------------------+------------------+

The page number identifies which page the address falls in; the offset identifies exactly where within that page. Translation only ever needs to convert the page number — the offset is identical between virtual and physical addresses, since pages and frames are the same size and page-aligned.

Virtual Address
      |
      v
[ Virtual Page Number ] [ Offset ]
      |
      v
  Page table lookup  --->  Physical Frame Number
      |
      v
[ Physical Frame Number ] [ Offset ]   <-- same offset, unchanged
      |
      v
Physical Address

On x86-64 Linux, that “page table lookup” is itself a multi-level walk (PGD -> PUD -> PMD -> PTE, as covered in more depth in a companion article on Page Table Entries), because a single flat table mapping every possible virtual page directly would be enormous and mostly empty for any real process. The multi-level, hierarchical structure lets the OS only allocate table entries for regions of address space actually in use.

The Role of the MMU

All of this translation happens in hardware, performed by the Memory Management Unit (MMU), a component built directly into the CPU. Software (the OS) is responsible for setting up and maintaining the page tables, but the actual per-access translation — happening potentially billions of times per second — is done entirely by dedicated silicon, because doing it in software on every access would be far too slow.

The TLB (Translation Lookaside Buffer) sits alongside the MMU’s page-table-walking logic as a cache of recent translations, avoiding the multi-level walk entirely for the overwhelming majority of accesses (a topic covered thoroughly in its own dedicated article, since it’s central enough to deserve full treatment on its own).

Segmentation: The Other (Mostly Historical) Approach

Paging isn’t the only address translation scheme that’s existed. Segmentation divides memory into variable-sized logical segments (code, data, stack, etc.) rather than fixed-size pages, translating a segment-relative address into a physical one via a segment base and limit. Early x86 processors relied heavily on segmentation, and vestiges of it (segment registers, though mostly vestigial in 64-bit mode) still exist in the x86-64 architecture for legacy compatibility. Modern general-purpose OSes, however, rely almost entirely on paging, because fixed-size pages are dramatically simpler to manage without suffering the external fragmentation problems variable-sized segments introduce.

Multi-Level Translation Under Virtualization

Things get an extra layer more complex under virtualization, worth mentioning because it’s an increasingly common real-world scenario. A virtual machine’s guest OS performs its own translation from guest-virtual to what it believes is “physical” memory — but that guest-physical address is itself really just another virtual address from the host’s perspective, requiring a second translation stage (guest-physical to host-physical) managed by the hypervisor. Modern CPUs support this directly in hardware via Extended Page Tables (Intel EPT) or Nested Page Tables (AMD NPT), avoiding the need for the hypervisor to trap and emulate every guest page table update in software, which would otherwise be prohibitively slow.

Address Translation Across Platforms

Practical, Real-World Consequences

Troubleshooting and Observability

  1. On Linux, /proc/<pid>/maps shows a process’s virtual memory layout — the mapped ranges, permissions, and backing files — directly reflecting what the translation system has set up for that process.
  2. /proc/<pid>/pagemap allows inspection of the actual physical frame each virtual page currently maps to, useful for low-level memory debugging.
  3. Unexpectedly slow memory-intensive code is often a translation-overhead problem (TLB misses from poor locality or insufficient hugepage use) rather than a raw compute problem — profile with hardware performance counters before assuming otherwise.
  4. dmesg/kernel logs reporting segmentation faults or general protection faults are, at the hardware level, address-translation failures — either no valid mapping existed, or a permission check embedded in the page table walk failed.

Best Practices

Summary

Address translation is the mechanism that separates the tidy, private-feeling address space every program sees from the messy, shared reality of physical RAM underneath. By converting virtual addresses to physical ones on every memory access — using paging, hardware-accelerated by the MMU and TLB, and orchestrated by OS-maintained page tables — modern systems achieve process isolation, memory protection, flexible physical memory allocation, and the ability to run programs larger than physical RAM, all without programs themselves needing any awareness that the translation is happening at all.

FAQs

What’s the difference between a virtual address and a physical address? A virtual address is what a program uses to reference memory, private to that process’s address space; a physical address identifies an actual location in RAM. Address translation converts one into the other on every access.

What component performs address translation? The Memory Management Unit (MMU), a hardware component built into the CPU, performs the actual translation on every access, working from page tables the OS maintains in memory, with a TLB caching recent results.

Is paging the only way to do address translation? No — segmentation is an older, largely historical alternative, but virtually all modern general-purpose operating systems rely on paging due to its simpler, fragmentation-resistant fixed-size-unit design.

How does virtualization affect address translation? It adds an additional translation stage — guest-virtual to guest-physical, then guest-physical to host-physical — accelerated in hardware via technologies like Intel EPT or AMD NPT to avoid prohibitively slow software emulation.

References

Exit mobile version