1.E820表的来源
E820表是Linux内核在x86架构上启动时,用于描述物理内存布局的关键数据结构。它详细记录了系统中哪些物理地址范围是可供操作系统使用的“可用内存”(RAM),哪些又被BIOS或其他系统组件“保留”而不可使用。 E820表的构建始于内核启动的早期阶段,具体过程如下: BIOS查询:计算机启动后,内核(通常由引导加载程序如GRUB加载)会通过BIOS中断调用int 0x15,并在AX寄存器中传入功能号0xE820,来向BIOS请求系统的物理内存布局信息。之所以叫“E820表”,正是源于这个功能号。 数据填充:BIOS处理该中断后,会将内存布局信息返回并填充到一个预定的结构体数组中,即boot_params.e820_table。每条记录都包含起始地址、大小和类型(如可用RAM、保留区域等)。 内核整理与转换:获取到原始数据后,内核在start_kernel()-> setup_arch()函数中,会调用e820__memory_setup_default()函数。这个函数负责将BIOS提供的原始E820表转换并合并到内核自己使用的e820_table中,并进行一些整理工作,比如合并相邻的相同类型的区域。
E820表中记录的所有地址都是物理地址。这是因为E820表是在内核初始化过程中、分页机制尚未完全建立之前就被构建出来的。它的核心使命是告诉操作系统“物理内存的真实地图”,比如从0到640KB的基本内存,以及1MB以上的扩展内存是可用的,而中间一些区域(如用于BIOS或设备内存映射的区域)则是保留的。内核的内存管理子系统(如memblock分配器)正是基于这张“物理地图”来开始工作的。
2.怎么理解保留内存
要理解“保留内存”,关键在于理解x86系统的内存映射I/O机制。X86的架构,内存与外设是统一编址的,CPU用于访问内存的地址总线,同时也被用来访问外部设备的寄存器。
注意 ARM的内存与IO是独立编址的,但是ARM有由于其它原因保留内存(例如DMA缓存、给协处理器预留共享内存区等等),所以ARM没有E820表,注意这里的区别。
当CPU访问一个被E820表标记为“保留”的物理地址时,它实际上是在访问外设内部自带的存储单元,而非内存条上的DRAM。这背后的核心技术是内存映射I/O。 计算机启动时,BIOS的任务是探测并初始化所有关键硬件。它清楚地知道系统中有哪些设备,以及这些设备需要占用哪部分物理地址空间来正常工作(比如显卡需要一块空间来作为显存的映射窗口)。BIOS将这些不能被操作系统当作普通内存分配使用的地址区域标记为“Reserved”(保留),并汇总到E820表中。所以,E820表中的保留内存,本质上就是BIOS告诉内核:“这片地址区域已经被‘征用’了,有专门的硬件设备驻扎在这里,你不能把它们分配给普通程序使用。
3.与保留内存地址重合的内存条上的存储空间怎么办
访问这些保留地址空间,是CPU通过“内存映射I/O”机制直接访问外设内部的寄存器或自带存储器,而非访问内存条上的物理内存。外设的这些存储单元被预先映射到了系统统一的物理地址空间中。 而内存条本身的物理地址是从0开始编址的,例如X86上物理地址640KB到1MB会被BIOS征用,那么内存条上对应物理地址640KB到1MB(即0xA0000至0xFFFFF)的这段存储空间,因为与外设地址范围重合,故其存储能力无法被系统作为普通内存来访问和使用,实际上就是被浪费掉了。
4.保留内存的一个景点例子
一个经典的例子:0xA0000-0xFFFFF区域 这个640KB到1MB之间的区域是理解保留内存的绝佳案例: 历史背景:在早期PC(如基于8086CPU)中,1MB以下的物理内存被分为两段:0到640KB为基本内存,供操作系统和应用程序使用;640KB到1MB这384KB的地址空间,则被预留给硬件。 具体用途:这部分地址空间被映射给了显卡的显存(如经典的VGA模式)、系统的BIOS ROM等。当CPU向这个范围内的地址写入数据时,实际上是在直接操作显卡硬件;当CPU从这个范围读取指令时,可能是在执行BIOS固件代码。 影响延续:尽管现在的计算机物理内存远大于1MB,但为了兼容历史上的硬件和规范,这段空间依然被保留下来,形成了著名的“640KB内存空洞”。
5.现代计算机的演变
动态资源分配:对于PCI/PCIe等现代总线设备,其所需的内存映射空间(称为MMIO空间)是由操作系统在启动时动态分配的,通常会将其映射到物理地址空间的高端(例如3.5GB以上),从而避免与主内存区域的冲突。