Skip to content

代码:物理内存分配器

分配器位于 kalloc.c 中。分配器的数据结构是一个可供分配的物理内存页面的空闲列表。每个空闲页的列表元素是一个 struct run。分配器从哪里获得内存来存放该数据结构?它将每个空闲页的 run 结构存储在空闲页本身中,因为那里没有存储其他东西。空闲列表由一个自旋锁保护。列表和锁被包装在一个结构中,以明确锁保护结构中的字段。现在,忽略锁和对 acquirerelease 的调用;第 6 章将详细研究锁定。

函数 main 调用 kinit 来初始化分配器。kinit 初始化空闲列表以容纳内核末尾和 PHYSTOP 之间的每一页。Xv6 应该通过解析硬件提供的配置信息来确定有多少物理内存可用。相反,xv6 假设机器有 128 兆字节的 RAM。kinit 调用 freerange,通过对每页调用 kfree 将内存添加到空闲列表中。一个 PTE 只能引用一个在 4096 字节边界上对齐的物理地址(是 4096 的倍数),所以 freerange 使用 PGROUNDUP 来确保它只释放对齐的物理地址。分配器开始时没有内存;这些对 kfree 的调用给了它一些来管理。

分配器有时将地址视为整数以对其执行算术运算(例如,在 freerange 中遍历所有页面),有时使用地址作为指针来读写内存(例如,操作存储在每个页面中的 run 结构);这种地址的双重用途是分配器代码充满 C 类型转换的主要原因。

函数 kfree 首先将被释放的内存中的每个字节设置为值 1。这将导致在释放后使用内存的代码(使用“悬空引用”)读取垃圾而不是旧的有效内容;希望这会使此类代码更快地崩溃。然后 kfree 将页面前置到空闲列表中:它将 pa 转换为指向 struct run 的指针,将旧的空闲列表的开头记录在 r->next 中,并将空闲列表设置为 rkalloc 删除并返回空闲列表中的第一个元素。