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:地址映射
思考题10:
- pframe_addr是无符号类型,它的值永远大于等于0,所以for循环无法退出,出现错误。
思考题11:分页机制
-
- 因为这里定义的x生成的地址是虚拟地址,超过了物理地址的界限,报错0xc014a000 outside of the physical memory。
而kvm.c 中的虚拟地址都经过了 va_to_pa 的转换,在物理地址范围之内。
- 因为这里定义的x生成的地址是虚拟地址,超过了物理地址的界限,报错0xc014a000 outside of the physical memory。
-
- 进行反汇编后,其地址如下:
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/