好 那我们首先再回顾一下历史
就是Lab1完成了什么事情
我们lab1其实一开始的时候
它有个很重要的功能
就是printf能够输出
能够输出字符串
那别小看printf
那么printf这个输出呢
能够有效地帮助我们来知道
我们当前执行在什么地方了
可以通过输出一些有意义的一些
字符或者数字来帮助你理解
你程序的一个执行行为
再接下来我们是完成了
保护模式和段机制的建立
那这个建立在我们80386
这个硬件基础之上的
那有了这个保护模式和段机制呢
为我们后续的页机制的建立
打下了一个基础
且有了段机制和保护模式之后
我们可以使用32位的地址空间
这是一个很重要的一个
就是在lab1要完成的事情
然后我们还完成了中断的机制
这一点很重要
因为中断我们前面在原理课讲到
中断是干什么呢
中断可以来响应外设的请求
第二个呢我们应用程序呢
也可以通过中断这种机制
来得到操作系统的服务
甚至说我们应用程序写错了
这所谓的异常 产生异常之后呢
我们操作系统也可以有效地去处理
那这里面呢 建立好中断机制之后呢
为我们这次实验这个缺页异常
缺页异常或者访问页异常
这种情况的处理打下很好的基础
那有了这三个的工作之后呢
那我们在完成lab3的时候
就可以做到有的放矢
那我们再看一下
这个中断机制怎么建立的
大家还回顾一下有没有印象
首先这里面有一个中断的一个
中断描述符表一个寄存器
它记录了中断描述符表的起始地址
那中断描述符表是干什么的呢
它其实是存了很多的中断门
一项一项的中断门
这个中断门指出了
当产生一个中断之后
它对应的中断服务例程
应该位于什么地方
就是干这个事情的
那么这相当于存了一个
相关的所有中断或者异常的
一个记录它地址的一个地方
这是IDT 那么我们在lab1里面
需要把这个IDT给建立好
还要完成一系列
中断初始化工作之后呢
我们中断整个机制就可以建好了
那建好中断处理机制之后
我们可以看到一旦产生了一个中断
那么中断都是有个中断号
那么这里面我说的中断
其实也包含了异常
中断 异常产生之后呢
会根据它的编号
硬件 我们CPU会找这个IDT
我们lab1我们ucore建好这个IDT
找到你对应的中断号
所对应的那个我们说叫中断
或者陷井这个gate 门 这个门
在这个门里面呢
很重要一点它有offset
就是它有一个偏移
这里面记的偏移
同时还记录一个信息
就是它那个selector
我们称之为选择子
这些概念其实在lab1里面都碰到过
这选择子实际上
跟我们段机制又结合起来了
这个选择子作为Index作为一个索引
来查找我们另外一个 另外一个表
我们称之为GDT
就是全局描述符表
或者简单说就是段表
有了段表之后呢
根据它（选择子）的Index可以查找
你到底它（选择子）属于哪一个段
然后从而可以知道这个段的
Base Address 就是基址
这个基址加上offset形成了最终的这个
中断服务例程的入口地址
所以说你可以看到我们的硬件
一旦识别产生了一个中断之后呢
根据它的中断号或者异常号
来再进一步查找IDT和GDT
从而可以跳到我们说
中断服务例程的起始地址去执行
这是说中断机制建立完之后呢
我们硬件可以干的事情
那我们操作系统从哪开始工作呢
就从这儿开始工作
一旦我们这个EIP说
我们程序计数器跳这个地方
我们CPU跳出来执行之后呢
接下来的工作就是中断服务例程
整个处理过程也是我们ucore
里面的lab1来完成的
它完成了怎么去响应和进一步去
处理这个中断或者异常这个情况
那也意味着如果说我们在lab3里面
产生了一个页异常的一个中断
那我们相应应该有一个
页异常的一个中断服务例程
来应对这种事情
这需要我们去在lab3中
去完成这个练习的
那我们再看看lab2
lab1做了一个简单回顾
我们再看lab2 那lab2是干什么呢
lab2主要是完成对物理内存的管理
主要完成这个页表机制建立
所以说你可以看到
它首先要去查找当前我们内存中
有多少物理空间
可以去使用 建立好一个
就是对空闲物理空间的管理
那这里面需要注意的是
这个物理空间呢是连续的地址
我们管理的是一块连续地址空间
那和我们原理课里面讲的这个
说连续地址空间内存分配呢
是有紧密对应关系的
第二个我们建立一个
很简单的一个内存分配算法
基于连续地址空间的内存分配算法
这个算法其实在我们
数据结构课里面也都会涉及到
再最后就是要建立页机制
它从而可以实现离散地址空间的
一个有效的管理
可以把一个所谓的虚地址
映射到一块物理地址
那么这是有页表机制在这里面
完成了所谓的地址映射工作
怎么去建立页表机制呢
应该说是lab2里一个重要的实验内容
好 页表机制涉及到什么呢
涉及到我们说页表里面
每一项一些功能
那里面其实关注几个
第一个页表呢它有一个起始地址
这个起始地址是放在
我们的CR3寄存器里面存着的
它CR3寄存器这是
我们一个特殊的控制寄存器
它存着我们建立页表的
起始地址在什么地方
然后页表其实也是一个大数组
里面每一项我们称之为页目录项
或者是页表项
如果是对于80386 32位机器而言
它是一个二级的一个分层的页表
所以呢它这里面会有所谓这个
页目录项和页表项
这两者是不一样的
那么是二层的一个页表结构
对于页目录项而言
它里面的每一项存放着什么呢
是存放着页表的基地址就是这一块
这是一个项 那么这一项呢是32位
那对于页目录项来说
它很重要的一个信息就是这个
page table address
就是它所指向的对应页表的起始地址
而对于页表项来说呢
那它存的是什么呢
存的是对应那个物理页
页帧的起始地址
那有了这个起始地址
再加上我们说那个
EIP所表示的offset
就是页内的偏移
我们就会形成最终的物理地址
那这是整个说CR3
然后这个是页目录项还有页表项
这三者结合在一起呢
就可以构成一个映射机制
那这是我们lab2里面来完成的工作
相信大家在完成lab1 lab2之后呢
对我刚才提到中断处理机制
页表机制有更深刻的理解
那么有了这个基础
等于是有了这个机制之后呢
我们就可以来完成虚存管理了
那我们虚存管理要干什么事情
首先要充分利用lab1和lab2
已经实现好的框架
我们已经实现好的中断处理框架
我们实现好了页表这个
映射机制的框架 这个我们要重用
在基础之上我们要去设计一个
虚存的管理总体框架 从而可以达到
我们刚才说的那个实验目标
在实验总体框架之后呢
我们要去针对总体框架关键部分
来进行处理
比如说怎么去有效处理这个页的
缺页错之后这个中断处理例程
中断服务例程这一块是要去完成的
怎么去处理针对硬盘
因为你要把页换入换出
换入到内存里面 从硬盘换入到内存
或者从内存导出到我们的硬盘
那这个swap机制
我们称之为swap机制你要去实现
你需要涉及到硬盘的读写
而这一块其实在我们的原理课里面
讲的比较少一点
因为它很具体的和我们这个
外设disk有打交道
而这个其实是分散的
那再后面就是完成页替换算法
这是我们专门的
就是原理课很重要的部分
就是讲各种各样的页替换算法
那我们需要建立好
前面这些机制之后呢
就可以实现页替换算法
从而可以最终的完成
让这个页替换算法能够
在我们这个系统中能够正常的工作
来实现它所表达的功能
就是换入换出的功能
那这是说我们是整个虚存管理
这个lab3的总体的一个框架
那可以看出来它和我们原理课
有很紧密的联系
这里面涉及到很多的知识点
在我们原理课里面都涉及到了
那它和原理课之间的区别在什么地方
区别在于实验是把原理课
一些分散的知识点
分散的关键的一些知识点呢
给它集中在一起
形成有机的整体来解决问题
那这也是说希望大家做实验
希望能体会到一点和原理课互相补充
一个很重要的一个方面
它的重要作用是什么呢
希望大家把原理课里面
所有学到的知识点
能够通过做实验 能够融会贯通
形成一体的一个感觉
你能够知道做一个系统
做一个操作系统应该
怎么把这些知识点给贯彻在一起
来完成相应的功能
而不是说一个一个分散的一个点
之间没有任何联系
当你完成这个lab3之后
你就会把前面介绍这些知识呢
能够有效地整合在一起
有效地整合在一起
好 那我就把这个总体框架
再逐一给大家做个简单介绍
第一点是要完成虚拟内存的管理机制
这里面最主要涉及到IDT硬盘的读写
和缺页异常的建立
好 那这个虚拟内存的这个管理呢
首先是需要初始化一些相关的工作
那初始化工作很明显就是
我们在ucore整体初始化函数
kern_ init这里面展开的
这个函数其实从我们lab1到lab8
都是从这个开始展开的
所以说有同学说要看这个
我看代码怎么去看
首先从这儿开始看起
可以从这个函数开始可以知道
整个这个系统初始化的一个过程
那在lab3之前是lab1 lab2
它们完成的是什么呢
是物理内存初始化
pmm 以及中断的初始化
比如PIC中断控制器IDT中断描述符表
这两部分的初始化
那这也是呢给我们进一步
去做虚存管理呢也是奠定了基础
第二步就是要设立这个页帧的空间
就是虚地址和物理地址对应关系
你要设置好这个
我们说这个实验环境
比如说我们说有5个物理页
但是你现在有6到7个虚拟页
那页不够了
你把这个环境要建好之后
才能够去进一步去验证
后续这个页替换算法
那为了建立好这个关系
要把这个关系表现清楚
我们还需要去进一步去阐述
就是这里面有些不在物理内存中的
合法的虚拟页
怎么去表述这个属性
因为有时候这个页被换出去了
它从应用程序的角度或者
站在使用者角度来说它还是存在的
但其实已经被我们OS换出去了
我们需要把这个特征给表述出来
那你需要去考虑怎么去设计
那第二点是关于我们说刚才
前面提到要给内存的使用者
提供一个虚拟的一个使用的内存空间
那这个内存空间呢
表示这是我们要去建立好的
用了两个关键数据结构
一个是mm struct
一个是vma struct
后续会进一步展开
其实这两个数据结构呢
我们可以把我们使用者或者
将来我们这个用户进程
它所使用的这个合法的一个
虚拟内存空间给表示出来
这是这一块的一些表述
那我们再看第三点
第三点要建立页表映射
页访问异常处理等函数的实现
这希望把前面两者给结合起来
把页表映射和缺页中断这一块
给有机的结合起来
当产生了缺页中断之后
我们需要把相应它缺失的页表的
映射关系给重新建立起来
从而可以使得在一个有限的
物理空间里面能够访问
更大的虚拟空间这么一个目标
这是我们第三步要去完善的
那第三点我们是需要去把这个
缺页异常之后要把这个相应的
物理内存和我们虚拟内存的
映射关系重新建立起来
为此我们需要完成一个
重要的函数do_pgfault的实现
它也位于这个vmm.c这里面
这里面有很详细的讲解
讲述了它一旦接收到这个任务之后呢
它怎么来完成的
它的call graph就是函数调用关系
这是它列出来的函数调用关系
那这里面你需要去完成一系列工作
要识别到底是什么原因
产生了这个缺页错误或者缺页异常
那么结合你进一步去分析
这个页呢是否是合法的
如果是合法的
你就要把这个映射关系建立好
如果不是合法的那么应该报错
甚至把当前这个系统给停下来
这是说这个do_pgfault
大致要完成的工作
第四步呢就是要开始测试了
就是我们说你前面
建好这一部分之后呢
我们怎么能够来看你建好这个页表项
是否能完成虚拟地址映射
其实如果说你正确完成lab2
那么这一步
我觉得第四步应该是比较容易完成的
第五步呢第五步要检查
你这个能否正确的表述
我们这个虚拟页它到底
是在内存中还是在硬盘中
那么这个也需要考虑怎么去能够
检测你前面的实现是否正确
那第六点呢 实际上是说我们要看看
当某一个虚拟的这个页表映射关系
它实际是把这个对应的页呢
是放到了硬盘上
那能否正常的从硬盘读进来
或者是写回去
那这里面需要去再做一个检查
我们需要去检查
在做实验上需要注意
基本上步骤一步步来检查
你前面的实现是否正确
那最后一步呢 假如说这些都做好了
最后一步干啥呢
就看你那个页替换算法
是否正确工作了
因为你的页替换算法其实是
需要前面那些能够正常工作之后
才能够去检查你的页替换算法
是否正常工作
所以说你最后才去检查你这个
你实现的页替换算法
是否能正常的完成它所需要的
把先进来的页
给先换出去这么一个功能
那其实关于这个vmm这个后面测试
其实有一系列的测试
要确保我们刚才建立合法的
虚拟空间是合理的
这里面有个check_vma_struct
这方面的一些工作
第二部分是要测试什么呢
测试你的page fault
这个机制能够正确的响应
就比如说我要访问
一个合法的一个虚拟地址
这个虚拟地址它其实
是可以被正常访问的
但是它的映射关系还没建好
所以一旦产生异常之后呢
我们这个do_pgfault需要能够建好
check_pgfault
是看你完成的那个函数是否建好了
这是看函数正确性这一块
就关于异常处理这一块
关于缺页异常处理这一块
怎么来完成
这是一个check
好 做完这个check之后
接下来check什么呢
check你的页替换算法
所以我们重点看swap这一块
这个check_swap实际上是要测试
你的页替换算法是否正确
当然你这边会涉及到什么呢
涉及到怎么去有效实现页替换算法
在swap_fifo.c和swap.h里面
这里面有详细的描述
你怎么去实现一个页替换算法
在这个框架之下实现就OK了
那整个这个关于
实现所谓的这个不同的
页替换算法框架在哪呢
就是在swap.c和swap.h里面
这里面建好了整体的一个框架
你可以看到这个框架里面
大致的一个关键一些函数
在这个swap_manager里面要去体现
这是要去测试的