实验进度

任务 完成
必做任务1  完成
必做任务2  完成
必做任务3  完成
必做任务4  完成
选做任务1  完成
选做任务2  完成
选做任务3  完成

思考题

思考题1

段选择符index是$2^13$,最多可以$2^13$个段描述符

思考题2

不可以,因为虚拟地址就是需要通过GDT进行转换,如果GDT的首地址都是虚拟地址那么就没有东西可以转换GDT的虚拟地址了

思考题3

在段寄存器中保存基地址和限制

思考题4

段的体积大,在内存中无法做到连续存储,容易形成外碎片,降低内存利用率。

思考题5

分页式管理便于进行内存调度,并且没有外碎片,内碎片不超过页的大小

思考题6

因为低12位是页内偏移,可以直接根据虚拟地址给出,不需要进行转换

不能,GDT使用线性地址一样,CR3是用于物理地址转换的,如果他也是虚拟地址就没有什么可以转换CR3的线性地址了。

一级页表有占用内存过多的缺点。而二级页表虽然看上去消耗内存反而增大了,但是实际上很多对应ptable并没有实际创建因而减小了内存。

思考题7

空指针并不是空的,只是指向的地址是0,属于操作系统无法访问。

思考题8

在页表项中有权限位,进行地址转换时首先检查权限位

思考题9

思考题10

原本的最后ptable会到负数,ptable[-1]

思考题11

问题1

因为这里定义的x生成的地址是虚拟地址,超过了物理地址的界限,报错说0xc014a000 outside of the physical memory.

而kvm.c中的虚拟地址都经过了va_to_pa的转换,在物理地址范围之内

问题2

两个虚拟地址指向同一个物理地址是因为在0xc0100000上的在init_mm中作为今后访问内核的虚拟地址,另一份在updir没有正常使用时让虚拟地址得以正常翻译

问题3

出现了present = 0的错误。因为开启paging位之后,所有地址都需要进行虚拟地址转换,而esp还是使用开始的物理地址发现这个虚拟地址下并没有使用。

问题4

出现的问题和上面相同,使用init_cond()会先push 再jmp,之后又会通过ret进行返回。通过调试得到是在loader中出现问题的,而在lnaddr_read和lnaddr_write处监视发现对应栈的位置也只读写了一次。只能推测是因为将地址压入栈导致栈溢出

实验遇到的问题

 因为这个实验跨度比较长,结果出现了cache没有写完就去写后面的分段分页的情况。结果写分页的时候始终出现了问题,于是便一条条代码去查看。在kvm.c的内联汇编处发现了stos只有第一次会正确写入,之后变不会写到内存,但是如果使用swaddr_read()读取了一下内存(当时用x命令查看)就能正常写入。当时花了近一天的时间才想到可能是内存相关函数的原因,然后就在内存函数中逐条追踪返回值,才发现原来cache没有写完。

在写段结构的lgdt命令时发现将数直接转换成指针可能会出现一些奇奇怪怪的问题。然后就直接访问内存读取数据然后进行拼接。但是之后发现是可以(PDE)(void)之类的方式进行转换的。甚至可以使用绝对地址进行函数跳转 (void(*)(void)eip)()。是一个参数为void 返回值为void 地址为eip的函数。

实验心得

  • 对分段、分页、cache的实现有了更深的理解
  • 对地址和指针之间的转换更加熟练