A request handler executes an instruction that reads a field from a structure:
Suppose request_pointer contains 0x00007f2100401000. The CPU adds the field offset and obtains:
This is a virtual address in the current process. Before the load can reach memory, the processor must determine whether the address is valid, whether a read is permitted, and which physical location supplies the four bytes.
The hardware responsible for this work is the memory management unit, or MMU.
The MMU translates virtual addresses into physical addresses and enforces the access permissions established by the operating system.
The MMU is what turns a process's virtual address space from a software convention into a hardware-enforced reality. Without it, ordinary application instructions could bypass process boundaries by issuing direct physical addresses.
The MMU is a hardware subsystem associated with a processor core. On modern general-purpose CPUs, it is integrated into the processor rather than being a separate chip that software communicates with explicitly.
Conceptually, it sits between address generation by the CPU and access to the physical memory system:
This diagram is deliberately conceptual. Real processors have multiple cache levels, parallel checks, and other optimizations. The essential ordering remains: an application supplies a virtual address, while access to the machine-wide physical address space occurs only after translation and permission checking.
The physical address usually reaches a cache or the memory controller rather than immediately selecting a particular RAM chip. Some physical address ranges can also identify hardware resources rather than ordinary RAM. Application code is insulated from those platform details.
The MMU participates in three kinds of ordinary memory operation:
Instruction addresses therefore require translation just as data pointers do. A process cannot execute code from an arbitrary numeric address merely because the CPU's instruction pointer contains that number.
Loading simulation...
A machine instruction rarely contains the complete address of a C variable. It usually tells the CPU how to calculate an effective address from registers, constants, and sometimes an index.
Consider:
If priority is eight bytes from the beginning of struct request, compiled code might conceptually calculate:
In a virtual-memory system, the result is a virtual address. The instruction does not calculate a physical address and does not know how the object is placed in RAM.
The same applies to common addressing operations:
All of these calculations remain within the process's virtual address namespace. Pointer arithmetic changes the virtual address; it does not manipulate the physical placement behind that address.
This separation is important when debugging. A register shown by a debugger contains the virtual address used by the faulting process. The MMU may translate it, reject it, or cause the operating system to take action. The number is not a direct RAM coordinate.
The numeric virtual address is only one input to translation. The MMU also needs the current translation context and the type of access being attempted.
Conceptually:
Four inputs decide one outcome. Change any of them and the same virtual address can translate successfully or raise an exception.
The address-space context identifies the mappings that belong to the process currently executing. This is why the same virtual number can produce different results in different processes:
The access type matters because a mapping may allow reading but reject writing or execution. Privilege mode matters because the operating system can create supervisor-only mappings that kernel code may access but user-mode code may not.
Translation is therefore not a simple global function such as:
A more accurate model is:
The result can be a permitted physical address or a precise reason that the access cannot proceed.
Consider one mapped unit with the following properties:
The half-open interval includes 0x4000 but excludes 0x5000. Both intervals are 0x1000 bytes long.
Suppose an instruction reads from virtual address 0x412c. First find the offset within the virtual interval:
Apply the same offset to the physical start:
The complete decision is:
| Check | Result |
|---|---|
Does 0x412c lie in [0x4000, 0x5000)? | Yes |
| Is the requested read operation allowed? | Yes |
| Physical result | 0xa12c |
A write to 0x412c would also be permitted. An instruction fetch from the same address would be rejected because the mapping does not grant execute permission.
A read from 0x512c cannot use this mapping because that address falls outside the virtual interval. It might be covered by a different mapping, or it might be unmapped.
The arithmetic demonstrates two general properties of translation:
Modern systems divide an address space into many independently translated units. Their exact size and the organization of the mapping information are separate design questions. The MMU's role remains the same: find the translation selected by the virtual address, enforce its attributes, and produce the physical address.
The simplest useful translation design can be described with two privileged values:
The process uses virtual addresses starting at zero. For a virtual address V, the hardware checks:
If the check succeeds:
Suppose:
Then:
This design illustrates relocation and protection.
The process can use the same virtual addresses no matter where its allocation begins physically; changing base changes its physical placement. The limit check prevents the process from reaching outside its assigned interval.
The operating system must be the only software allowed to change base and limit. If user code could replace them, it could redirect its addresses into the kernel or another process.
A single base-and-limit pair is too restrictive for a modern process. It describes only one continuous virtual interval backed by one continuous physical interval. It cannot naturally represent sparse regions, separate code permissions, noncontiguous physical storage, or selective sharing. More capable MMUs generalize the same principles with many mappings.
The simple model is still valuable because it exposes the two jobs that every practical MMU must perform:
The MMU performs translation, but it does not decide which mappings a process should have. That is operating-system policy.
The kernel creates and changes mappings when it:
For each mapping, the kernel records information such as the virtual range, its physical association or current backing, and its access permissions. The hardware architecture defines the format that the MMU understands.
The kernel then installs privileged translation-control state for the address space that should be active. Ordinary user-mode instructions cannot replace this state or edit translation data in a way that grants themselves access.
This creates a clear division of responsibility:
The operating system is not called for every normal load, store, and instruction fetch. Such a design would make each memory access far too expensive. Once valid translation state is installed, the MMU applies it directly in hardware.
Kernel intervention is needed when mappings change or when the MMU encounters a condition that cannot be completed through the current translation state.
A translation carries more than a physical location. It also carries attributes that describe how the mapping may be used.
Common permission categories include:
The exact representation varies across CPU architectures, but the protection goals are consistent.
Suppose a process has these conceptual mappings:
Several addresses are mapped, but the operation determines whether access succeeds:
This hardware checking happens on the actual memory operation. A C cast cannot override it:
The cast constructs a virtual pointer. It neither changes the mapping nor grants write permission. If 0x4100 belongs to a read-and-execute mapping, the MMU rejects the store.
Protection also applies while the kernel is mapped into an address space. User-mode code may know or guess a kernel virtual address, but the privilege check prevents it from using a supervisor-only translation. Knowledge of an address is not authority to access it.
When the scheduler switches a CPU from a thread in Process A to a thread in Process B, the CPU registers and execution state change. If the processes have different address spaces, the active translation context must change as well.
The number 0x6000 did not change. Its interpretation changed because the active context changed.
Threads in the same process normally share one address space. Switching between them changes registers and stacks, but it does not require selecting a different set of process mappings. Switching between different processes normally does.
The context-selection operation is privileged. If Process B could ask the MMU to use Process A's context directly, process isolation would disappear.
Real processors optimize translation heavily, so changing contexts involves more than replacing one conceptual mapping pointer. The important contract here is that the MMU always interprets a user access using the address space the kernel selected for the currently running process.
The MMU does not turn off merely because the CPU enters the kernel. Kernel instructions also use virtual addresses.
When a system call, interrupt, or exception transfers control to the operating system, the CPU changes to a privileged execution mode. The kernel can then access supervisor-only mappings that user mode cannot use.
Conceptually:
The exact arrangement differs across operating systems and architectures. Some kernel mappings may be present in many process contexts, while other designs switch or restrict mappings more aggressively. In all cases, privilege is checked by hardware rather than trusted to an application's good behavior.
When the kernel reads data through a user-provided pointer, it must treat that pointer as belonging to the requesting process's virtual address space. Kernels use guarded access routines because the address may be unmapped, may lack the requested permission, or may cease to be usable under concurrent activity.
The MMU therefore protects the kernel from user mode while also giving the kernel controlled ways to operate on process memory.
For a normal access, translation succeeds without transferring control to the operating system. When the MMU cannot complete the access, the processor raises a synchronous exception.
Common reasons include:
The exception is synchronous because it is caused by the instruction currently executing. If the same instruction runs again under the same conditions, it reaches the same translation problem.
Conceptually, the hardware provides the kernel with:
The kernel then interprets the condition using its higher-level knowledge of the process's address space.
Some conditions are recoverable. The kernel can update the required state and retry the instruction, which then completes as though the temporary interruption had not occurred.
Other conditions represent invalid program behavior. On Linux, the kernel commonly delivers SIGSEGV for an unmapped access or a protection violation. The signal is an operating-system response to the hardware exception; the MMU itself does not send Unix signals.
A translation exception therefore does not automatically mean that the process will crash. It means the fast hardware path could not complete the access and the kernel must decide what the condition means.
Suppose a crash report says:
A common cause is a null-derived field access. If a structure pointer is null and the desired field is 0x18 bytes into the structure, the CPU calculates:
The MMU does not find a readable user mapping at that address and raises an exception. No ordinary physical address is produced for the load.
Now consider:
The address may fall inside a valid mapping containing program instructions. Translation exists, but the mapping may permit only reading and execution. The write fails because of a permission mismatch rather than because the address is unmapped.
A useful diagnostic sequence is:
This distinguishes two broad cases:
A mapping with suitable permissions still does not prove that the program's access was valid according to the programming language. The pointer may refer to a released object or an out-of-bounds location that happens to remain inside writable mapped memory.
The MMU does not allocate memory when an application calls malloc(). It does not decide which process should receive physical memory, which regions should be shared, or which inactive contents should remain resident.
Those are operating-system and runtime decisions. The MMU enforces the translation state currently presented to it.
This distinction prevents several mistaken explanations:
The layers cooperate but solve different problems. An allocator can return a virtual pointer from a region already available to the process without changing MMU state for that individual object. Conversely, the operating system can change a mapping while the application's allocator remains unaware of its physical placement.
The MMU understands mapped address units and hardware permissions. It does not understand C objects, Java objects, ownership, garbage-collector reachability, or the requested size recorded by malloc().
One alternative would be to insert a software check before every memory operation:
This approach is inadequate for ordinary native applications. Programs execute enormous numbers of instruction fetches, loads, and stores. Requiring a system call or general kernel routine for each one would dominate execution time.
It would also be unsafe to trust the application to perform its own checks. A buggy or malicious program could skip them and issue a direct access.
Hardware assistance solves both problems:
The operating system defines policy at relatively infrequent mapping events. The MMU enforces that policy at memory-access speed.
The CPU generates virtual addresses for instruction fetches, loads, and stores. The MMU combines each virtual address with the active process context, requested operation, and privilege mode. It either produces a permitted physical address or raises a synchronous exception.
The operating system defines mappings, permissions, and active address-space contexts. The MMU enforces those decisions in hardware without invoking the kernel for every successful memory access. When translation cannot complete, the kernel decides whether to repair the condition and retry the instruction or report an invalid access.
Address translation therefore has two inseparable purposes: relocation and protection. It lets processes use stable virtual addresses while physical placement changes, and it ensures that knowing an address does not grant permission to access it.
5 quizzes