OS-lab2实验报告

lab2实验报告

一、实验思考题

Thinking 2.1
请思考cache用虚拟地址来查询的可能性,并且给出这种方式对访存带来的好处和坏处。另外,你能否能根据前一个问题的解答来得出用物理地址来查询的优势?

虚拟地址:

  • cache用虚拟地址来查询是有可能的,并且确实存在这样的cache组织方式(VIVTVIPT

  • 好处:不需要使用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 <asm/regdef.h>
#include <asm/cp0regdef.h>
#include <asm/asm.h>

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整体的架构进行简单的讲解,这个讲解是自上而下的,让学生在做完各个部分之后,通过阅读这一部分,两种实现方向结合起来理解,能够对实验的架构有比较清楚的认知。

posted @ 2021-04-24 13:07  神樂坂清清  阅读(428)  评论(0编辑  收藏  举报