图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

在 Linux 内核空间存在这样一个映射区,映射区的虚拟内存的功能在源码编写阶段已经确认,那么从该阶段到内核运行直到内核终止,虚拟内存的含义不变,Linux 将该区域称为永久映射区. 永久映射区的虚拟内存都用来做特殊功能使用,可以理解为该区域的虚拟内存被预留做特殊功能. Linux 使用 Permanent Mapping Memory Allocator(永久映射内存分配器) 负责该区域虚拟内存分配和页表映射工作, 其属于固定映射内存分配器的一个分支。从实现角度来看,永久映射内存分配器通过在编译阶段从内核的虚地址地址空间占用一段虚拟内存,再等到系统运行之后,通过将这个区域的某段虚拟地址映射到特定的物理内存或者外设寄存器上,只要不释放那么这段映射会永久有效.

“永久” 到底有多久? 为什么是 “永久”? 可以从三个维度来回答这个问题: 维度一内核从启动到系统真正运行会经历不同的阶段,每个阶段会临时建立一些临时映射用于完成该阶段的任务,当任务完成之后映射也会清除,例如启动阶段的恒等映射阶段。但对于永久映射分配器分配的虚拟内存,一旦页表建立,只要映射关系不解除,那么映射关系可以一直存在,并且其页表从编译阶段就存在.

维度二对比常见的内存分配器,内核或者应用程序需要分配虚拟内存,分配器会动态从虚拟空间分配一块可用的虚拟内存区域,然后通过直接或缺页的方式与物理内存或外设寄存器建立页表,最后才能使用这块虚拟内存。但永久映射分配器分配的虚拟内存就不同,其可以在源码编写阶段就可以知道虚拟内存值,且到系统运行这个虚拟地址值的含义都不会变. 因此永久映射内存分配器称为 Compile-Time 分配器.

维度三进程的地址空间包括了堆、栈、MMAP 区域等,这些区域都不是固定位置,即这些区域的长度和起始位置可能不相同,但使用永久映射分配器分配的虚拟内存都是保持一致的,且位于内核空间的末尾 0xffffffffff7ff000 向下的区域,例如 VSYSCALL 使用的虚拟地址对所有的进程都是一样的,因此永久还体现在所有进程看到的都是一致的.

永久内存分配器的实现原理如上图,分配器维护了一个索引,每个索引与特定的功能绑定在一起,因此可以从索引对应的宏知道这段虚拟地址的用途。分配器从系统的虚拟地址空间分配了 FIXADDR_START 到 FIXADDR_TOP 的区域,其长度为 FIXADDR_SIZE. 分配器从 FIXADD-R_TOP 向地址将虚拟地址分作 4KiB 大小的页,并将索引表依次反向对应 FIXADDR_TOP 向下的 4KiB 页,也就是索引 0 对应 FIXADDR_TOP 向下的第一个 4KiB 页,索引 1 对应 FIXADDR_TOP 向下的第二个 4KiB 页,依次类推。因此可以通过索引表就可以获得一个唯一的虚拟地址,其关系如下:

图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

图片无法显示,请右键点击新窗口打开图片