B01 —— BIOS 给操作系统的第一份地图:e820 内存映射表

金句:计算机启动时,操作系统第一个要解决的问题是——内存有多大、都在哪里。


开机后操作系统的第一个问题

计算机加电后,BIOS(UEFI)首先完成硬件初始化,然后操作系统开始启动。操作系统第一个要解决的问题是:物理内存有多大、在哪里、哪些区域可以用

Bash
启动顺序:

1. CPU 复位 → 执行 BIOS/UEFI(从 0xFFFF0 开始)
2. BIOS 执行 POST(Power-On Self-Test)→ 枚举内存设备
3. BIOS 读取 SPD → 知道每个 DIMM 的容量和速度
4. BIOS 构建 e820 内存映射表
5. BIOS 把控制权交给操作系统(bootloader)
6. 操作系统读取 e820 表 → 建立自己的内存管理

e820 表是 BIOS 和操作系统之间的"第一份合同"。

什么是 e820

e820 是 x86 架构的 BIOS 内存映射表,以 INT 15h 中断的 E820h 功能提供给操作系统。

Bash
e820 的全称:
  INT 15h, AX = E820h - "Get System Memory Map"

为什么叫 e820:
  - 功能号是 E820h(0xE820)
  - 调用这个中断,能获取完整的物理内存布局

返回的数据结构(SMAP):

Offset  Size    Description
------  ----    -----------
0x00    4B      Base Address(内存区域起始地址,64-bit)
0x08    8B      Length(内存区域长度,64-bit)
0x10    4B      Type(内存区域类型)

Type 取值:
  1 = AddressRangeMemory    (可用物理内存)
  2 = AddressRangeReserved   (保留/被外设占用)
  3 = ACPI Reclaim Memory    (ACPI 表,可以回收使用)
  4 = ACPI NVS Memory        (ACPI NVS,不可覆盖)
  其他  = 保留/不可用

e820 表的实际例子

Bash
用 dmesg 查看 e820 映射(Linux):

$ dmesg | grep -i "e820" | head -20

[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] e820:  0x0000000000000000 - 0x000000000009ffff (640 KB)
[    0.000000] e820:  0x0000000000100000 - 0x00000000bf79e000 (1918 MB)
[    0.000000] e820:  0x00000000bf79e000 - 0x00000000bf7de000 (240 KB)
[    0.000000] e820:  0x00000000bf7de000 - 0x00000000c0000000 (4224 KB)
[    0.000000] e820:  0x00000000c0000000 - 0x00000000fe000000 (960 MB)
[    0.000000] e820:  0x00000000fe000000 - 0x00000000ff000000 (16 MB)
[    0.000000] e820:  0x00000000ff000000 - 0x00000000fff00000 (15 MB)
[    0.000000] e820:  0x000000fff00000 - 0x000000ffff0000 (64 KB)

解析:
  0x0000000000000000 - 0x000000000009ffff (640 KB)         = 传统 640KB 区域(Type 1,可用)
  0x0000000000100000 - 0x00000000bf79e000 (1918 MB)       = 主内存区域(Type 1,可用)
  0x00000000c0000000 - 0x00000000fe000000 (960 MB)         = MMIO 区域(Type 2,保留)
  0x00000000fe000000 - 0x00000000ff000000 (16 MB)          = 固件区(Type 2,保留)
  0x00000000fff00000 - 0x00000000ffff0000 (64 KB)           = BIOS ROM(Type 2,保留)

总共 4GB 地址空间(0x0 ~ 0xFFFFFFFF)
  其中约 2.6GB 是可用物理内存,1.4GB 是 MMIO 保留区

为什么需要 e820:MMIO 区域的存在

e820 最重要的作用是告诉操作系统:哪些地址是给外设用的,不能当作 DRAM

Bash
MMIO(Memory-Mapped I/O)和 DRAM 的区别:

DRAM(物理内存):
  地址 0x10000000 ~ 0x17FFFFFF = 128MB DDR 内存
  → CPU 访问这个地址,内存控制器会读写 DRAM 颗粒

MMIO(外设寄存器):
  地址 0xE0000000 ~ 0xEFFFFFFF = PCIe BAR 或集成显卡显存
  → CPU 访问这个地址,内存控制器会把这个请求发到 PCIe 总线
  → 外设寄存器响应,但 DRAM 颗粒不被访问

BIOS 的职责:
  - 扫描 PCIe 总线,发现有哪些外设
  - 给每个外设分配 MMIO 地址范围(BAR 地址)
  - 把这些范围记录在 e820 里(Type=2,保留)

操作系统读取 e820:
  → 知道 0xE0000000 ~ 0xEFFFFFFF 不能用于内存分配
  → 把这段地址从物理页分配器中排除

e820 的构建过程

Bash
e820 是怎么生成的:

1. BIOS 读取 SPD(Serial Presence Detect)
   → 知道每个 DIMM 的容量(例如:8GB DDR4 × 2 = 16GB)
   → 构建"可用内存"条目(Type=12. BIOS 枚举 PCIe 外设
   → 读取每个设备的 BAR(Base Address Register)
   → 决定 MMIO 地址范围
   → 添加"保留"条目(Type=23. BIOS 分配 ACPI 地址
   → ACPI tables 放在固定地址(通常是 0xE0000000 以下)
   → ACPI Reclaim Memory(Type=3)和 ACPI NVS(Type=44. BIOS 添加固件区域
   → BIOS ROM 在 0xFFFE0000 ~ 0xFFFFFFFF(64KB)
   → SMM(System Management Mode)区域

最终 e820 是一个连续的"内存段"列表。

在代码中访问 e820

Bash
Linux 内核中的 e820 处理:

// arch/x86/include/asm/e820.h

struct e820_entry {
    __u64 addr;   // 起始地址
    __u64 size;   // 大小
    __u32 type;   // 类型
};

每次启动时,Linux 调用:
  e820__memory_setup() // 构建 e820
  e820__reserve_memory() // 标记保留区域
  e820__print_table() // 输出到 dmesg

C
// 用 INT 15h E820 读取内存映射的例子(x86 汇编)

mov ax, 0e820h      // AH = 0xE820, AL = 0xE820
mov edx, 534d4150h  // EDX = 'SMAP'(签名)
mov di, buffer       // ES:DI = 输出缓冲区
int 15h             // 调用 BIOS 中断
jc error            // 如果 CF=1,出错

// 返回的 buffer 格式:
// 偏移0:Base Address Low (4B)
// 偏移4:Base Address High (4B)
// 偏移8:Length Low (4B)
// 偏移12:Length High (4B)
// 偏移16:Type (4B)
// 偏移20:AcpiUid (4B,扩展用)

next_entry:
mov ax, 0e820h
mov di, buffer
int 15h
jnz done  // EAX=0 表示没有更多条目

为什么 32 位系统只显示 4GB 内存

Bash
32 位系统的内存寻址限制:

32 位地址最大寻址:2^32 = 4GB

e820 里的 4GB 地址空间分配:
  0x00000000 ~ 0xBFFFFFFF:DRAM(最高 3GB 可用)
  0xC0000000 ~ 0xFFFFFFFF:MMIO(固件/显卡/PCIe,1GB)

所以即使插了 4GB 内存,32 位系统只能用到 ~2.5~3GB
其他被 MMIO 占用(地址空间被 MMIO "偷走"了)

PAE(Physical Address Extension):
  - IA-32 提供了 PAE 模式(36-bit 物理地址)
  - 可以访问最多 64GB 内存
  - 但每个进程仍然只能用 4GB 虚拟地址空间
  - 这就是为什么 32 位 Windows 服务器版能识别 >4GB 内存

64 位系统:
  - 虚拟地址 48-bit(用户空间)或 57-bit(Kernel)
  - 物理地址 48-bit~52-bit
  - 可以寻址 256TB~4PB 内存

总结

  • e820:BIOS 给操作系统的内存映射表,通过 INT 15h E820h 调用获取
  • 结构:Base Address(64-bit)+ Length(64-bit)+ Type(32-bit)
  • Type 类型:1=可用内存,2=保留/MMIO,3=ACPI Reclaim,4=ACPI NVS
  • MMIO:内存映射 I/O,外设寄存器占用的地址空间,不能当 DRAM 用
  • 为什么 32 位只能看到 4GB:地址空间被 MMIO 分走了一部分,DRAM 可用空间缩水

下篇预告(B02):内存条的”身份证”——SPD 芯片里到底存了什么?SPD 数据格式详解,以及 BIOS 是怎么用 SPD 来配置内存的。


关注公众号「AI不着急」,回复”资料”获取内存学习路线图。

最后修改: 2024年5月23日

作者

评论

发表评论

您的邮箱地址不会被公开。