Use the two-level address translation model and map exactly four resident virtual memory pages, numbered 0x12, 0x13, 0xa0, and 0xa1. Assign their data page frames to 0x07, 0x02, 0x05, and 0x06, respectively. Only two root indices occur: 1 and 10. Allocate exactly three table nodes: the root at 0x8000, the lower-level table for root index 1 at 0x9000, and the lower-level table for root index 10 at 0xa000. Root entries 1 and 10 reference those tables; all other root entries are absent. Lower-level entries 2 and 3 in the first table and entries 0 and 1 in the second table hold the four mappings; every other lower-level entry is absent. Use the read-only permissions already specified for virtual memory page 0x12; let the other three mappings permit user reads and writes, with no execution, and let both populated root entries permit user reads and writes.
Each root index covers 16 virtual memory pages, hence bytes of the address space. A compact table node occupies bytes. The three nodes therefore require bytes of table storage, including the root and all absent entry slots in the allocated nodes. A single-level array requires bytes for the same address space, saving bytes in the compact model. Four data page frames occupy another bytes; those contents are separate from table storage.
If instead the allocator dedicates a whole 256-byte page frame to every table node, the three nodes consume 768 bytes, leaving bytes unused within those allocations. The single-level table fills four such page frames and still consumes 1024 bytes. Thus this example saves 256 bytes of allocated table storage under that alternative policy. Neither accounting includes allocator metadata, operating-system process records, or translation lookaside buffer storage; data allocations are the same in both designs.
Figure 1.
All table nodes for four mapped pages, and the footprint change when the same four pages occupy four root regions
. Details.
Placement matters as well as mapping count. Four virtual memory pages 0x12, 0x42, 0x72, and 0xa2 require the root plus four lower-level tables: five nodes, 320 compact bytes, or 1280 bytes with a dedicated page frame per node. Only four data page frames are still required. Let be the number of distinct root indices represented by the mappings. With no retained empty lower-level tables, table storage is compact bytes or allocated bytes under the dedicated-page frame policy. Even an empty address space retains the root in this accounting, so costs 64 or 256 bytes.
For a fully populated address space, : 17 nodes cost 1088 compact bytes or 4352 allocated bytes. The single-level table remains 1024 bytes. Sparse allocation is therefore a conditional space benefit; scattered mappings, node allocation granularity, and the root's overhead can reverse it. Additional dependent lookups on a TLB miss also have a cost, but node counts alone establish no universal execution-time ordering.
Sv39 page table 2026-10-14
The actual Sv39 page table geometry uses three nine-bit indices and a twelve-bit offset for base 4096-byte memory pages. Each table contains 512 eight-byte page table entries, exactly one 4096-byte memory page; the satp processor register identifies the root. A 64-bit virtual address must repeat bit 38 in bits 63 through 39. Higher-level terminal entries can also map aligned larger memory pages, shortening the page table walk. These rules differ from the 16-bit, four-byte-entry two-level address translation model. [ref]
For ordinary Sv39 page tables, valid nonterminal page table entries reference the next table with read, write, and execute flags clear; those clear flags do not prohibit reading the eventual data. The terminal entry supplies access permissions, with additional processor privilege level and status checks. An invalid entry, malformed encoding, or denied access can cause a page fault. Thus the model's set intersection of read/write permissions at both levels must not be applied mechanically to Sv39 page tables. [ref]
In xv6 RISC-V source code, root allocation is separate from lower-level allocation: uvmcreate() allocates and clears a root page table, while walk() with allocation enabled allocates and clears missing lower-level tables and installs valid references to them. With allocation disabled, a missing table makes walk() return failure. This software routine finds a page table entry for the operating system; it is not the hardware's complete permission-checking page table walk. [ref]