NEMU PA3 菜鸡记录

闲话

NEMU是南大的一个课设,你天拿来当小学期任务,端地一番折磨

本篇博客由PA3实验报告改编而来,后续可能会有必做题的过程也可能没有

看阿瓜的懒惰程度了

实验进度表

任务序号 任务内容 完成情况
必做任务1 实现一级Cache 已完成
必做任务2 在NEMU中实现分段机制 已完成
必做任务3 在NEMU中实现分页机制 已完成
必做任务4 实现TLB 已完成
选做任务1 实现二级Cache 已完成
选做任务2 建议调试器 未完成
选做任务3 为用户进程创建video memory 映射 未完成

思考题

思考题1:GDT能有多大

  • 段选择符的结构中,INDEX有13位,故GDT最大能容纳2^13个段描述符

思考题2:为什么是线性地址

  • 不可以。虚拟地址需要经GDT中的段表翻译才能得出地址,而如果GDTR中存放虚拟地址则找不到GDT在哪里了。

思考题3:如何提高寻找段描述符的效率

  • 可以按照高速缓存的思想,建立类似cache 和 TLB 的结构来提高寻找效率。

思考题4:段式存储管理的缺点

  • 分段管理要求分配一大段连续的存储空间,难以实现并且容易造成大量的外部碎片出现。

思考题5:页式存储管理的优点

  • 没有外部碎片,并且不再需要大段连续的存储空间,提高了内存的利用率。

思考题6:一些问题

    • Q:为什么页表表项中的基地址信息只有20位而不是32位
    • A:分页基地址有20位是8086的传统
      在8086的分段机制中,每个段的基地址由seg_reg(即段寄存器的值)<<4得到,而段寄存器是16位的,左移4位得到20位的基地址。
    • Q:表项和CR3中的基地址都是物理地址,这是必须的吗?能否采用虚拟地址或者线性地址?
    • A:是必须的,如果cr3中的基地址是虚拟地址,则无从寻找页表翻译成物理地址,进入鸡生蛋蛋生鸡的死循环。至于其他表项的虚拟地址与线性地址问题同理。
    • Q:为什么不采用一级页表?采用一级页表会有什么缺点?
    • A:多级页表可以有效地节约内存空间,如果仅采用一级页表,将可能导致较大的页表长期驻留在内存中。

思考题7:空指针是“空”的吗

  • 空指针只是未分配或者未指向内存任何位置的指针,并不是“NULL”的。

思考题8:在扁平模式下如何进行保护

  • 对于数据有不同的访问权限,未达到需要的权限时不能进行写操作。

思考题9:地址映射

思考题9

思考题10:

  • pframe_addr是无符号类型,它的值永远大于等于0,所以for循环无法退出,出现错误。

思考题11:分页机制

    • 因为这里定义的x生成的地址是虚拟地址,超过了物理地址的界限,报错0xc014a000 outside of the physical memory。
      而kvm.c 中的虚拟地址都经过了 va_to_pa 的转换,在物理地址范围之内。
    • 进行反汇编后,其地址如下:
      c01003d6: e8 65 09 00 00 call c0100d40
      <init_page>
      该call指令的opcode为e8,实现的是跳转到:该条指令的下一条指令的首地址+偏移量的位置。由于未进行寻址,故不需要进行虚实地址转化。
    • 分页的环境下,在没有初始化页表时,0~128M的虚拟地址到物理地址的映射相当于一个简易的页表,使得高位的地址可以通过该虚拟地址(即经过va_to_pa)访问到物理地址,从而进行初始化页表的操作。
    • init_mm()函数执行退出时。该函数将nemu映射到了高位地址并且将之前的PDE全部置为无效,此时返回main.c时,栈中保存的返回地址需要经过虚实转换,可由于页面被置为了无效,所以报错。
    • 查看汇编代码,直接调用此函数时,nemu运行在物理地址上,由于在init_mm中将之前的PDE都置为无效,所以在loader()函数寻址时页面无效,导致报错。

实验遇到的问题、思考、解决办法

  • 对GDT一直看不很明白,以为数组的元素大小也需要跟处理器位数一样,最大是32位,实际上GDT的元素就是64位的段描述符,不过是分成了两个32位的结构体
  • 由于初始时对分段很不理解,然后对指导书内容进行了相应的整理,如下:
    • 由于内存的增长,单纯段寄存器无法满足寻址需要,需要采用分段机制。

    • 80386作为32位处理器,故提供的指针都是32位的,而段寄存器只有16位,而段描述符有64位,用指针存储也无法做到。故采用GDT(Global Descriptor Table, 全局描述符表)来存储段描述符。

    • GDT是一个数组,数组元素是64位的段描述符(实际上是一个有两个32位member的结构体)而数组下标则由段寄存器的前13位得到(最后两位用来描述优先级,中间1位TI用来判断是GDT还是LDT)(GDT是全局描述符表,而LDT是每个进程的描述符表,由于分页机制的出现,LDT并不需要在NEMU中实现)

    • CPU还需要记录GDT的基地址,这通过设置一个GDTR的寄存器来实现,该寄存器有48位,前32位类似于指针存放GDT的首地址,后16位记录GDT的长度

实验心得

  • 指针最大位数受CPU位数影响,所以才需要GDT进行段描述符的存储和读取

NEMU PA3 菜鸡记录
http://zqizhang.github.io/2021/09/28/NEMU_PA3/
作者
Wang Xun
发布于
2021年9月28日
许可协议