update lab3

This commit is contained in:
chyyuu 2014-10-26 11:40:42 +08:00
parent 89d28c7ba1
commit 7dc7687862
13 changed files with 101 additions and 94 deletions

View File

@ -1,3 +1,4 @@
# 实验三:虚拟内存管理
做完实验二后大家可以了解并掌握物理内存管理中的连续空间分配算法的具体实现以及如何建立二级页表。本次实验是在实验二的基础上借助于页表机制和实验一中涉及的中断异常处理机制完成Page
Fault异常处理和FIFO页替换算法的实现。实验原理最大的区别是在设计了如何在磁盘上缓存内存页从而能够支持虚存管理提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。

View File

@ -1,4 +1,4 @@
## 1. 实验目的
## 实验目的
* 了解虚拟内存的Page Fault异常处理实现
* 了解页替换算法在操作系统中的实现

View File

@ -1,5 +1,5 @@
### 2.1 练习
### 练习
#### 练习0填写已有实验
@ -11,17 +11,23 @@
的时候需要参考页面所在 VMA
的权限,同时需要注意映射物理页时需要操作内存控制
结构所指定的页表而不是内核的页表。注意在LAB2 EXERCISE
1处填写代码。执行“make
qemu”后如果通过check\_pgfault函数的测试后会有“check\_pgfault()
1处填写代码。执行
```
make qemu
```
如果通过check\_pgfault函数的测试后会有“check\_pgfault()
succeeded!”的输出表示练习1基本正确。
#### 练习2补充完成基于FIFO的页面替换算法需要编程
完成vmm.c中的do\_pgfault函数并且在实现FIFO算法的swap\_fifo.c中完成map\_swappable和swap\_out\_vistim函数。通过对swap的测试。注意在LAB2
EXERCISE 2处填写代码。执行“make
qemu”后如果通过check\_swap函数的测试后会有“check\_swap()
EXERCISE 2处填写代码。执行
```
make qemu
```
如果通过check\_swap函数的测试后会有“check\_swap()
succeeded!”的输出表示练习2基本正确。
#### 扩展练习 Challenge实现识别dirty bit的 extended clock页替换算法需要编程
challenge部分不是必做部分不过在正确最后会酌情加分。需写出有详细的设计、分析和测试的实验报告。完成出色的可获得适当加分。(基本实验完成后一周内完成,单独提交)。
challenge部分不是必做部分不过在正确最后会酌情加分。需写出有详细的设计、分析和测试的实验报告。完成出色的可获得适当加分。

View File

@ -1,46 +1,46 @@
### 2.2 项目组成
### 项目组成
表1实验三文件列表
表1实验三文件列表
```
|-- boot
|-- kern
| |-- driver
| | |-- …
| | |-- ide.c
| | \`-- ide.h
| |-- fs
| | |-- fs.h
| | |-- swapfs.c
| | \`-- swapfs.h
| |-- init
| | |-- …
| | \`-- init.c
| |-- mm
| | |-- default\_pmm.c
| | |-- default\_pmm.h
| | |-- memlayout.h
| | |-- mmu.h
| | |-- pmm.c
| | |-- pmm.h
| | |-- swap.c
| | |-- swap.h
| | |-- swap\_fifo.c
| | |-- swap\_fifo.h
| | |-- vmm.c
| | \`-- vmm.h
| |-- sync
| \`-- trap
| |-- trap.c
| \`-- …
|-- libs
| |-- list.h
| \`-- …
\`-- tools
```
|-- boot
|-- kern
| |-- driver
| | |-- …
| | |-- ide.c
| | \`-- ide.h
| |-- fs
| | |-- fs.h
| | |-- swapfs.c
| | \`-- swapfs.h
| |-- init
| | |-- …
| | \`-- init.c
| |-- mm
| | |-- default\_pmm.c
| | |-- default\_pmm.h
| | |-- memlayout.h
| | |-- mmu.h
| | |-- pmm.c
| | |-- pmm.h
| | |-- swap.c
| | |-- swap.h
| | |-- swap\_fifo.c
| | |-- swap\_fifo.h
| | |-- vmm.c
| | \`-- vmm.h
| |-- sync
| \`-- trap
| |-- trap.c
| \`-- …
|-- libs
| |-- list.h
| \`-- …
\`-- tools
```
相对与实验二,实验三主要增加的文件如上表红色部分所示,主要修改的文件如上表紫色部分所示,其他需要用到的重要文件用黑色表示。主要改动如下:
相对与实验二,实验三主要改动如下:
* kern/mm/default\_pmm.[ch]实现基于struct pmm\_manager类框架的Fist-Fit物理内存分配参考实现分配最小单位为页即4096字节相关分配页和释放页等实现会间接被kmalloc/kfree等函数使用。
* kern/mm/pmm.[ch]pmm.h定义物理内存分配类框架struct pmm\_manager。pmm.c包含了对此物理内存分配类框架的访问以及与建立、修改、访问页表相关的各种函数实现。在本实验中会用到kmalloc/kfree等函数。
@ -50,8 +50,8 @@
* kern/mm/memlayout.h修改了struct Page增加了两项pra\_\*成员结构其中pra\_page\_link可以用来建立描述各个页访问情况比如根据访问先后的链表。在本实验中会涉及使用这两个成员结构以及le2page等宏。
* kern/mm/vmm.[ch]vmm.h描述了mm\_structvma\_struct等表述可访问的虚存地址访问的一些信息下面会进一步详细讲解。vmm.c涉及mm,vma结构数据的创建/销毁/查找/插入等函数这些函数在check\_vma、check\_vmm等中被使用理解即可。而page
fault处理相关的do\_pgfault函数是本次实验需要涉及完成的。
* kern/mm/swap.[ch]:定义了实现页替换算法类框架struct swap\_manager。swap.c包含了对此页替换算法类框架的初始化、页换入/换出等各种函数实现。重点是要理解何时调用swap\_out和swap\_in函数。和如何在此框架下连接具体的页替换算法实现。check\_swap函数以及被此函数调用的\_fifo\_check\_swap函数完成了对本次实验中的练习2FIFO页替换算法基本正确性的检查可了解便于知道为何产生错误。
* kern/mm/swap\_fifo.[ch]FIFO页替换算法的基于类框架struct swap\_manager的简化实现主要被swap.c的相关函数调用。重点是\_fifo\_map\_swappable函数可用于建立页访问属性和关系比如访问时间的先后顺序和\_fifo\_swap\_out\_victim函数可用于实现挑选出要换出的页当然换出哪个页需要借助于fifo\_map\_swappable函数建立的某种属性关系已选出合适的页。
* kern/mm/swap.[ch]定义了实现页替换算法类框架struct swap\_manager。swap.c包含了对此页替换算法类框架的初始化、页换入/换出等各种函数实现。重点是要理解何时调用swap\_out和swap\_in函数。和如何在此框架下连接具体的页替换算法实现。check\_swap函数以及被此函数调用的\_fifo\_check\_swap函数完成了对本次实验中的练习2FIFO页替换算法基本正确性的检查可了解便于知道为何产生错误。
* kern/mm/swap\_fifo.[ch]FIFO页替换算法的基于页替换算法类框架struct swap\_manager的简化实现主要被swap.c的相关函数调用。重点是\_fifo\_map\_swappable函数可用于建立页访问属性和关系比如访问时间的先后顺序和\_fifo\_swap\_out\_victim函数可用于实现挑选出要换出的页当然换出哪个页需要借助于fifo\_map\_swappable函数建立的某种属性关系已选出合适的页。
* kern/mm/mmu.h其中定义额也页表项的各种属性位比如PTE\_P\\PET\_D\\PET\_A等对于实现扩展实验的clock算法会有帮助。
本次实验的主要练习集中在vmm.c中的do\_pgfault函数和swap\_fifo.c中的\_fifo\_map\_swappable函数、\_fifo\_swap\_out\_victim函数。
@ -61,8 +61,8 @@ fault处理相关的do\_pgfault函数是本次实验需要涉及完成的。
编译并运行代码的命令如下:
```
make
make qemu
make
make qemu
```
则可以得到如附录所示的显示内容(仅供参考,不是标准答案输出)

View File

@ -1,6 +1,5 @@
## 2. 实验内容
## 实验内容
做完实验二后大家可以了解并掌握物理内存管理中的连续空间分配算法的具体实现以及如何建立二级页表。本次实验是在实验二的基础上借助于页表机制和实验一中涉及的中断异常处理机制完成Page
Fault异常处理和FIFO页替换算法的实现结合磁盘提供的缓存空间从而能够支持虚存管理提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的涉及到与进程管理系统、文件系统等的交叉访问。如果大家有余力可以尝试完成扩展练习实现extended
clock页替换算法。
本次实验是在实验二的基础上借助于页表机制和实验一中涉及的中断异常处理机制完成Page
Fault异常处理和FIFO页替换算法的实现结合磁盘提供的缓存空间从而能够支持虚存管理提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。这个实验与实际操作系统中的实现比较起来要简单不过需要了解实验一和实验二的具体实现。实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的涉及到与进程管理系统、文件系统等的交叉访问。如果大家有余力可以尝试完成扩展练习实现extended clock页替换算法。

View File

@ -1,10 +1,12 @@
### 3.1 基本原理概述
### 基本原理概述
什么是虚拟内存简单地说是指程序员或CPU
“需要”和直接“看到”的内存这其实暗示了两点1、虚拟内存单元不一定有实际的物理内存单元对应即实际的物理内存单元可能不存在2、如果虚拟内存单元对应有实际的物理内存单元那二者的地址一般不是相等的。通过操作系统的某种内存管理和映射技术可建立虚拟内存与实际的物理内存的对应关系使得程序员或CPU访问的虚拟内存地址会转换为另外一个物理内存地址。
什么是虚拟内存简单地说是指程序员或CPU“看到”的内存。但有几点需要注意
那么这个“虚拟”的作用或意义在哪里体现呢在操作系统中虚拟内存其实包含多个虚拟层次在不同的层次体现了不同的作用。首先在有了分页机制后程序员或CPU直接“看到”的地址已经不是实际的物理地址了这已经有一层虚拟化我们可简称为内存地址虚拟化。有了内存地址虚拟化我们就可以通过设置页表项来限定软件运行时的访问空间确保软件运行不越界完成内存访问保护的功能。
1. 虚拟内存单元不一定有实际的物理内存单元对应,即实际的物理内存单元可能不存在;
2. 如果虚拟内存单元对应有实际的物理内存单元,那二者的地址一般是不相等的;
3. 通过操作系统实现的某种内存映射可建立虚拟内存与物理内存的对应关系使得程序员或CPU访问的虚拟内存地址会自动转换为一个物理内存地址。
通过内存地址虚拟化可以使得软件在没有访问某虚拟内存地址时不分配具体的物理内存而只有在实际访问某虚拟内存地址时操作系统再动态地分配物理内存建立虚拟内存到物理内存的页映射关系这种技术属于lazy load技术简称按需分页demand paging。把不经常访问的数据所占的内存空间临时写到硬盘上这样可以腾出更多的空闲内存空间给经常访问的数据当CPU访问到不经常访问的数据时再把这些数据从硬盘读入到内存中这种技术称为页换入换出page
swap in/out。这种内存管理技术给了程序员更大的内存“空间”我们称为内存空间虚拟化。
那么这个“虚拟”的作用或意义在哪里体现呢在操作系统中虚拟内存其实包含多个虚拟层次在不同的层次体现了不同的作用。首先在有了分页机制后程序员或CPU“看到”的地址已经不是实际的物理地址了这已经有一层虚拟化我们可简称为内存地址虚拟化。有了内存地址虚拟化我们就可以通过设置页表项来限定软件运行时的访问空间确保软件运行不越界完成内存访问保护的功能。
通过内存地址虚拟化可以使得软件在没有访问某虚拟内存地址时不分配具体的物理内存而只有在实际访问某虚拟内存地址时操作系统再动态地分配物理内存建立虚拟内存到物理内存的页映射关系这种技术称为按需分页demand paging。把不经常访问的数据所占的内存空间临时写到硬盘上这样可以腾出更多的空闲内存空间给经常访问的数据当CPU访问到不经常访问的数据时再把这些数据从硬盘读入到内存中这种技术称为页换入换出page swap in/out。这种内存管理技术给了程序员更大的内存“空间”从而可以让更多的程序在内存中并发运行。

View File

@ -1,13 +1,13 @@
### 3.2 实验执行流程概述
### 实验执行流程概述
本次实验主要完成ucore内核对虚拟内存的管理工作。其总体设计思路还是比较简单即首先完成初始化虚拟内存管理机制即需要设置好哪些页需要放在物理内存中哪些页不需要放在物理内存中而是可被换出到硬盘上并涉及完善建立页表映射、页错误异常处理操作等函数实现。然后就执行一组访存测试看看我们建立的页表项是否能够正确完成虚实地址映射是否正确描述了虚拟内存页在物理内存中还是在硬盘上是否能够正确把虚拟内存页在物理内存和硬盘之间进行传递是否正确实现了页面替换算法等。lab3的总体执行流程如下。
本次实验主要完成ucore内核对虚拟内存的管理工作。其总体设计思路还是比较简单即首先完成初始化虚拟内存管理机制即需要设置好哪些页需要放在物理内存中哪些页不需要放在物理内存中而是可被换出到硬盘上并涉及完善建立页表映射、页访问异常处理操作等函数实现。然后就执行一组访存测试看看我们建立的页表项是否能够正确完成虚实地址映射是否正确描述了虚拟内存页在物理内存中还是在硬盘上是否能够正确把虚拟内存页在物理内存和硬盘之间进行传递是否正确实现了页面替换算法等。lab3的总体执行流程如下。
首先是初始化过程。参考ucore总控函数init的代码可以看到在调用完成虚拟内存初始化的vmm\_init函数之前需要首先调用pmm\_init函数完成物理内存的管理这也是我们lab2已经完成的内容。接着是执行中断和异常相关的初始化工作即调用pic\_init函数和idt\_init函数等这些工作与lab1的中断异常初始化工作的内容是相同的。
在调用完idt\_init函数之后将进一步调用三个lab3中才有的新函数vmm\_init、ide\_init和swap\_init。这三个函数设计了本次实验中的两个练习。第一个函数vmm\_init是检查我们的练习1是否正确实现了。为了表述不在物理内存中的“合法”虚拟页需要有数据结构来描述这样的页为此ucore建立了mm\_struct和vma\_struct数据结构在3.3小节中有进一步详细描述假定我们已经描述好了这样的“合法”虚拟页当ucore访问这些“合法”虚拟页时会由于没有虚实地址映射而产生页错误异常。如果我们正确实现了练习1则do\_pgfault函数会申请一个空闲物理页并建立好虚实映射关系从而使得这样的“合法”虚拟页有实际的物理页帧对应。这样练习1就算完成了。
在调用完idt\_init函数之后将进一步调用三个lab3中才有的新函数vmm\_init、ide\_init和swap\_init。这三个函数设计了本次实验中的两个练习。第一个函数vmm\_init是检查我们的练习1是否正确实现了。为了表述不在物理内存中的“合法”虚拟页需要有数据结构来描述这样的页为此ucore建立了mm\_struct和vma\_struct数据结构在3.3小节中有进一步详细描述假定我们已经描述好了这样的“合法”虚拟页当ucore访问这些“合法”虚拟页时会由于没有虚实地址映射而产生页访问异常。如果我们正确实现了练习1则do\_pgfault函数会申请一个空闲物理页并建立好虚实映射关系从而使得这样的“合法”虚拟页有实际的物理页帧对应。这样练习1就算完成了。
ide\_init和swap\_init是为练习2准备的。由于页面置换算法的实现存在对硬盘数据块的读写所以ide\_init就是完成对用于页换入换出的硬盘简称swap硬盘的初始化工作。完成ide\_init函数后ucore就可以对这个swap硬盘进行读写操作了。swap\_init函数首先建立swap\_managerswap\_manager是完成页面替换过程的主要功能模块其中包含了页面置换算法的实现具体内容可参考5小节。然后会进一步调用执行check\_swap函数在内核中分配一些页模拟对这些页的访问这会产生页错误异常。如果我们正确实现了练习2就可通过do\_pgfault来调用swap\_map\_swappable函数来查询这些页的访问情况并间接调用实现页面置换算法的相关函数把“不常用”的页换出到磁盘上。
ide\_init和swap\_init是为练习2准备的。由于页面置换算法的实现存在对硬盘数据块的读写所以ide\_init就是完成对用于页换入换出的硬盘简称swap硬盘的初始化工作。完成ide\_init函数后ucore就可以对这个swap硬盘进行读写操作了。swap\_init函数首先建立swap\_managerswap\_manager是完成页面替换过程的主要功能模块其中包含了页面置换算法的实现具体内容可参考5小节。然后会进一步调用执行check\_swap函数在内核中分配一些页模拟对这些页的访问这会产生页访问异常。如果我们正确实现了练习2就可通过do\_pgfault来调用swap\_map\_swappable函数来查询这些页的访问情况并间接调用实现页面置换算法的相关函数把“不常用”的页换出到磁盘上。
ucore在实现上述技术时需要解决三个关键问题

View File

@ -1,2 +1,2 @@
## 3. 虚拟内存管理
## 虚拟内存管理

View File

@ -1,19 +1,23 @@
## 4. Page Fault异常处理
## Page Fault异常处理
对于第三节提到的第二个关键问题解决的关键是page fault异常处理过程中主要涉及的函数 -- do\_pgfault。在程序的执行过程中由于某种原因(页框不存在/写只读页等)而使 CPU 无法最终访问到相应的物理内存单元即无法完成从虚拟地址到物理地址映射时CPU 会产生一次页错误异常,从而需要进行相应的页错误异常服务例程。这个页错误异常处理的时机就是求调页/页换入换出/处理的执行时机。当相关处理完成后,页错误异常服务例程会返回到产生异常的指令处重新执行,使得软件可以继续正常运行下去。
实现虚存管理的一个关键是page fault异常处理其过程中主要涉及到函数 -- do\_pgfault的具体实现。比如在程序的执行过程中由于某种原因(页框不存在/写只读页等)而使 CPU 无法最终访问到相应的物理内存单元即无法完成从虚拟地址到物理地址映射时CPU 会产生一次页访问异常,从而需要进行相应的页访问异常的中断服务例程。这个页访问异常处理的时机被操作系统充分利用来完成虚存管理,即实现“按需调页”/“页换入换出”处理的执行时机。当相关处理完成后,页访问异常服务例程会返回到产生异常的指令处重新执行,使得应用软件可以继续正常运行下去。
具体而言,当启动分页机制以后,如果一条指令或数据的虚拟地址所对应的物理页框不在内存中或者访问的类型有错误(比如写一个只读页或用户态程序访问内核态的数据等),就会发生页错误异常。产生页面异常的原因主要有:
具体而言,当启动分页机制以后,如果一条指令或数据的虚拟地址所对应的物理页框不在内存中或者访问的类型有错误(比如写一个只读页或用户态程序访问内核态的数据等),就会发生页访问异常。产生页访问异常的原因主要有:
* 目标页不存在页表项全为0即该线性地址与物理地址尚未建立映射或者已经撤销)
* 相应的物理页不在内存中页表项非空但Present标志位=0比如在swap分区或磁盘文件上),这将在下面介绍换页机制实现时进一步讲解如何处理;
* 访问权限不符合(此时页表项P标志=1比如企图写只读页面).
* 目标页不存在页表项全为0即该线性地址与物理地址尚未建立映射或者已经撤销)
* 相应的物理页不在内存中页表项非空但Present标志位=0比如在swap分区或磁盘文件上),这在本次实验中会出现,我们将在下面介绍换页机制实现时进一步讲解如何处理;
* 不满足访问权限(此时页表项P标志=1但低权限的程序试图访问高权限的地址空间或者有程序试图写只读页面).
当出现上面情况之一那么就会产生页面page fault\#PF异常。产生异常的线性地址存储在CR2中并且将是page fault的产生类型保存在 error code 中,比如 bit 0 表示是否 PTE\_P为0bit 1 表示是否 write 操作
当出现上面情况之一那么就会产生页面page fault\#PF异常。CPU会把产生异常的线性地址存储在CR2中并且把表示页访问异常类型的值简称页访问异常错误码errorCode保存在中断栈中
产生页错误异常后CPU硬件和软件都会做一些事情来应对此事。首先页错误异常也是一种异常所以针对一般异常的硬件处理操作是必须要做的即CPU在当前内核栈保存当前被打断的程序现场即依次压入当前被打断程序使用的eflagscseiperrorCode由于页错误异常的中断号是0xECPU把异常中断号0xE对应的中断异常服务例程的地址vectors.S中的标号vector14处加载到cs和eip寄存器中开始执行中断服务例程。这时ucore开始处理异常中断首先需要保存硬件没有保存的寄存器。在vectors.S中的标号vector14处先把中断号压入内核栈然后再在trapentry.S中的标号\_\_alltraps处把ds、es和其他通用寄存器都压栈。自此被打断的程序现场被保存在内核栈中。接下来在trap.c的trap函数开始了中断服务例程的处理流程大致调用关系为
>[提示]页访问异常错误码有32位。位0为表示对应物理页不存在表示写异常比如写了只读页表示访问权限异常比如用户态程序访问内核空间的数据
trap--\> trap\_dispatch--\>pgfault\_handler--\>do\_pgfault
>[提示] CR2是页故障线性地址寄存器保存最后一次出现页故障的全32位线性地址。CR2用于发生页异常时报告出错信息。当发生页异常时处理器把引起页异常的线性地址保存在CR2中。操作系统中对应的中断服务例程可以检查CR2的内容从而查出线性地址空间中的哪个页引起本次异常。
产生页访问异常后CPU硬件和软件都会做一些事情来应对此事。首先页访问异常也是一种异常所以针对一般异常的硬件处理操作是必须要做的即CPU在当前内核栈保存当前被打断的程序现场即依次压入当前被打断程序使用的EFLAGSCSEIPerrorCode由于页访问异常的中断号是0xECPU把异常中断号0xE对应的中断服务例程的地址vectors.S中的标号vector14处加载到CS和EIP寄存器中开始执行中断服务例程。这时ucore开始处理异常中断首先需要保存硬件没有保存的寄存器。在vectors.S中的标号vector14处先把中断号压入内核栈然后再在trapentry.S中的标号\_\_alltraps处把DS、ES和其他通用寄存器都压栈。自此被打断的程序执行现场context被保存在内核栈中。接下来在trap.c的trap函数开始了中断服务例程的处理流程大致调用关系为
> trap--\> trap\_dispatch--\>pgfault\_handler--\>do\_pgfault
下面需要具体分析一下do\_pgfault函数。do\_pgfault的调用关系如下图所示
@ -21,8 +25,6 @@ trap--\> trap\_dispatch--\>pgfault\_handler--\>do\_pgfault
![image](../lab3_figs/image002.png)
产生页错误异常后CPU把引起页错误异常的虚拟地址装到寄存器CR2中并给出了出错码tf-\>tf\_err指示引起页错误异常的存储器访问的类型。而中断服务例程会调用页错误异常处理函数do\_pgfault进行具体处理。页错误异常处理是实现按需分页、swap in/out的关键之处。
产生页访问异常后CPU把引起页访问异常的线性地址装到寄存器CR2中并给出了出错码errorCode说明了页访问异常的类型。ucore OS会把这个值保存在struct trapframe 中tf\_err成员变量中。而中断服务例程会调用页访问异常处理函数do\_pgfault进行具体处理。这里的页访问异常处理是实现按需分页、页换入换出机制的关键之处。
ucore中do\_pgfault函数是完成页错误异常处理的主要函数它根据从CPU的控制寄存器CR2中获取的页错误异常的虚拟地址以及根据
error
code的错误类型来查找此虚拟地址是否在某个VMA的地址范围内以及是否满足正确的读写权限如果在此范围内并且权限也正确这认为这是一次合法访问但没有建立虚实对应关系。所以需要分配一个空闲的内存页并修改页表完成虚地址到物理地址的映射刷新TLB然后调用iret中断返回到产生页错误异常的指令处重新执行此指令。如果该虚地址不再某VMA范围内这认为是一次非法访问。
ucore中do\_pgfault函数是完成页访问异常处理的主要函数它根据从CPU的控制寄存器CR2中获取的页访问异常的物理地址以及根据errorCode的错误类型来查找此地址是否在某个VMA的地址范围内以及是否满足正确的读写权限如果在此范围内并且权限也正确这认为这是一次合法访问但没有建立虚实对应关系。所以需要分配一个空闲的内存页并修改页表完成虚地址到物理地址的映射刷新TLB然后调用iret中断返回到产生页访问异常的指令处重新执行此指令。如果该虚地址不在某VMA范围内这认为是一次非法访问。

View File

@ -1,13 +1,11 @@
### 5.1 页替换算法
操作系统为何要进行页面置换呢?这是由于操作系统给用户态的应用程序提供了一个虚拟的“大容量”内存空间,而实际的物理内存空间又没有那么大。所以操作系统就就“瞒着”应用程序,只把应用程序中“常用”的数据和代码放在物理内存中,而不常用的数据和代码放在了硬盘这样的存储介质上。如果应用程序访问的是“常用”的数据和代码,那么操作系统已经放置在内存中了,不会出现什么问题。但当应用程序访问它认为应该在内存中的的数据或代码时,如果这些数据或代码不在内存中,则根据上一小节的介绍,会产生页错误异常。这时,操作系统必须能够应对这种页错误异常,即尽快把应用程序当前需要的数据或代码放到内存中来,然后重新执行应用程序产生异常的访存指令。如果在把硬盘中对应的数据或代码调入内存前,操作系统发现物理内存已经没有空闲空间了,这时操作系统必须把它认为“不常用”的页换出到磁盘上去,以腾出内存空闲空间给应用程序所需的数据或代码。
操作系统为何要进行页面置换呢?这是由于操作系统给用户态的应用程序提供了一个虚拟的“大容量”内存空间,而实际的物理内存空间又没有那么大。所以操作系统就就“瞒着”应用程序,只把应用程序中“常用”的数据和代码放在物理内存中,而不常用的数据和代码放在了硬盘这样的存储介质上。如果应用程序访问的是“常用”的数据和代码,那么操作系统已经放置在内存中了,不会出现什么问题。但当应用程序访问它认为应该在内存中的的数据或代码时,如果这些数据或代码不在内存中,则根据上一小节的介绍,会产生页访问异常。这时,操作系统必须能够应对这种页访问异常,即尽快把应用程序当前需要的数据或代码放到内存中来,然后重新执行应用程序产生异常的访存指令。如果在把硬盘中对应的数据或代码调入内存前,操作系统发现物理内存已经没有空闲空间了,这时操作系统必须把它认为“不常用”的页换出到磁盘上去,以腾出内存空闲空间给应用程序所需的数据或代码。
操作系统迟早会碰到没有内存空闲空间而必须要置换出内存中某个“不常用”的页的情况。如何判断内存中哪些是“常用”的页,哪些是“不常用”的页,把“常用”的页保持在内存中,在物理内存空闲空间不够的情况下,把“不常用”的页置换到硬盘上就是页替换算法着重考虑的问题。容易理解,一个好的页替换算法会导致页错误异常次数少,也就意味着访问硬盘的次数也少,从而使得应用程序执行的效率就高。本次实验涉及的页替换算法(包括扩展练习):
操作系统迟早会碰到没有内存空闲空间而必须要置换出内存中某个“不常用”的页的情况。如何判断内存中哪些是“常用”的页,哪些是“不常用”的页,把“常用”的页保持在内存中,在物理内存空闲空间不够的情况下,把“不常用”的页置换到硬盘上就是页替换算法着重考虑的问题。容易理解,一个好的页替换算法会导致页访问异常次数少,也就意味着访问硬盘的次数也少,从而使得应用程序执行的效率就高。本次实验涉及的页替换算法(包括扩展练习):
* 先进先出(First In First Out, FIFO)页替换算法该算法总是淘汰最先进入内存的页即选择在内存中驻留时间最久的页予以淘汰。只需把一个应用程序在执行过程中已调入内存的页按先后次序链接成一个队列队列头指向内存中驻留时间最久的页队列尾指向最近被调入内存的页。这样需要淘汰页时从队列头很容易查找到需要淘汰的页。FIFO算法只是在应用程序按线性顺序访问地址空间时效果才好否则效率不高。因为那些常被访问的页往往在内存中也停留得最久结果它们因变“老”而不得不被置换出去。FIFO算法的另一个缺点是它有一种异常现象Belady现象即在增加放置页的页帧的情况下反而使页错误异常次数增多。
* 先进先出(First In First Out, FIFO)页替换算法该算法总是淘汰最先进入内存的页即选择在内存中驻留时间最久的页予以淘汰。只需把一个应用程序在执行过程中已调入内存的页按先后次序链接成一个队列队列头指向内存中驻留时间最久的页队列尾指向最近被调入内存的页。这样需要淘汰页时从队列头很容易查找到需要淘汰的页。FIFO算法只是在应用程序按线性顺序访问地址空间时效果才好否则效率不高。因为那些常被访问的页往往在内存中也停留得最久结果它们因变“老”而不得不被置换出去。FIFO算法的另一个缺点是它有一种异常现象Belady现象即在增加放置页的页帧的情况下反而使页访问异常次数增多。
* 时钟Clock页替换算法也称最近未使用 (Not Used Recently, NUR)
页替换算法。虽然二次机会算法是一个较合理的算法但它经常需要在链表中移动页面这样做既降低了效率又是不必要的。一个更好的办法是把各个页面组织成环形链表的形式类似于一个钟的表面。然后把一个指针指向最古老的那个页面或者说最先进来的那个页面。时钟算法和第二次机会算法的功能是完全一样的只是在具体实现上有所不同。时钟算法需要在页表项PTE中设置了一位访问位来表示此页表项对应的页当前是否被访问过。当该页被访问时CPU中的MMU硬件将把访问位置“1”。然后将内存中所有的页都通过指针链接起来并形成一个循环队列。初始时设置一个当前指针指向某页比如最古老的那个页面。操作系统需要淘汰页时对当前指针指向的页所对应的页表项进行查询如果访问位为“0”则淘汰该页把它换出到硬盘上如果访问位为“1”这将该页表项的此位置“0”继续访问下一个页。该算法近似地体现了LRU的思想且易于实现开销少。但该算法需要硬件支持来设置访问位且该算法在本质上与FIFO算法是类似的惟一不同的是在clock算法中跳过了访问位为1的页。
* 时钟Clock页替换算法是LRU算法的一种近似实现。时钟页替换算法把各个页面组织成环形链表的形式类似于一个钟的表面。然后把一个指针简称当前指针指向最老的那个页面即最先进来的那个页面。另外时钟算法需要在页表项PTE中设置了一位访问位来表示此页表项对应的页当前是否被访问过。当该页被访问时CPU中的MMU硬件将把访问位置“1”。当操作系统需要淘汰页时对当前指针指向的页所对应的页表项进行查询如果访问位为“0”则淘汰该页如果该页被写过则还要把它换出到硬盘上如果访问位为“1”则将该页表项的此位置“0”继续访问下一个页。该算法近似地体现了LRU的思想且易于实现开销少需要硬件支持来设置访问位。时钟页替换算法在本质上与FIFO算法是类似的不同之处是在时钟页替换算法中跳过了访问位为1的页。
* 改进的时钟Enhanced Clock页替换算法在时钟置换算法中淘汰一个页面时只考虑了页面是否被访问过但在实际情况中还应考虑被淘汰的页面是否被修改过。因为淘汰修改过的页面还需要写回硬盘使得其置换代价大于未修改过的页面。改进的时钟置换算法除了考虑页面的访问情况还需考虑页面的修改情况。即该算法不但希望淘汰的页面是最近未使用的页而且还希望被淘汰的页是在主存驻留期间其页面内容未被修改过的。这需要为每一页的对应页表项内容中增加一位引用位和一位修改位。当该页被访问时CPU中的MMU硬件将把访问位置“1”。当该页被“写”时CPU中的MMU硬件将把修改位置“1”。这样这两位就存在四种可能的组合情况00表示最近未被引用也未被修改首先选择此页淘汰01最近未被使用但被修改其次选择10最近使用而未修改再次选择11最近使用且修改最后选择。该算法与时钟算法相比可进一步减少磁盘的I/O操作次数但为了查找到一个尽可能适合淘汰的页面可能需要经过多次扫描增加了算法本身的执行开销。
* 改进的时钟Enhanced Clock页替换算法在时钟置换算法中淘汰一个页面时只考虑了页面是否被访问过但在实际情况中还应考虑被淘汰的页面是否被修改过。因为淘汰修改过的页面还需要写回硬盘使得其置换代价大于未修改过的页面,所以优先淘汰没有修改的页,减少磁盘操作次数。改进的时钟置换算法除了考虑页面的访问情况还需考虑页面的修改情况。即该算法不但希望淘汰的页面是最近未使用的页而且还希望被淘汰的页是在主存驻留期间其页面内容未被修改过的。这需要为每一页的对应页表项内容中增加一位引用位和一位修改位。当该页被访问时CPU中的MMU硬件将把访问位置“1”。当该页被“写”时CPU中的MMU硬件将把修改位置“1”。这样这两位就存在四种可能的组合情况00表示最近未被引用也未被修改首先选择此页淘汰01最近未被使用但被修改其次选择10最近使用而未修改再次选择11最近使用且修改最后选择。该算法与时钟算法相比可进一步减少磁盘的I/O操作次数但为了查找到一个尽可能适合淘汰的页面可能需要经过多次扫描增加了算法本身的执行开销。

View File

@ -6,7 +6,7 @@
* 哪些页可以被换出?
* 一个虚拟的页如何与硬盘上的扇区建立对应关系?
* 何时进行换入和换出操作?
* 如何设计数据结构支持页替换算法?
* 如何设计数据结构支持页替换算法?
* 如何完成页的换入换出操作?
这些问题在下面会逐一进行分析。注意,在实验三中仅实现了简单的页面置换机制,但现在还没有涉及实验四和实验五才实现的内核线程和用户进程,所以还无法通过内核线程机制实现一个完整意义上的虚拟内存页面置换功能。
@ -48,7 +48,7 @@ ucore认为合法的所有虚拟内存空间集合而mm中的每个vma表示
换出页面的时机相对复杂一些针对不同的策略有不同的时机。ucore目前大致有两种策略即积极换出策略和消极换出策略。积极换出策略是指操作系统周期性地或在系统不忙的时候主动把某些认为“不常用”的页换出到硬盘上从而确保系统中总有一定数量的空闲页存在这样当需要空闲页时基本上能够及时满足需求消极换出策略是指只是当试图得到空闲页时发现当前没有空闲的物理页可供分配这时才开始查找“不常用”页面并把一个或多个这样的页换出到硬盘上。
在实验三中的基本练习中支持上述第二种情况。对于第一种积极换出策略即每隔1秒执行一次的实现积极的换出策略可考虑在扩展练习中实现。对于第二种消极的换出策略则是在ucore调用alloc\_pages函数获取空闲页时此函数如果发现无法从物理内存页分配器比如first fit获得空闲页就会进一步调用swap\_out函数换出某页实现一种消极的换出策略。
在实验三中的基本练习中,支持上述第二种情况。对于第一种积极换出策略即每隔1秒执行一次的实现积极的换出策略可考虑在扩展练习中实现。对于第二种消极的换出策略则是在ucore调用alloc\_pages函数获取空闲页时此函数如果发现无法从物理内存页分配器获得空闲页就会进一步调用swap\_out函数换出某页实现一种消极的换出策略。
#### 4. 页替换算法的数据结构设计
@ -62,7 +62,7 @@ uintptr\_t pra\_vaddr;
};
```
pra\_page\_link可用来构造按页的第一次访问时间进行排序的一个链表这个链表的开始表示第一次访问时间最近的页链表结尾表示第一次访问时间最远的页。当然链表头可以就可设置为pra\_list\_head定义在swap\_fifo.c中构造的时机在page fault发生后进行do\_pgfault函数时。pra\_vaddr可以用来记录此物理页对应的虚拟页起始地址。
pra\_page\_link可用来构造按页的第一次访问时间进行排序的一个链表这个链表的开始表示第一次访问时间最近的页链表结尾表示第一次访问时间最远的页。当然链表头可以就可设置为pra\_list\_head定义在swap\_fifo.c中构造的时机在page fault发生后进行do\_pgfault函数时。pra\_vaddr可以用来记录此物理页对应的虚拟页起始地址。
当一个物理页 struct Page 需要被 swap 出去的时候首先需要确保它已经分配了一个位于磁盘上的swap page由连续的8个扇区组成。这里为了简化设计在swap\_check函数中建立了每个虚拟页唯一对应的swap page其对应关系设定为虚拟页对应的PTE的索引值 = swap page的扇区起始位置\*8。
@ -89,11 +89,11 @@ struct swap_manager
};
```
这里关键的两个函数指针是map\_swappable和swap\_out\_vistim前一个函数用于记录页访问情况相关属性后一个函数用于挑选需要换出的页。显然第二个函数依赖第一个函数记录的页访问情况。tick\_event函数指针也很重要结合定时产生的中断可以实现一种积极的换页策略。
这里关键的两个函数指针是map\_swappable和swap\_out\_vistim前一个函数用于记录页访问情况相关属性后一个函数用于挑选需要换出的页。显然第二个函数依赖第一个函数记录的页访问情况。tick\_event函数指针也很重要结合定时产生的中断可以实现一种积极的换页策略。
#### 5. swap_check的检查实现
下面具体讲述一下实验三中实现置换算法的页面置换的检查执行逻辑便于大家实现练习2。实验三页面置换的检查过程在函数swap\_checkkern/mm/swap.c中其大致流程如下。
下面具体讲述一下实验三中实现置换算法的页面置换的检查执行逻辑便于大家实现练习2。实验三的检查过程在函数swap\_checkkern/mm/swap.c中其大致流程如下。
1. 调用mm\_create建立mm变量并调用vma\_create创建vma变量设置合法的访问范围为4KB\~24KB
2. 调用free\_page等操作模拟形成一个只有4个空闲 physical page并设置了从4KB\~24KB的连续5个虚拟页的访问操作

View File

@ -1,2 +1 @@
## 5. 页面置换机制的实现

View File

@ -1,14 +1,14 @@
## 6. 实验报告要求
从网站上下载lab3.zip后,解压得到本文档和代码目录 lab3完成实验中的各个练习。完成代码编写并检查无误后在对应目录下执行 make handin 任务,即会自动生成 lab3-handin.tar.gz。最后请一定提前或按时提交到网络学堂上。
从网站上下载lab3后解压得到本文档和代码目录 lab3完成实验中的各个练习。完成代码编写并检查无误后在对应目录下执行 make handin 任务,即会自动生成 lab3-handin.tar.gz。
注意有“LAB3”的注释代码中所有需要完成的地方challenge除外都有“LAB3”和“YOUR CODE”的注释请在提交时特别注意保持注释并将“YOUR CODE”替换为自己的学号并且将所有标有对应注释的部分填上正确的代码。所有扩展实验的加分总和不超过10分。
注意有“LAB3”的注释代码中所有需要完成的地方challenge除外都有“LAB3”和“YOUR CODE”的注释请在提交时特别注意保持注释并将“YOUR CODE”替换为自己的学号并且将所有标有对应注释的部分填上正确的代码。
附录:正确输出的参考:
```
yuchen@yuchen-PAI4:~/oscourse/2012spring/lab3_figs/lab3-code-2012$ make qemu
$ make qemu
(THU.CST) os is loading ...
Special kernel symbols:
entry 0xc010002c (phys)