写时复制(COW )机制是操作系统中一种常见的惰性机制,在很多场景下可以提供较好 的性能和比物理资源多很多的虚拟资源量,因而机制在内存管理中使用较多。实现 COW 机制 需要我们将前几个实验的涉及的概念(进程、分页和中断)融会贯通,从这个实验开始,我们 的工作将遍及 xv6 内核的各个部分。
开始之前切换到 cow 分支
我们早在第三章 Lab Utilities 中就已经接触了 xv6 中用于生成子进程的系统调用 fork ,该系统调用由于其实用的设计被后世的类 Unix 系统一直沿用,可谓 Unix v6 留下的宝贵遗产。在原始的 xv6 的实现中,fork 会将父进程的所有页面完整地复制到一份,映射到子进程中。然而很多时候,子进程只会读取这些页面的内容,而不会写入这些页面。为了节约内存的实际用量,我们可以在子进程(或父进程)真正需要写入页面时才分配新的页面并进行数据的复制,这种机制被称为 COW fork 。
下面是一些建议:
一些注意的点:
首先需要处理引用计数的问题。在 kernel/kalloc.c 中定义一个全局变量和锁。
// the reference count of physical memory page
int useReference[PHYSTOP/PGSIZE];
struct spinlock ref_count_lock;
然后在函数 kalloc() 中,初始化新分配的物理页的引用计数为 1.
void *
kalloc(void)
{
...
if(r) {
kmem.freelist = r->next;
acquire(&ref_count_lock);
// initialization the ref count to 1
useReference[(uint64)r / PGSIZE] = 1;
release(&ref_count_lock);
}
release(&kmem.lock);
...
}
在该代码段中,if (r) 是一个条件语句,用于判断 kmem.freelist 是否为空。kmem.freelist 是一个指向空闲物理内存页面链表的指针。如果 kmem.freelist 不为空,则说明有空闲的物理内存页面可以被分配给进程使用。