JOS学习笔记(七)

接前一篇。

上篇日志主要是完成了一些分页相关机制的工作,但还没有真正的去使用这个分页系统。Lab2的part3部分主要就是让我们使用part2中完成的映射机制来初始化内核的页目录和页表,并将此页目录加载到cr3里,让os真正去使用我们初始化之后的页目录以取代kernpgdir.c里面简单的页目录。

在开始之前让我们看一下JOS的虚拟内存分布图,在part3里的所有工作就是照着此图实现其中的部分映射。

JOS学习笔记(七)_第1张图片

 首先从高位地址说起,kernbase到最高为4g是一块remapped内存,这块很大的内存从低到高要映射整块物理内存。

其次kernbase往下PTSIZE(貌似是4M)是无用内存,再往下是KERNSTACKTOP也就是内核栈的栈顶,众所周知栈的生长顺序是从高地址向低地址生长,所以这个位置也就是栈开始生长的位置。从KERNSTACKTOP往下KSTKSIZE为内核栈区域,大概是8个页面,内核栈不能超过这个区域。从KERNSTACKTOP往下一个PTSIZE这块区域除了内核栈之外还有一块空白区域,防止栈溢出的时候复写用户空间的数据。

接着是ULIM,代表用户空间的最高地址,换句话说此位置往下所有空间为用户空间,用户空间有很多东西,之后的LAB会详细说明,在此只说UPAGES。

UPAGES是JOS用户记录物理页面使用情况的数据结构,为了使用户空间能访问这块数据结构,会将PAGES映射到UPAGES位置。

好总结一下,是要总共要求映射3块区域,,1是映射UPAGES结构,,2是映射内核栈,3映射整块物理内存。


 

1、UPAGES的映射

所谓映射就是将某个虚拟内存地址(符号地址)映射到实际的物理地址,UPAGES映射到pages也就是通过UPAGES访问到的地址恰恰是pages这个符号指向的物理地址的内容。而映射的实质就是往页表和页目录里写入相应的值,使UPAGES这个符号经过地址变换之后指向的地址恰好是pages的物理地址。

实现的方式很简单,先获取pages数组的大小,并向上对齐到PGSIZE得到要映射的页面,然后一个页面一个页面分别映射即可。

容易出错的地方:(1)不能使用sizeof获取pages数组的大小,因为此数组是动态分配的,使用sizeof只会获取到pages这个指针本身的大小(4B)。(2)一个页面地址映射一次即可,映射的最小单位是页面,一个页面内的地址不需要反复映射。

JOS学习笔记(七)_第2张图片


2、内核栈的映射。

目前内核栈的符号地址是bootstack表示栈底(栈的低地址,不是逻辑上的栈底反而是逻辑上的栈顶,因为向下生长),需要将KSTACKTOP-KSTKSIZE映射到这个位置,并向上按顺序映射KSTKSIZE大小的内存区域。

其余部分不用管,虽然函数说明中写了很多。

JOS学习笔记(七)_第3张图片



3、映射整块内存区域。

要映射KERNBASE到0xffffffff总共256M内存的空间到从0开始的物理地址。虽然我们没有足够的物理空间,但we just set up the mapping anyway。什么叫just set up the mapping anyway啊,没有就是没有,不够就是不够,你让我映射到哪里去?!抱怨归抱怨,但我都给映射到了物理的0地址上去了,也算是没有办法的办法。

但这么映射整块物理内存会出问题。在page_insert操作中会将该Page结构的pp_ref++,但映射整块物理内存的过程实际是便利了整个pages数组,这就会导致所有的Page结构的pp_ref++。但别忘了还有个page_free_list串起来了空闲的Page,这些空闲的Page理所应当的pp_ref也被增加了,最后的结果是page_free_list里面页面的pp_ref不为0。在空闲链表中的页面的引用次数不为0,这很明显是不符合逻辑的,因此要进行--操作。

当然还有一种解释是:不这么做通不过check,至于愿意相信哪种随你好了。

通过这个问题我觉得这算是JOS设计不合理的一个地方,映射和页面被引用是否是一个概念?谁负责维护pp_ref的信息,是这个页面的申请者还是page_insert函数还是page_insert的调用者?感觉这些地方还有待商榷。但之前1和2的映射不用考虑这个问题是因为牵扯到的页面不在free_page_list里,这在free_page_list初始化的时候就把里面的页面剔除了。

JOS学习笔记(七)_第4张图片


最后,可以看到通过check。

JOS学习笔记(七)_第5张图片


lab2到此结束,lab2和lab1的代码我上传到自己的资源里欢迎下载。

你可能感兴趣的:(JOS学习笔记(七))