在xv6中,与物理内存分配相关的代码都在proc.c/kalloc.c文件中,主要实现了三个功能:物理内存分配器初始化;分配物理内存空间;释放物理内存空间。
前置知识点
为什么操作系统内核能使用物理内存
我们知道系统内核和用户进程都是使用虚拟内存的,为什么操作系统内核能操作物理内存呢?
首先,我们来回顾下虚拟内存系统是如何工作的:
在设置好stap寄存器后,MMU(memory manage unit)就会开始使用stap寄存器指向的页表来对CPU传送过来的指令地址进行转换,再到内存中寻址。
而系统内核使用的映射,大多是恒等映射(如下图所示),因此我们在系统内核中使用的虚拟内存地址其实与物理地址相同的。
空闲物理内存在内核中的表示
先说三个细节:
- 在RISC-V中,一个地址使用64 bits表示,也即8 bytes,所以struct run的大小就是8 bytes;
- 分配与释放物理内存空间的单位是一个页面,在RISC-V中,一个页面的大小是4 kb,也即4096 bytes;
为了方便,我假设RAM大小为3*4kb = 12Kb,即有3个物理页。在初始时,三个页面都是空闲页。pa0,pa1,pa2分别是三个物理页的首地址。
我们需要解决两个问题,一是如何表示一个空闲物理页,二是如何表示所有的空闲物理页。
对于第一个问题,由于每个物理页的大小是相同的,因此我们只需要使用每个物理页的首地址,就可以表示出该物理页(首地址往后4 kb是同一页)。
而对于第二个问题,可以使用链表来将所有空闲的物理页串起来。
一般的链表可以使用如下代码实现:
struct ListNode {
int val;
struct ListNode* next;
};
在xv6中,所有的空闲物理内存记录在头节点为kmem.freelist的链表中,链表的存储的数据类型是struct run,struct run中只有一个成员struct run的指针。
struct run {
struct run *next;
};
struct {
struct spinlock lock;
struct run *freelist;
} kmem;
具体来说,我们可以在每个空闲物理页的前八个字节存放一个地址,这个地址指向了下一个空闲页的首地址。而kmem.freelist就指向这个链表的头结点。
物理内存分配器初始化与物理内存释放
在物理内存分配器初始化是需要对物理内进行释放,因此这里对两者一并介绍。
首先来看看xv6是如何释放一个物理页的。
void kfree(void *pa) {
struct run *r;
// 检查物理地址pa 是否是页大小的整数倍 与 是否在RAM的地址范围内
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
// 将pa表示的一个物理页填充满垃圾数据,以方便捕捉垂悬引用
memset(pa, 1, PGSIZE);
// 将r指向一个物理页的起始地址pa
r = (struct run*)pa;
acquire(&kmem.lock);
// 在空闲物理页的开头放入下一个空闲物理页的首地址
r->next = kmem.freelist;
// 将空闲物理页链表的头结点设置为r
kmem.freelist = r;
release(&kmem.lock);
}
而初始化其实就是遍历RAM(KERNBASE ~ PHYBASE)中的每个页,并一一使用kfree()进行释放。初始化后的freelist应该包括了RAM中的所有物理页。
void kinit() {
initlock(&kmem.lock, "kmem");
freerange(end, (void*)PHYSTOP);
}
void freerange(void *pa_start, void *pa_end) {
char *p;
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
kfree(p);
}
物理内存分配
而物理内存的分配其实也十分简单,就是取当前头结点,将头结点从freelist中删除,并返回该空闲物理页的首地址。
void *kalloc(void) {
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}