B11 — Modern Computer Memory Layout: Complete Virtual to Physical Address Mapping

Key Insight: Understanding memory layout is the key to understanding system design.


Evolution from Physical to Virtual Addressing

Early computers (before the 80286) had no MMU. They directly used physical addresses. Later, segmentation was introduced, then paging, making memory layouts increasingly complex.

Bash
内存布局的演进:

1. 纯物理地址(80286 之前):
   - 没有 MMU
   - 物理地址 = 访问地址
   - 无法实现进程隔离

2. 实模式(8086):
   - 段寄存器 × 16 + 偏移 = 20-bit 物理地址
   - 1MB 地址空间
   - 无保护,无隔离

3. 保护模式(80286+):
   - GDT/LDT 定义段描述符
   - 段选择子 + 偏移 = 线性地址
   - 特权级(Ring 0/1/2/34. 分页保护模式(80386+):
   - 线性地址通过页表翻译成物理地址
   - 虚拟内存支持
   - 每个进程独立的页表

5. x86-64 长模式(AMD64):
   - 48-bit 虚拟地址(用户 128TB,内核 128TB)
   - 四级页表
   - 不再使用段寄存器(CS/DS 等仍是选择子,但基址=0

Complete x86-64 Virtual Address Space Layout

Bash
x86-64 虚拟地址空间布局(Linux 5.x+):

用户空间(User Space,0x0000000000000000 ~ 0x00007FFFFFFFFFFF):
  0x0000000000000000:NULL 指针陷阱(防止 NULL deref)
  0x0000000000400000:可执行文件加载地址(ELF)
  0x0000004000000000:程序堆(brk 增长)
  0x00007F0000000000:mmap 区域(匿名映射/共享库)
  0x00007FF000000000:栈(向低地址增长)
  0xFFFF800000000000:内核空间起始(符号扩展)

内核空间(Kernel Space,0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF):
  0xFFFF800000000000 ~ 0xFFFF88007FFFFFFF:
    直接映射区(Direct Map)
    → 线性映射:物理地址 + 0xFFFF880000000000 = 虚拟地址
    → 物理 0~896MB 直接映射到这个区域(低端 896MB)
    → 适用于 kmalloc/vmalloc(物理连续)

  0xFFFF880100000000 ~ 0xFFFFC3FFFFFFFFFFFF:
    vmalloc 区域
    → 映射不连续的物理内存(vmalloc)
    → module 空间(0xFFFF880000000000 ~ 0xFFFF8BFFFFFFFFFF)
    → ioremap(0xFFFFC40000000000 开始)

  0xFFFFC40000000000 ~ 0xFFFFC7FFFFFFFFFFF:
    固定映射区(Fixed Address Mapping)
    → early boot、ACPI tables、物理内存映射

  0xFFFFC80000000000 ~ 0xFFFFFFFFFFFFFFFF:
    保留区/硬件区
    → ACPI NVS
    → 固件保留
    → 未使用

Physical Address to Virtual Address Translation

Bash
物理地址到虚拟地址的几种方式:

1. 直接映射(0xFFFF880000000000):
   PA 0x1000000 → VA = 0x1000000 + 0xFFFF880000000000
               = 0xFFFF880100000000

   用于低端 896MB DRAM(kmalloc/vmalloc)

2. kmap(kmap_atomic):
   用于高端内存(HIGHMEM,> 896MB)
   - 临时映射
   -kunmap_atomic 释放

3. vmalloc 映射:
   PA 可以不连续
   VA 在 vmalloc 区域动态分配

4. ioremap:
   外设 MMIO 寄存器
   VA 在 ioremap 区域

物理地址范围的定义(arch/x86/include/asm/page_32_types.h):
  #define __PAGE_OFFSET      0xFFFF880000000000
  #define PHYSICAL_PAGE_MASK  (~0xFFFUL)

Detailed Linux Kernel Memory Layout

Bash
内核内存布局的关键区域(x86-644-level pagetable):

1. 直接映射区(ZONE_NORMAL,896MB):
   - 物理地址 0 ~ 896MB 映射到这里
   - 用于 kmalloc / 伙伴系统
   - 虚拟 = 物理 + __PAGE_OFFSET
   - 高效:无页表开销

2. vmalloc 区(动态映射):
   - 大约 32TB 虚拟空间(0xFFFF880100000000 ~ 0xFFFFC3FFFFFFFFFF)
   - 模块加载(0xFFFF880000000000 ~ 0xFFFF8BFFFFFFFFFF)
   - 适用:vmalloc / ioremap

3. 固定映射区:
   - 编译时确定的虚拟地址
   - 用途:early boot(VMALLOC_START ~ VMALLOC_END)
   - 编译时固定,不动态分配

4. 高端内存(HIGHMEM,32-bit x86):
   - 物理 > 896MB 的部分
   - 不能直接映射,需要临时映射(kmap)

物理内存 > 物理地址空间时:
  - 64-bit 系统不需要高端内存概念
  - 256TB 虚拟空间 >> 实际物理内存

/proc/iomem: Viewing Physical Address Layout

Bash
Linux 查看物理地址布局:

$ cat /proc/iomem

00000000-00000fff : Reserved
00001000-0009ffff : System RAM          ← 传统 640KB
000a0000-000fffff : Reserved            ← VGA/ROM
00100000-bfffffff : System RAM          ← 主内存区域
c0000000-feafffff : Reserved            ← MMIO 区域
  c0000000-cfffffff : PCI Bus 0000:01  ← PCIe BAR
  d0000000-dfffffff : RAM (系统内存,但 MMIO 占用)
  fe000000-fe7fffff : Local APIC        ← APIC
  fe800000-fe9fffff : Reserved          ← firmware
ff800000-ffffffff : Reserved            ← BIOS ROM

# 查看虚拟地址映射(/proc/self/maps)
$ cat /proc/self/maps
00400000-00405000 : r-xp /bin/cat         ← 可执行代码
00600000-00606000 : rw-p [heap]          ← 堆
7fffc8c00000-7fffc8c20000 : rw-p [stack] ← 栈
ffff8800000000-ffff8880000000 : vmalloc   ← vmalloc 区域

NUMA System Memory Layout

Bash
NUMA(Non-Uniform Memory Access)系统的内存布局:

2 路服务器(2 NUMA nodes):

Node 0(CPU 0~7 本地):
  物理地址 0x0 ~ 0x100_0000_0000(64GB)
  本地内存,访问延迟 ~60ns

Node 1(CPU 8~15 本地):
  物理地址 0x100_0000_0000 ~ 0x200_0000_0000(64GB)
  本地内存,访问延迟 ~60ns
  跨节点访问延迟 ~100ns

NUMA 配置的虚拟地址映射:
  - 内核直接映射区(0xFFFF880000000000)跨越所有节点
  - 但某个虚拟地址对应哪个节点取决于物理地址

Linux NUMA API:
  $ numactl --hardware
  available: 2 nodes (0-1)
  node 0 size: 65536 MB
  node 1 size: 65536 MB

  $ numactl --membind=0 ./program
  强制程序在 Node 0 分配内存(本地访问)

Summary

  • Memory Layout Evolution: Physical address → Real Mode segments → Protected Mode segments → Paging → x86-64 Long Mode
  • x86-64 Virtual Space: User 128TB (low) + Kernel 128TB (high); segment base = 0 (segmentation not used)
  • Kernel Direct Mapping: Physical + 0xFFFF880000000000; used for kmalloc (low 896MB)
  • vmalloc Region: 0xFFFF880100000000 to 0xFFFFC3FFFFFFFFFF; dynamic mapping; modules/ioremap
  • Fixed Mapping Region: Fixed at compile time; used for early boot/ACPI
  • NUMA: Each node has independent physical address space; local access is fast, cross-node access is slow (~100ns)

B-Layer Summary: B01-B11 covers the complete chain from BIOS/UEFI → Bootloader → e820 → SPD → Boot flow → Real Mode → Protected Mode → GDT/CR0 → MMU/Paging → Buddy System → memblock → vmalloc/kswapd → COW → Memory Layout.

Next (K01): After the Buddy System allocates physical pages, how are kernel objects allocated? SLAB/SLUB allocator design. Why object pools are needed and how allocation latency is reduced.


Last modified: 2024年8月14日

Author

Comments

Write a Reply or Comment

Your email address will not be published.