lab3 主要是页表相关,难度突然提高了好多,遇到了无数个坑,太难了。
打印页表
第一个进程启动时打印页表内容
实现:kernel/vm.c
里添加,递归实现即可
每个进程一张内核页表
目前xv6的实现里,每个进程只有一张独立的用户地址空间页表,共享一张内核页表,这样的话每次内核不能直接使用用户指针,只能先将其通过用户页表转为物理地址。本节及下一节要做的是使内核可以直接解引用用户指针,就是可以直接通过虚拟地址访问。
本节要做的就是为每个进程复制一个内核页表。
实现
省略了 添加函数到defs.h
的步骤,这一节一些实现与下一节相关。
- 首先
kernel/proc.h
里为struct proc
添加内核页表
-
kernel/vm.c
添加新版本的kvminit
用于为每个进程生成内核页表,其中没有映射CLINT,因为用户地址空间为低于PLIC的部分,而CLINT也是低于PLIC的
之后修改kernel/proc.h
,在allocproc
里调用上面那个函数新建内核页表,同时将procinit
页表映射相关移到allocproc
里
- 在
scheduler
里添加页表切换,每次切换进程时,页表切换到该进程的内核页表
- 最后释放内存时释放内核页表,这里添加了一个
proc_freekpagetable
用于释放内核页表。先释放内核页表,再释放用户页表。
这里第221行是因为下一节里需要将用户页表拷贝到内核页表里,因此需要unmap用户地址空间,并不释放物理内存,物理内存在proc_freepagetable
里释放。
简化 copyin/copyinstr
这一节主要是将每个进程里的用户页表复制到内核页表,这样就可以简化copyin/copyinstr函数,因为可以直接对访问用户指针了。
- 替换copyin和copyinstr函数
- 将进程中用户页表复制到内核页表中
首先写一个通用函数,在kernel/vm.c
里fork
里需要调用exec
里也需要调用,注意调用前需要先释放旧的内核页表里映射的用户页表sys_sbrk
里内存增加时需要复制到内核页表,内存减少需要解除映射,这里把55行int改为uint64可能会陷入无限循环,出现panic: uvmunmap: walk
,某个测试点过不了.userinit
也需要调用,这是第一个进程初始化。不然会开局panic: uvmunmap: walk"
- 最后在
exec
里添加 内存大小PLIC限制
- make grade
总结
遇到的比较常见的问题是释放页表时各种 panic, 释放页表一般包括释放页表指向的物理内存和释放页表本身物理内存,uvmunmap
用于unmap虚拟内存到物理内存的映射,同时最后一个参数表示是否释放该物理内存,如果没有其它进程页表指向该物理内存才可以释放该物理内存;freewalk
用于释放页表本身的物理内存,该页表不能包含叶子,即所有叶子都已经调用了uvmunmap
。
lab介绍里的提示已经够详细了,还是出现了各种问题,看到panic就想吐了。。有些实在不清楚咋搞,参考了这位大佬的[1].
参考
[1] https://blog.csdn.net/u013577996/article/details/109582932