Introduction
Welcome to the fourth module of our course, where we will dive deep into one of the most fundamental components of any modern operating system: Memory Management. In the previous module, you mastered kernel concurrency, culminating in resolving complex deadlock scenarios. Now, we shift our focus from how the kernel manages CPU time to how it manages memory, a resource just as critical and contended.
This lesson begins our journey by addressing the learning outcome: Explain the virtual memory model, including virtual address spaces, page tables, and the MMU's role.
Understanding virtual memory is non-negotiable for advanced kernel work. It is the mechanism that provides process isolation, enables efficient use of physical RAM, and forms the bedrock for features like fork(), shared libraries, and demand paging. For your goals, a solid grasp of this model is crucial for debugging, performance tuning, and answering the deep architectural questions you'll face in top-tier interviews.
1. The Problem: Why Not Use Physical Memory Directly?
Imagine a simple system where programs directly access physical RAM. A few critical problems immediately arise:
- No Isolation: A buggy application could write over the memory of another application, or worse, the kernel itself, leading to a system-wide crash.
- Inefficient Allocation: As programs start and stop, physical memory becomes fragmented into small, unusable holes. A new program might not be able to start, even if there's enough total free memory, because there isn't a large enough contiguous block.
- No Flexibility: Every program needs to be loaded into memory in its entirety before it can run, which is slow and wasteful if the program only uses a fraction of its code.
Virtual memory is the operating system's elegant solution to all these problems.
2. Virtual Memory: The Core Concepts
The central idea of virtual memory is to give each process its own private, independent view of the system's memory. This view is called the virtual address space. From the process's perspective, it has a large, contiguous block of memory all to itself.
In reality, this is an illusion maintained by the OS and the CPU's Memory Management Unit (MMU). The kernel maps pages from this virtual address space to actual page frames in physical RAM.
Let's watch a short video that introduces these foundational ideas.
Linux Kernel Internals: Memory Management
This video from Maple Circuit provides an excellent introduction to virtual memory, the distinction between virtual and physical addresses, and how memory is divided into pages.
Watch the first two segments. The first part (00:38 - 01:42) explains the concept of virtual memory and per-process address spaces. The second part (01:51 - 02:58) introduces the concepts of page frames and pages.
To summarize the key terminology from the video:
- Virtual Address: An address within a process's private address space.
- Physical Address: The actual address on the physical RAM hardware.
- Page: A fixed-size block of virtual memory (commonly 4KB on x86-64).
- Page Frame: A fixed-size block of physical memory, corresponding in size to a page.
The fundamental job of the memory management subsystem is to manage the mapping between virtual pages and physical page frames.
3. The Translator: MMU and Page Tables
The translation from a virtual address to a physical address is performed by a piece of hardware called the Memory Management Unit (MMU), which is typically part of the CPU. The OS is responsible for setting up data structures, called page tables, that the MMU uses to perform these translations.
3.1. The Page Table Hierarchy
A simple, single-level page table that maps every possible virtual page to a physical frame would be enormous and wasteful. For a 64-bit address space, it's completely impractical.
To solve this, modern CPUs use a multi-level (or hierarchical) page table. A virtual address is split into several parts. Each part serves as an index into a different level of the page table, with the final level pointing to the physical page frame.

Let's see this process in action.
Linux Kernel Internals: Memory Management
Let's return to the Maple Circuit video. This segment visually demonstrates the MMU, the role of the TLB (which we'll discuss next), and the multi-level page table walk.
Watch from 03:04 to 07:50. Pay close attention to how the virtual address is used as a set of indices to navigate from the Page Global Directory (PGD) down to the Page Table Entry (PTE).
3.2. Linux Page Table Levels
The video used the x86-64 architecture as an example. The Linux kernel has a generalized model to support multiple architectures, which can have different numbers of page table levels. The canonical 5-level hierarchy is:
- Page Global Directory (PGD): The top-level directory.
- P4 Directory (P4D)
- Page Upper Directory (PUD)
- Page Middle Directory (PMD)
- Page Table Entry (PTE): The final level. A PTE contains the physical address of the page frame (if present) and various permission/status bits.
Architectures that don't need all five levels can "fold" them, meaning a higher-level directory points directly to a lower-level one, skipping the intermediate steps. For example, a 4-level system might fold the P4D.
For an authoritative, detailed description of this hierarchy, the official kernel documentation is the best source.
Page Tables - The Linux Kernel documentation
This document from the Linux kernel documentation provides the canonical description of the page table hierarchy and the role of the MMU.
Read the sections 'Page Tables' and the descriptions of the different levels (pte, pmd, pud, p4d, pgd). Don't worry about 'Page Table Folding' for now. Focus on the purpose of each layer and the definition of a Page Frame Number (pfn).
The bits within a pte_t are crucial. They tell the MMU not just where the page is, but also its status. Key bits include:
- Present bit: Is this page currently in physical RAM?
- Read/Write bit: Can the page be written to, or is it read-only?
- User/Supervisor bit: Can this page be accessed by user-space code, or only by the kernel?
- Accessed bit: Has this page been read or written to recently?
- Dirty bit: Has this page been written to?
- No-Execute (NX) bit: Can code on this page be executed? (A key security feature).
4. When Translations Fail: The Page Fault
What happens if the MMU walks the page tables and finds a PTE that indicates the page is not present in physical memory? It can't complete the translation. Instead of crashing, it triggers a special type of exception called a page fault.
The CPU traps into the kernel, which now must handle the fault. A page fault is not necessarily an error; it's a fundamental part of how virtual memory works.

Common, valid reasons for a page fault include:
- Demand Paging: The kernel uses lazy allocation. When you launch a program, its pages are not loaded from the executable file into RAM all at once. They are only loaded when the program first tries to access them, triggering a page fault for each new page.
- Copy-on-Write (COW): When a process uses
fork(), the kernel doesn't immediately copy all the parent's pages. Instead, it shares them and marks them as read-only. If either the parent or child tries to write to a shared page, a page fault occurs. The kernel then creates a private copy of that page for the writing process. - Swapping: If the system is low on memory, the kernel might move an inactive page from RAM to a swap file on disk to free up a page frame. If a process later tries to access that swapped-out page, a page fault occurs, and the kernel must read the page back from disk into RAM.
Of course, a page fault can also be a genuine error, such as a process trying to access an address that is not part of any valid memory region (e.g., a NULL pointer dereference). In this case, the kernel sends a segmentation fault signal (SIGSEGV) to the process, which usually terminates it.
Tutorial: Linux Memory Management and Containers - Gerlof Langeveld, AT Computing
Let's watch a section from a Linux Foundation tutorial that explains demand paging and the difference between virtual and resident memory size.
Watch the segment from 31:21 to 36:04. This provides a clear, step-by-step explanation of how demand paging works through page faults and clarifies the important distinction between a process's virtual size and its actual resident size (RSS).
5. Making it Fast: The Translation Lookaside Buffer (TLB)
Walking the multi-level page tables for every single memory access would be far too slow. To avoid this, the MMU contains a small, extremely fast cache called the Translation Lookaside Buffer (TLB).
The TLB stores recently used virtual-to-physical address translations.
- On a memory access:
- The MMU first checks the TLB for a valid translation for the virtual address.
- TLB Hit: If a translation is found, the physical address is generated immediately. This is the fast path.
- TLB Miss: If no translation is found, the MMU must perform a full, slow page table walk to find the translation. Once found, it stores the new translation in the TLB (potentially evicting an older one) for future use.
Because programs tend to access memory in localized patterns (temporal and spatial locality), the TLB is highly effective. A high TLB hit rate is critical for system performance.
6. Interview Scenario
Your knowledge of embedded systems gives you a strong hardware-oriented perspective. An interviewer might ask you to connect the high-level software concept of a pointer to the low-level hardware reality.
Question: "Describe the complete journey of a memory access in a user-space Linux application, starting from a C statement like *p = 10;. Detail the roles of the compiler, kernel, and hardware (MMU, TLB, RAM)."
Click to see the analysis
A strong answer would break this down into clear stages:
-
Compile Time: The C compiler translates
*p = 10;into a machine code instruction, likeMOV [address], 10. Theaddresshere is a virtual address within the process's address space. -
Execution Time - The CPU and MMU take over:
- The CPU fetches and begins to execute this instruction.
- It presents the virtual address to the MMU for translation.
-
The Fast Path - TLB Hit:
- The MMU first checks its TLB (the translation cache).
- If the translation for the page containing this virtual address is in the TLB (a "hit"), the MMU immediately gets the corresponding physical page frame number.
- It combines this with the offset from the original virtual address to form the final physical address.
- The MMU checks the permission bits in the cached TLB entry. Since this is a write operation, it verifies the 'Write' bit is set.
- Assuming permissions are okay, the value
10is written to the correct location in physical RAM. The journey ends here.
-
The Slower Path - TLB Miss:
- If the translation is not in the TLB (a "miss"), a hardware page table walk is initiated.
- The MMU uses the
CR3register (on x86) to find the base address of the PGD for the current process. - It then walks the page table hierarchy (PGD -> P4D -> PUD -> PMD -> PTE) using parts of the virtual address as indices at each level.
-
Page Table Walk Resolution:
- Case A: Valid PTE Found. The PTE is valid and present. The MMU retrieves the physical page frame address. It caches this new translation in the TLB and then proceeds as in step 3. It also sets the 'Accessed' and 'Dirty' bits in the PTE.
- Case B: Page Fault. The PTE indicates the page is not present (or the access violates permissions). The MMU triggers a page fault exception, trapping into the kernel.
-
Kernel Page Fault Handler:
- The kernel's fault handler inspects the virtual address and the process's memory map (
vm_area_structs). - If it's a valid access to a page that's just not in RAM (e.g., demand paging), the kernel allocates a physical page frame, loads the data from the executable file on disk, updates the PTE to point to the new frame with correct permissions, and then returns.
- If it's an invalid access (e.g., to an unmapped area), the kernel sends a
SIGSEGVsignal to the process.
- The kernel's fault handler inspects the virtual address and the process's memory map (
-
Retry: After the kernel handles a valid fault, it returns control to the user process. The instruction that caused the fault is re-executed. This time, the MMU's TLB miss and page table walk will succeed (Case A), and the memory access completes.
Conclusion
In this lesson, we have laid the essential groundwork for understanding Linux memory management. You now have a mental model for how the kernel and hardware collaborate to provide every process with a safe, private, and flexible virtual address space.
Key Takeaways:
- Virtual memory solves the problems of isolation, security, and efficiency by abstracting physical RAM.
- Each process gets a private virtual address space, which is mapped by the OS to physical page frames.
- The MMU is the hardware that performs virtual-to-physical address translation using hierarchical page tables set up by the kernel.
- A page fault is a trap into the kernel that occurs when the MMU cannot perform a translation. It is a core mechanism for demand paging, COW, and swapping, not just an error condition.
- The TLB is a critical hardware cache that makes virtual memory performant by storing recent address translations.
You have now mastered the "demand" side of memory management—how processes see and request memory. In our next lesson, we will explore the "supply" side: "Describe the kernel's physical memory management using zones and the buddy allocator." We'll investigate how the kernel keeps track of all the physical page frames it has available to satisfy these demands.