Describe the role of the page table entry (PTE) in a page table

Describe the role of the page table entry (PTE) in a page table

If you zoom all the way into the machinery of virtual memory, past the TLB, past the multi-level page table structure, you eventually land on the smallest meaningful unit of the whole system: the Page Table Entry, or PTE. Every single virtual-to-physical mapping in a running system ultimately comes down to one of these small structures. I want to go through exactly what a PTE contains, how it’s used, and why its individual bits matter far more than their small size suggests.

Where the PTE Sits in the Page Table Hierarchy

On x86-64 Linux, address translation walks through up to four (or five, with 5-level paging on newer hardware) levels of tables before reaching the actual page frame:

PGD (Page Global Directory)
   -> PUD (Page Upper Directory)
       -> PMD (Page Middle Directory)
           -> PTE (Page Table Entry)   <-- final level
               -> Physical page frame

Every level above the PTE is really just a table of pointers to the next level down. The PTE is the only one that actually points to real data memory rather than to another table (with the exception of hugepage entries, discussed below, which terminate the walk one or two levels early). It’s the final, authoritative answer to “where does this virtual page actually live in physical RAM, and what am I allowed to do with it.”

What’s Inside a PTE

A PTE is typically a single machine word (64 bits on x86-64), tightly packed with both the physical frame address and a set of control bits. The exact layout is architecture-specific, but on x86-64 it looks roughly like this:

 63     62  ...  12   11 ... 9    8   7   6   5   4   3   2   1   0
+----+-------------+---------+---+---+---+---+---+---+---+---+---+
| NX | Physical     | Avail   |G  |PAT|D  |A  |PCD|PWT|U/S|R/W|P  |
|    | Frame Number |(for OS) |   |   |   |   |   |   |   |   |   |
+----+-------------+---------+---+---+---+---+---+---+---+---+---+

The key fields worth knowing:

  • Present (P) — whether this entry currently points to a valid physical frame. If clear, any access triggers a page fault, and the OS is free to store whatever it wants in the rest of the entry (commonly, information about where the page lives on swap).
  • Read/Write (R/W) — whether writes are allowed. This is exactly the bit copy-on-write mappings clear to force a fault on the first write.
  • User/Supervisor (U/S) — whether user-mode code is allowed to access this page at all, or whether it’s kernel-only. This is the hardware-enforced boundary that keeps user processes out of kernel memory.
  • Page Write-Through (PWT) / Page Cache Disable (PCD) — control caching behavior for this specific page, important for memory-mapped device registers where caching would be actively wrong.
  • Accessed (A) — set automatically by the CPU whenever the page is accessed, letting the OS implement page-replacement algorithms (like approximations of LRU) without having to trap every single access.
  • Dirty (D) — set automatically by the CPU on the first write, letting the OS know this page’s contents differ from whatever’s on backing storage and must be written back before the frame can be reused for something else.
  • NX (No-Execute) — when set, instructions can’t be fetched from this page, a critical hardware-enforced mitigation against classic code-injection exploits that rely on executing attacker-supplied data.
  • Physical Frame Number — the actual payload: the upper bits of the physical address this virtual page maps to.
  • Available/OS-reserved bits — several bits are explicitly left for the OS to use however it wants (Linux, for instance, uses some to help track swap information).

Why the Accessed and Dirty Bits Matter So Much

These two bits deserve special attention because they quietly enable some of the most important memory management decisions the OS makes, entirely in hardware, with zero software overhead per access.

Without the Accessed bit, the OS would have no cheap way to know which pages are being actively used and which are sitting idle — information it desperately needs to decide what to evict under memory pressure. Instead of trapping every memory access (which would be catastrophically slow), the CPU silently sets this bit whenever it walks a PTE to satisfy an access. The OS periodically scans and clears these bits, and pages that get re-marked “accessed” between scans are considered hot; pages that stay untouched are strong eviction candidates. This is the hardware foundation underneath Linux’s active/inactive LRU-ish page reclaim lists.

Without the Dirty bit, the OS would have to assume every resident page might have been modified, forcing it to write every evicted page back to disk/swap even if it was never actually changed — hugely wasteful for read-only or unmodified pages. The Dirty bit lets the kernel skip that write-back entirely for clean pages, which is a meaningful I/O savings on any real workload.

PTEs and Hugepages

Normally, the walk goes all the way down to a PTE describing a single 4KB page. But architectures supporting large pages let a PMD (or even PUD) entry directly describe a mapping to a large contiguous physical region — 2MB at the PMD level, 1GB at the PUD level on x86-64 — terminating the walk one or two levels early and skipping the PTE level entirely for that mapping. This is exactly how hugepages reduce TLB pressure: one PMD-level “PTE-equivalent” entry now covers 512 times the address space a standard PTE would, meaning far fewer entries are needed to map a large working set, and far fewer TLB misses occur as a result.

How the OS Manipulates PTEs

The kernel doesn’t just read PTEs passively — huge amounts of memory management logic boil down to setting, clearing, or checking specific PTE bits:

  • mprotect() changes a page’s permission bits (R/W, execute) directly in the relevant PTEs.
  • Swapping out a page clears the Present bit and repurposes the remaining bits to record where on the swap device the data now lives.
  • Copy-on-write clears the Read/Write bit on both parent’s and child’s shared PTEs after fork().
  • madvise(MADV_DONTNEED) can cause the kernel to invalidate PTEs for the specified range, forcing future accesses to fault and be re-satisfied (potentially with zeroed pages, for anonymous memory).
  • Page reclaim (kswapd/direct reclaim) periodically walks PTEs checking and clearing the Accessed bit as part of its aging logic for deciding what to evict.

PTEs on Other Platforms

The concept is universal even though the exact bit layout differs:

  • ARM (used in Android and iOS devices) has its own page table entry formats, with analogous access-permission, execute-never (XN), and access/dirty-tracking bits, though the specific encoding and multi-level table structure (translation tables) differ from x86-64.
  • Windows, running primarily on x86-64 and ARM64, uses the same underlying hardware PTE formats as Linux since it’s constrained by the same CPU architecture — the OS-level data structures wrapping them (the PFN database, working set lists) differ, but the hardware PTE bits mean the same thing.
  • Classic UNIX systems on various architectures (SPARC, PA-RISC, MIPS) each defined their own PTE formats, but every one of them needed the same conceptual fields: a physical frame reference, permission bits, and presence/validity indication.

Troubleshooting and Inspection

  • On Linux, /proc/<pid>/pagemap exposes per-page mapping information (including whether a page is present, and its physical frame number if resident) for a process, usable for advanced memory debugging.
  • /proc/<pid>/smaps breaks down memory regions with more human-readable detail — resident set size, whether pages are shared, and more — derived ultimately from the same underlying PTE state.
  • Tools like pagemap-reading utilities or crash/gdb on kernel dumps let you inspect raw PTE values when debugging memory corruption or unexpected access violations at a very low level.

Best Practices

  • Understand that permission changes (mprotect) and unmapping (munmap) directly translate to PTE modifications — and each one potentially requires a TLB invalidation, so batching such changes where possible avoids unnecessary overhead.
  • When investigating unexplained memory behavior, remember the Accessed and Dirty bits are hardware-maintained; software-side logic (kswapd, reclaim heuristics) only interprets them periodically, so there’s inherent staleness in “hotness” tracking that’s worth keeping in mind.
  • For workloads sensitive to TLB pressure, understanding that hugepages skip the PTE level entirely (terminating at PMD/PUD) is the key mental model for why they help.

Summary

The Page Table Entry is deceptively small — a single machine word on most architectures — but it’s the actual, final source of truth for every virtual memory mapping in the system: where a page physically lives, whether it’s present, what’s allowed to touch it, and whether it’s been read or written recently. Nearly every high-level virtual memory feature you’d recognize — copy-on-write, swapping, memory protection, page reclaim, hugepages — ultimately comes down to the OS reading or manipulating specific bits within these entries. Small structure, enormous responsibility.

FAQs

What information does a PTE actually store? A physical frame number plus control bits: present/valid, read/write permission, user/supervisor access level, accessed, dirty, cacheability, and no-execute, among others (exact layout is architecture-specific).

What is the Accessed bit used for? It lets the CPU mark, in hardware and with no extra overhead, which pages have been recently touched, giving the OS the information it needs to decide what to evict under memory pressure without trapping every access.

How do hugepages relate to PTEs? Hugepages let a higher-level table entry (PMD or PUD) directly describe a large contiguous mapping, terminating the page table walk before reaching the PTE level and covering far more address space per entry, reducing TLB pressure.

Does every operating system use the same PTE format? No — the format is defined by the CPU architecture (x86-64, ARM, etc.), not the OS, though every format serves the same conceptual purpose: physical address plus permission and status bits.

References

  • Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, Chapter 4
  • AMD64 Architecture Programmer’s Manual, Volume 2
  • Linux kernel documentation, Documentation/mm/page_table_check.rst and related paging docs
  • Arm Architecture Reference Manual, translation table descriptor format sections
Total
0
Shares

Leave a Reply

Previous Post
Explain the concept of address translation in the context of virtual memory

Explain the concept of address translation in the context of virtual memory

Next Post
How does the operating system handle a page fault during address translation

How does the operating system handle a page fault during address translation

Related Posts