The problem virtual memory solves
On an early computer with no virtual memory, all programs shared physical RAM directly. If process A used address 1000, and process B also used address 1000, they'd stomp on each other's data. A buggy program could overwrite the OS itself. Implementing isolation required every programmer to know exactly where every other program was loaded — fragile, slow, and impossible to scale.
Virtual memory solves this by giving each process its own private address space.
The addresses your program uses — the ones you print with %p —
are virtual addresses. The CPU hardware translates each one to a
physical address before accessing RAM. Two processes can use the same
virtual address and get different physical memory.
Pages: the unit of virtual memory
Virtual memory is managed in fixed-size chunks called pages, typically 4 KB on x86-64. The OS maps virtual pages to physical pages (called frames). The mapping is stored in a per-process data structure called the page table, maintained in kernel memory.
The hardware component that performs the translation — walking the page table on every memory access — is the Memory Management Unit (MMU), built into the CPU. To avoid doing a full page table walk for every instruction, the CPU caches recent translations in the Translation Lookaside Buffer (TLB).
Page faults
When you access a virtual address that has no mapping in the page table, the MMU raises a page fault — a hardware exception that transfers control to the OS kernel's fault handler.
Not all page faults are errors. The OS uses them deliberately:
- Demand paging — when a program starts, the OS maps its pages without loading them all into RAM. The first access to each page triggers a fault, and the OS loads that page on demand.
- Stack growth — the stack starts small. Accessing a valid but unmapped stack address triggers a fault; the OS grows the stack by mapping a new page.
- Copy-on-write — after
fork(), parent and child share physical pages marked read-only. The first write to a shared page triggers a fault; the OS copies the page and gives each process its own copy. - Swapping — if RAM is full, the OS can evict physical pages to disk. Accessing a swapped-out page faults; the OS reloads it from swap.
SIGSEGV to your process. The default action is to terminate and dump core.
Why virtual memory enables process isolation
Each process has its own page table. Physical RAM is shared between all processes,
but because each process's page table maps its virtual addresses to different physical
frames, no process can read or modify another process's memory —
unless the OS explicitly sets up a shared mapping (as with mmap and
shared memory).
This is the foundation of security on modern operating systems. A buffer overflow in one process cannot corrupt another process's memory, because they operate in completely separate virtual address spaces.
Address space layout randomization (ASLR)
ASLR randomizes the base addresses of the stack, heap, and mapped libraries each time a program runs. Even though a process always sees the same structure (stack high, heap low, text at some base), the actual virtual addresses change between runs.
This defeats a class of exploits that rely on knowing where specific code or data
lives in memory. An attacker who finds a buffer overflow cannot reliably guess
where to redirect execution if the addresses are random.
You may notice this when debugging: the addresses in %p output
change between runs.
Virtual addresses are translated to physical addresses by the MMU on every access — each process has its own page table, so the same virtual address in two processes refers to different physical memory.