3.LAB-5 COW
LAB-5 COW
主要是修改fork
和缺页中断处理程序
以前fork时,会直接复制原进程的页表内容到新页表,具体过程是
对于原页表中的每一页,malloc一个新页,然后复制数据到新页,最后把这一页映射到新页表
现在进行修改:
1. fork复制页表
- 对于新进程,为其创建新页表
- 直接复用旧页表项,不复制旧物理页
- 如果旧页表项没有写权限,那就无需修改权限,直接复制页表项
- 如果有写权限,消除掉其写权限,并为其添加COW Flag
[!IMPORTANT]
如果只有新进程没有写权限,而旧进程有权限,旧进程有可能修改页内容,此时新进程将会出错,因此双方都没有写权限
2. 中断处理程序
当需要修改时,会发生缺页中断,此时再进行数据迁移
-
得到发生缺页的物理地址
查看此页的引用数,如果是1,直接修改页表项,使其有写权限
-
malloc新页
-
把旧页的数据复制到新页中。
-
修改页表,使其映射到新页
-
修改新页表项,使其有写权限
3. 引用计数
XV6中,页大小为4KB,页分配的最小单位也是4KB
- 在kinit时,初始化每一页的引用数为0
- kalloc时,引用数加1
- kfree时,引用数减1,当引用数为0时,才真的释放此页
4. 页表项Flag说明
相关文件:kernel/riscv.h
PTE_V
:Valid,页表项是否有效,如果未设置,则对该页的使用会引起异常(即不允许访问)。PTE_R
:Read,是否允许指令读取该页。PTE_W
:Write,是否允许写入页。PTE_X
:Execute,CPU是否可以将页的内容解释为指令并执行它们。PTE_U
:User,是否允许用户模式下的指令访问该页,如果未设置PTE_U
,则只能在监督者模式下使用。
一些坑
1. 计算页号
XV6的用户内存大小是128M,一共2^15页,但是其起始地址并不是0,而是
// riscv.h
#define KERNBASE 0x80000000L
因此计算页号时,不要直接使用pa<<12
获取页号,而要(pa-KERNBASE)<<12
,
2.uvmcopy复制页表项
复制页表项时,只有当旧表项有写权限时,才需要将其记录为cow页,
如果是只读页,无需进行这种处理,会出错。
比如0地址,存储的是二进制代码,用户态只有读权限,如果将其记录为COW页,尝试向其写入数据时,中断处理会修改这一页,导致出错