How Linux Handles Page Faults During Execution
This article provides an overview of how the Linux kernel manages page faults when applications run. A page fault is a hardware interrupt raised by the CPU's Memory Management Unit (MMU) when an application attempts to access a virtual memory page that is not currently mapped to physical RAM. Linux resolves these interrupts dynamically through a series of checks, hardware-to-kernel handoffs, and memory-mapping strategies, categorizing each event as a minor fault, a major fault, or an invalid access violation.
The Trigger: Virtual to Physical Address Translation
Linux uses virtual memory, isolating processes by granting each its own virtual address space. The CPU's Memory Management Unit (MMU) translates these virtual addresses into physical addresses using page tables.
When a process references an address whose corresponding Page Table Entry (PTE) is marked as "not present" or violates access permissions (such as writing to a read-only page), the MMU cannot complete the translation. The hardware immediately pauses the offending instruction and triggers a page fault exception.
The Architecture Exception Handler
When the page fault occurs, the CPU switches to kernel mode and jumps
to the architecture-specific fault handler, such as
do_page_fault() on x86 architectures. The CPU registers
pass critical details to the kernel:
- Faulting Address: The virtual address that
triggered the fault (stored in the
CR2register on x86). - Error Code: Hardware-generated flags indicating whether the access was a read or write, executed in user or kernel mode, and whether a translation table entry was present.
- Instruction Pointer: The memory address of the instruction that caused the fault.
Evaluating the Memory Region (VMA)
The Linux kernel checks the process's memory descriptor
(mm_struct) and searches its Virtual Memory Areas (VMAs) to
determine whether the requested address falls within a legally allocated
region.
- Invalid Memory Access: If the address does not
belong to any valid VMA, or if the access violates the region’s
permissions (e.g., attempting to write to a read-only code segment), the
kernel flags an invalid access. The kernel sends a
SIGSEGV(Segmentation Fault) signal to the process, which typically terminates the application unless a custom signal handler is configured. - Valid Access: If the address is within a legal VMA
and the access permissions match, the kernel proceeds to resolve the
missing page using
handle_mm_fault().
Types of Page Faults
Linux classifies valid page faults into two primary categories based on whether disk access is required:
Minor (Soft) Page Faults
A minor page fault occurs when the data required already resides in physical memory, but the process's page tables have not yet established a mapping. Common scenarios include:
- Page Cache Hits: A file-backed page is already loaded in RAM because another process accessed it.
- Shared Libraries: Dynamic libraries like
glibcthat are shared across multiple running processes. - Demand Zero Allocation: Newly allocated memory via
malloc()ormmap()is not assigned physical frames until accessed for the first time. The kernel maps these directly to a pre-cleared, zero-filled physical page without performing disk I/O.
Because minor faults avoid storage operations, the kernel resolves them quickly by updating the PTE and assigning the existing physical frame.
Major (Hard) Page Faults
A major page fault occurs when the required page is not in physical RAM and must be read from disk. This happens when:
- Code or data from an executable binary is accessed for the first time.
- Memory mapped files need to be retrieved from persistent storage.
- Pages that were previously swapped out to disk or zswap must be restored.
During a major fault, the kernel initiates an asynchronous I/O
operation to read the page from disk into a newly allocated physical
page frame. Because disk I/O is slow, the kernel places the faulting
process into a sleeping state (TASK_UNINTERRUPTIBLE or
TASK_INTERRUPTIBLE) and schedules other tasks to run on the
CPU.
Copy-on-Write (COW) Faults
When a process calls fork(), child processes share the
parent’s physical pages to save memory, with all shared pages marked as
read-only. When either process attempts to write to one of these shared
pages, a page fault occurs. The kernel catches the permission mismatch,
allocates a new physical frame, duplicates the contents from the
original page, updates the faulting process's page table to point to the
new frame, and sets the permissions to read-write.
Resolution and Execution Resumption
Once the physical frame is allocated and populated:
- The kernel updates the corresponding Page Table Entry (PTE) with the physical frame address and marks the entry as present with the appropriate access flags.
- The kernel updates the Translation Lookaside Buffer (TLB) so the CPU caches the new translation.
- If the process was sleeping due to a major fault, the kernel marks the process as runnable, returning it to the runqueue.
- The kernel switches execution context back to user mode, restarting the exact instruction that previously faulted. With the page table entry now present, the instruction completes without interruption.