When a program prints a pointer, it looks like it has obtained “a memory address.” Many wrong assumptions start there.
On systems such as Linux, the address seen by a user program is usually not a physical memory address. It is a virtual address. The same 0x400000 in two different processes can point to completely different physical pages. A pointer from one process is not directly meaningful in another process.
The safest first model is this: a program sees its own virtual address space. The CPU uses the MMU and page tables to translate virtual addresses into physical addresses. The operating system uses that translation layer for isolation, permission checks, demand allocation, and shared mappings.
Program accesses virtual address
-> MMU checks page table
-> physical page and permission are found
-> real memory is accessed
Virtual memory is not fake memory. It is an address translation and protection mechanism.
Why Programs Do Not See Raw Physical Addresses
If every program used physical addresses directly, the system would be hard to manage.
A bad pointer in one program could overwrite another program’s data. Programs would need to know the global physical memory layout. Loading a program would need to avoid all memory already used by others.
Virtual memory separates those concerns.
Each process sees its own address space:
- where code is mapped
- where the heap grows
- where the stack is
- where shared libraries are mapped
- which addresses are inaccessible
Those addresses are the process’s own view. Which physical memory backs them is decided by the kernel and page tables.
This gives an important result: a pointer is meaningful only inside its current address space.
The same pointer value in another process may point to another object or to nothing valid at all.
What Page Tables Do
A page table maps virtual addresses to physical addresses. Memory is usually managed in pages, such as common 4 KB pages.
When a program accesses a virtual address, the CPU’s MMU checks the page table and obtains information such as:
- whether the virtual page is mapped
- which physical page it maps to
- whether the access is allowed
- whether the page is readable, writable, or executable
- whether user mode may access it
If the mapping exists and permissions allow the access, execution continues. If the mapping is missing or permission fails, an exception occurs, such as a page fault or segmentation fault.
So a segmentation fault usually does not mean “the memory hardware is broken.” It often means the process accessed an address that is not allowed in its own address space.
Why Each Process Looks Like It Owns Memory
Virtual address spaces give processes a private view.
Process A can believe its code starts at one address, and Process B can use a similar address. They do not conflict because their page tables are different.
Process A: virtual address 0x400000 -> physical page X
Process B: virtual address 0x400000 -> physical page Y
That makes program loading, allocation, and isolation simpler.
A process does not need to know which other programs exist or which physical page it got. It runs in its own virtual address space.
The kernel manages physical memory behind the scenes:
- which physical pages are free
- which pages belong to which process
- which pages can be shared
- which pages can be reclaimed or swapped
- which pages must be written back to files
That is why each process looks like it owns memory. It is not physical ownership. It is an address-space view.
Why Permission Protection Matters
Page tables record not only address mappings, but also permissions.
Common permissions include:
- readable
- writable
- executable
- user-accessible
- kernel-only
This lets the system block many dangerous behaviors.
For example, code pages can be mapped read-only and executable, preventing the program from modifying its own instructions. Stack and heap can be mapped non-executable, reducing the risk of treating data as code. Kernel address space is not directly accessible to ordinary user programs, preventing applications from modifying kernel data.
So virtual memory is not only about “making memory look larger.” Its more fundamental value is isolation and protection.
A Page Fault Is Not Always an Error
When a program accesses a virtual address that does not currently have a physical page behind it, a page fault occurs. The name makes it sound like an error, but it is not always one.
A page fault can be a normal path.
For example, a program may allocate a large region. The kernel can reserve a virtual address range without immediately allocating all physical pages. When the program actually writes to a page, the kernel allocates a physical page and creates the mapping.
File mappings work similarly. After a program mmaps a file, the whole file does not have to be read into memory immediately. When a page is accessed, the kernel can load the corresponding file content into a physical page.
This demand allocation and demand loading reduce startup cost and memory waste.
But a page fault can also be an error:
- accessing near a null pointer
- accessing an unmapped or freed region
- writing to a read-only page
- user mode accessing a kernel-only address
- executing a non-executable page
The key question is whether the kernel can legally create a mapping for the access.
Why Shared Memory Must Be Explicit
Processes have isolated address spaces by default, so one process cannot pass an ordinary pointer to another process and expect it to work.
If two processes need to share memory, the kernel must map the same physical pages into both address spaces.
Process A: virtual address VA1 -> physical page P
Process B: virtual address VA2 -> physical page P
The virtual addresses may differ, but the physical page behind them is the same.
That explains two properties of shared memory:
- data exchange is fast because repeated copying is avoided
- synchronization is still required because both processes can access the same state
Shared memory solves “both sides can see the same data.” It does not solve “who may modify it when.”
Why Virtual Memory and DMA Are Easy to Confuse
The CPU can access memory through virtual address translation, but peripheral DMA usually does not directly use ordinary user-space virtual addresses.
When a driver configures DMA, it must provide an address the hardware can understand and ensure that the memory is not moved, reclaimed, or mapped with incompatible permissions during DMA.
Cache coherency also matters. The CPU may keep data in cache while the DMA device sees old physical memory. Or DMA may write memory while the CPU still has stale cached data.
That is why drivers often need DMA mapping, cache clean, or cache invalidate operations.
So “I have a pointer; give it to the device for DMA” is not a safe model. User-space pointers, kernel virtual addresses, physical addresses, and DMA addresses can be different layers.
Do Embedded Systems Always Have Virtual Memory?
No.
Many MCUs have no MMU, only a simple MPU, or no memory protection unit at all. In bare-metal systems or small RTOSes, tasks often see one shared system address space.
That makes the system simple, direct, and cheap to switch, but isolation is weak:
- a bad pointer in one task can corrupt the whole system
- stack overflow can overwrite another task or global data
- application and driver boundaries are less clear
- shared memory is natural, but synchronization and protection rely on discipline
Embedded Linux with an MMU is different. User processes see virtual addresses, and the kernel manages address-space isolation and page table mappings.
So when talking about an address, first ask: is this bare metal, an RTOS, an MPU-based system, or MMU-based Linux? The meaning of an address changes a lot.
Debug Memory Problems by Separating Address Layers
When debugging segmentation faults, illegal access, DMA data corruption, or shared memory bugs, do not focus only on the pointer value.
Ask:
- is this a user virtual address, kernel virtual address, physical address, or DMA address
- which process address space owns it
- is there a page-table mapping, and what permissions does it have
- can the memory be freed, swapped, or remapped
- do multiple processes map the same physical pages
- were cache coherency operations done around DMA
- does the platform have an MMU or MPU
Those questions separate what the address actually means.
If address layers are mixed, permission errors, lifetime bugs, cache coherency problems, and hardware access bugs quickly become one confusing pile.
What to Remember in Practice
Virtual memory lets programs see their own address spaces instead of raw physical memory.
Its core values include:
- isolating processes
- letting the kernel enforce permissions
- supporting demand allocation and demand loading
- allowing the same physical page to be shared by multiple address spaces
- separating user-space, kernel-space, and hardware access boundaries
A pointer is not a system-wide address. It is meaningful only in the right address space and lifetime.
Separating virtual addresses, physical addresses, kernel addresses, and DMA addresses gives memory access, shared memory, and driver data-corruption bugs the right starting point.