OS-lab2实验报告
- 格式:pdf
- 大小:189.39 KB
- 文档页数:3
OS-lab2实验报告
lab2实验报告
⼀、实验思考题
Thinking 2.1
请思考cache⽤虚拟地址来查询的可能性,并且给出这种⽅式对访存带来的好处和坏处。另外,你能否能根据前⼀个问题的解答来得出⽤物理地址来查询的优势?
虚拟地址:
cache⽤虚拟地址来查询是有可能的,并且确实存在这样的cache组织⽅式(VIVT、VIPT)
好处:不需要使⽤mmu转换成物理地址就可以直接根据虚拟地址查找cache中的数据
坏处:不同的进程可能会有相同的虚拟地址,安全性和数据正确性很难保证,需要引⼊物理地址或者其他的标识符加以区分
物理地址:
优势:优势即在于没有根据虚拟地址查询时需要区分不同的进程的问题,查询的安全性和数据正确性更有保证
Thinking 2.2
在我们的实验中,有许多对虚拟地址或者物理地址操作的宏函数(详见include/mmu.h ),那么我们在调⽤这些宏的时候需要弄清楚需要操作的地址是物理地址还是虚拟地址,阅读下⾯
的代码,指出x是⼀个物理地址还是虚拟地址。
int x;
char *value = return_a_pointer();
*value = 10;
x = (int) value;
x是⼀个虚拟地址
Thinking 2.3
我们在 include/queue.h 中定义了⼀系列的宏函数来简化对链表的操作。实际上,我们在 include/queue.h ⽂件中定义的链表和 glibc 相关源码较为相似,这⼀链表设计也应⽤于
Linux 系统中 (sys/queue.h ⽂件)。请阅读这些宏函数的代码,说说它们的原理和巧妙之处。
原理:链表头是⼀个结构体,存储了头指针,对于链表的每⼀个节点,内部存储的结构体存储了指向下⼀个节点的地址和前⼀项中指向下⼀个节点的指针的地址。
巧妙之处:每⼀个节点存储了前⼀项中指向下⼀个节点的指针的地址,插⼊和删除节点的复杂度很低
Thinking 2.4
我们注意到我们把宏函数的函数体写成了 do { /* ... */ } while(0)的形式,⽽不是仅仅写成形如 { /* ... */ } 的语句块,这样的写法好处是什么?
空的宏定义避免warning
存在⼀个独⽴的block,可以⽤来进⾏变量定义,替换到程序当中不会产⽣歧义或者错误,能够进⾏⽐较复杂的实现
Thinking 2.5
注意,我们定义的 Page 结构体只是⼀个信息的载体,它只代表了相应物理内存页的信息,它本⾝并不是物理内存页。 那我们的物理内存页究竟在哪呢?Page 结构体⼜是通过怎样
的⽅式找到它代表的物理内存页的地址呢? 请你阅读 include/pmap.h 与 mm/pmap.c 中相关代码,并思考⼀下。
物理内存页储存在真实的物理内存中
⽤page2pa()函数,根据pp指针相对于pages的偏移量计算
static inline u_long
page2ppn(struct Page *pp)
{
return pp - pages;
}
/* Get the physical address of Page 'pp'.*/
static inline u_long
page2pa(struct Page *pp)
{
return page2ppn(pp) << PGSHIFT;
}
Thinking 2.6
请阅读 include/queue.h 以及 include/pmap.h, 将Page_list的结构梳理清楚,选择正确的展开结构(请注意指针)。
正确的展开结构为C
struct Page_list{
struct {
struct {
struct Page *le_next;
struct Page **le_prev;
} pp_link;
u_short pp_ref;
}* lh_first;
}
Thinking 2.7
在 mmu.h 中定义了 bzero(void *b, size_t) 这样⼀个函数,请你思考,此处的b指针是⼀个物理地址, 还是⼀个虚拟地址呢?
b指针应该为虚拟地址Thinking 2.8
了解了⼆级页表页⽬录⾃映射的原理之后,我们知道,Win2k内核的虚存管理也是采⽤了⼆级页表的形式,其页表所占的 4M 空间对应的虚存起始地址为 0xC0000000,那么,它
的页⽬录的起始地址是多少呢?
0xC0000000 >> 10 + 0xC0000000 = 0xC0300000
Thinking 2.9
注意到页表在进程地址空间中连续存放,并线性映射到整个地址空间,思考:是否可以由虚拟地址直接得到对应页表项的虚拟地址?上⼀节末尾所述转换过程中,第⼀步查页⽬录有必
要吗,为什么?
可以,不使⽤⼆级页表使⽤⼀级页表即可
有必要,⼆级页表的使⽤可以节省很多的内存开销
Thinking 2.10
观察给出的代码可以发现,page_insert 会默认为页⾯设置PTE_V的权限。请问,你认为是否应该将PTE_R 也作为默认权限?并说明理由。
我认为不应该将PTE_R设置为默认权限,PTE_R是为了页⾯置换存在的。如果没有PTE_R这⼀位标识位,在进⾏页⾯置换的时候,每次置换都需要把当前页先写回到磁盘,这个写回
的代价是⽐较⼤的。如果有这⼀位标识位,PTE_R为0说明该页在内存中只涉及了读操作⽽没有进⾏写操作,当该页进⾏了写操作(被修改)才置1。这样⼀来,置换页⾯时先
对PTE_R位进⾏判断,如果为0就不需要进⾏写回操作了,显著的减⼩了写回的代价。如果将PTE_R设置为默认权限,则PTE_R就失去了其存在的意义,⽆法减⼩写回的代价。
#define PTE_R 0x0400 // Dirty bit ,'0' means only read ,otherwise make interrupt
Thinking 2.11
思考⼀下tlb_out 汇编函数,结合代码阐述⼀下跳转到NOFOUND的流程?从MIPS⼿册中查找tlbp和tlbwi指令,明确其⽤途,并解释为何第10⾏处指令后有4条nop指令。
#include
#include
#include
LEAF(tlb_out)
//1: j 1b
nop
mfc0 k1,CP0_ENTRYHI
//将CP0_ENTRYHI的值存储在k1寄存器中
mtc0 a0,CP0_ENTRYHI
//将a0寄存器的值存储在CP0_ENTRYHI中,即虚拟地址
nop
// insert tlbp or tlbwi
tlbp
//根据CP0_ENTRYHI中的虚拟地址查找TLB
//如果没有TLB条⽬匹配,设置Index寄存器的⾼位为1
//如果有TLB条⽬匹配,把匹配项的index保存在Index寄存器中
nop
nop
nop
nop
//四条nop指令是流⽔线暂停,等待tlbp指令执⾏完毕
mfc0 k0,CP0_INDEX
//将tlbp改写过后的CP0_INDEX的值存储在k0寄存器中
bltz k0,NOFOUND
//如果k0 < 0(即Index寄存器的⾼位置1,即TLB没有查找到),跳转到NOFOUND
//如果k0 > 0(即TLB查找到了),不跳转。继续执⾏
nop
mtc0 zero,CP0_ENTRYHI //$10
mtc0 zero,CP0_ENTRYLO0 //$2
nop
// insert tlbp or tlbwi
tlbwi
//更新TLB
NOFOUND:
mtc0 k1,CP0_ENTRYHI //$27 $10
j ra
nop
END(tlb_out)
Thinking 2.12
显然,运⾏后结果与我们预期的不符,va值为0x88888,相应的pa中的值为0。这说明我们的代码中存在问题,请你仔细思考我们的访存模型,指出问题所在。
va2pa函数返回的是va所在物理页的物理⾸地址,但是并没有考虑页内偏移
Thinking 2.13
在X86体系结构下的操作系统,有⼀个特殊的寄存器CR4,在其中有⼀个PSE位,当该位设为1时将开启4MB⼤物理页⾯模式,请查阅相关资料,说明当PSE开启时的页表组织形式
与我们当前的页表组织形式的区别。
对X86体系结构下的操作系统,PSE开启时,页⽬录项引⼊了⼀个新的标识,如果标识为1,则这个页⽬录表项指向⼀个4MB的⼤物理页⾯,标识为0,则像原来那样指向⼀个页表。
⽽我们当前的页表组织形式,页⽬录表项指向⼀个页表,每⼀个页表映射到的地址⼤⼩同样是4MB,即每⼀个页⽬录表项都指向⼀个4MB的⼤物理页⾯。
⼆、实验难点图⽰
第⼀个难点是对于这个⼆重指针的理解。迟迟想不通为什么要这么设计,加上⾃⼰C语⾔的基础本来就不够好,对于指针的使⽤⼀直不是很熟悉,本来就够迷惑了,还⽤了⼆重指
针就更加迷惑了在⽹上查了很多的资料,但是没有很清晰的讲解,最后找到了这样⼀张图,参考这张图来理解才突然恍然⼤悟,知道这个⼆重指针到底是怎么回事
第⼆个难点是LIST_INSERT_TAIL函数的补全,主要是⼀直没想明⽩遍历的时候循环变量到底怎么写,因为函数中没有变量类型,所以⽆法定义⼀个新的变量作为循环变量,最后
通过和同学的交流探讨发现可以使⽤elm -> filed.le_next暂时作为循环变量进⾏遍历,处理完毕后将elm -> filed.le_next置为NULL即可
第三个难点是Exercise2.5-2.7的三个函数的补全,主要难点在于对于虚实地址的混淆,经常会没有转换成正确的虚/实地址⽽导致错误,debug很久
第四个难点是对于tlb_asm.S中tlb_out函数的补写,实验中的MIPS体系结构似乎和计组的体系结构并不相同,有很多陌⽣的指令,英⽂指令集读起来还蛮困难的,不过⼀条⼀条指
令解读,最后还是勉强理解了
三、体会与感想
感觉这次真的好难啊qaq,总共花费的时间加起来保守估计应该有25个⼩时了,很多地⽅想不通就⼀直钻⽜⾓尖,然后就越想越想不通,很痛苦,过程中多亏了和⼤佬们的交流探
讨,解决了我很多的困惑。
体会:
遇到不理解的地⽅多和同学交流沟通,不应该只是⼀直空想,越想越迷惑
基础理论知识⼀定要牢靠,知道实验的原理,知道⾃⼰要做什么,基础理论知识不清晰带来的后果是很严重的(课上测试这⼀点尤为显著)
⼤胆动⼿写,不要担⼼⾃⼰会写错,⼀步⼀步写,验证⾃⼰的想法的过程是很棒的,只不过出锅了就很痛苦了。。。
四、指导书反馈
没有什么问题和错误,但是有⼀个想法
指导书给出的实现思路是⾃下⽽上的,先实现低端的函数功能,再向上延申,但是这个过程其实很难让我们对于实验的整体结构有⼀个⽐较清楚的了解,需要⾃⼰去读代码(主要在于
读mips_vm_init()函数整体的流程),⽽如果单纯的想实现函数功能,通过课下测试,没有去读其他部分代码(虽然这种想法与课程组的初衷相违背,但是我认为存在这样的现象),
这样的话对于实验整体的认知是很模糊的。是否可以在指导书中添加⼀个部分对lab2整体的架构进⾏简单的讲解,这个讲解是⾃上⽽下的,让学⽣在做完各个部分之后,通过阅读这
⼀部分,两种实现⽅向结合起来理解,能够对实验的架构有⽐较清楚的认知。