Finish lab 3

This commit is contained in:
Object Ho 2014-10-14 18:35:21 +08:00
parent eadf811630
commit 0a5dc30b11
22 changed files with 442 additions and 595 deletions

View File

@ -66,6 +66,19 @@
* [附录B. 实现物理内存探测](lab2/lab2_3_6_implement_probe_phymem.md)
* [附录C. 链接地址/虚地址/物理地址/加载地址以及edata/end/text的含义](lab2/lab2_3_7_phymemlab_concepts.md)
* [Lab 3](lab3.md)
* [实验目的](lab3/lab3_1_goals.md)
* [实验内容](lab3/lab3_2_lab2.md)
* [练习](lab3/lab3_2_1_exercises.md)
* [项目组成](lab3/lab3_2_2_files.md)
* [虚拟内存管理](lab3/lab3_3_vmm.md)
* [基本原理概述](lab3/lab3_3_1_vmm_principles.md)
* [实验执行流程概述](lab3/lab3_3_2_labs_steps.md)
* [关键数据结构和相关函数分析](lab3/lab3_3_3_data_structures.md)
* [Page Fault异常处理](lab3/lab3_4_page_fault_handler.md)
* [页面置换机制的实现](lab3/lab3_5_swapping.md)
* [页替换算法](lab3/lab3_5_1_page_swapping.md)
* [页面置换机制实验报告要求](lab3/lab3_5_2_page_swapping_principles.md)
* [实验报告要求](lab3/lab3_6_labs_requirement.md)
* [Lab 4](lab4.md)
* [Lab 5](lab5.md)
* [Lab 6](lab6.md)

BIN
lab2/.DS_Store vendored

Binary file not shown.

BIN
lab2_figs/.DS_Store vendored Normal file

Binary file not shown.

585
lab3.md
View File

@ -1,588 +1,3 @@
# 实验三:虚拟内存管理
## 1. 实验目的
* 了解虚拟内存的Page Fault异常处理实现
* 了解页替换算法在操作系统中的实现
## 2. 实验内容
做完实验二后大家可以了解并掌握物理内存管理中的连续空间分配算法的具体实现以及如何建立二级页表。本次实验是在实验二的基础上借助于页表机制和实验一中涉及的中断异常处理机制完成Page
Fault异常处理和FIFO页替换算法的实现结合磁盘提供的缓存空间从而能够支持虚存管理提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的涉及到与进程管理系统、文件系统等的交叉访问。如果大家有余力可以尝试完成扩展练习实现extended
clock页替换算法。
### 2.1 练习
#### 练习0填写已有实验
本实验依赖实验1/2。请把你做的实验1/2的代码填入本实验中代码中有“LAB1”,“LAB2”的注释相应部分。
#### 练习1给未被映射的地址映射上物理页需要编程
完成do\_pgfaultmm/vmm.c函数给未被映射的地址映射上物理页。设置访问权限
的时候需要参考页面所在 VMA
的权限,同时需要注意映射物理页时需要操作内存控制
结构所指定的页表而不是内核的页表。注意在LAB2 EXERCISE
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()
succeeded!”的输出表示练习2基本正确。
#### 扩展练习 Challenge实现识别dirty bit的 extended clock页替换算法需要编程
challenge部分不是必做部分不过在正确最后会酌情加分。需写出有详细的设计、分析和测试的实验报告。完成出色的可获得适当加分。基本实验完成后一周内完成单独提交
### 2.2 项目组成
表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
相对与实验二,实验三主要增加的文件如上表红色部分所示,主要修改的文件如上表紫色部分所示,其他需要用到的重要文件用黑色表示。主要改动如下:
* 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等函数。
* libs/list.h定义了通用双向链表结构以及相关的查找、插入等基本操作这是建立基于链表方法的物理内存管理以及其他内核功能的基础。在lab0文档中有相关描述。其他有类似双向链表需求的内核功能模块可直接使用list.h中定义的函数。在本实验中会多次用到插入删除等操作函数。
* kern/driver/ide.[ch]定义和实现了内存页swap机制所需的磁盘扇区的读写操作支持在本实验中会涉及通过swapfs\_\*函数间接使用文件中的函数。故了解即可。
* kern/fs/\*定义和实现了内存页swap机制所需从磁盘读数据到内存页和写内存数据到磁盘上去的函数
swapfs\_read/swapfs\_write。在本实验中会涉及使用这两个函数。
* 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/mmu.h其中定义额也页表项的各种属性位比如PTE\_P\\PET\_D\\PET\_A等对于实现扩展实验的clock算法会有帮助。
本次实验的主要练习集中在vmm.c中的do\_pgfault函数和swap\_fifo.c中的\_fifo\_map\_swappable函数、\_fifo\_swap\_out\_victim函数。
#### 编译执行
编译并运行代码的命令如下:
```
make
make qemu
```
则可以得到如附录所示的显示内容(仅供参考,不是标准答案输出)
## 3.虚拟内存管理
### 3.1 基本原理概述
什么是虚拟内存简单地说是指程序员或CPU
“需要”和直接“看到”的内存这其实暗示了两点1、虚拟内存单元不一定有实际的物理内存单元对应即实际的物理内存单元可能不存在2、如果虚拟内存单元对应有实际的物理内存单元那二者的地址一般不是相等的。通过操作系统的某种内存管理和映射技术可建立虚拟内存与实际的物理内存的对应关系使得程序员或CPU访问的虚拟内存地址会转换为另外一个物理内存地址。
那么这个“虚拟”的作用或意义在哪里体现呢在操作系统中虚拟内存其实包含多个虚拟层次在不同的层次体现了不同的作用。首先在有了分页机制后程序员或CPU直接“看到”的地址已经不是实际的物理地址了这已经有一层虚拟化我们可简称为内存地址虚拟化。有了内存地址虚拟化我们就可以通过设置页表项来限定软件运行时的访问空间确保软件运行不越界完成内存访问保护的功能。
通过内存地址虚拟化可以使得软件在没有访问某虚拟内存地址时不分配具体的物理内存而只有在实际访问某虚拟内存地址时操作系统再动态地分配物理内存建立虚拟内存到物理内存的页映射关系这种技术属于lazy
load技术简称按需分页demand
paging。把不经常访问的数据所占的内存空间临时写到硬盘上这样可以腾出更多的空闲内存空间给经常访问的数据当CPU访问到不经常访问的数据时再把这些数据从硬盘读入到内存中这种技术称为页换入换出page
swap
in/out。这种内存管理技术给了程序员更大的内存“空间”我们称为内存空间虚拟化。
### 3.2 实验执行流程概述
本次实验主要完成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就算完成了。
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在实现上述技术时需要解决三个关键问题
1、 当程序运行中访问内存产生page
fault异常时如何判定这个引起异常的虚拟地址内存访问是越界、写只读页的“非法地址”访问还是由于数据被临时换出到磁盘上或还没有分配内存的“合法地址”访问
2、 何时进行请求调页/页换入换出处理?
3、 如何在现有ucore的基础上实现页替换算法
接下来将进一步分析完成lab3主要注意的关键问题和涉及的关键数据结构。
### 3.3 关键数据结构和相关函数分析
对于第一个问题的出现,在于实验二中有关内存的数据结构和相关操作都是直接针对实际存在的资源--物理内存空间的管理没有从一般应用程序对内存的“需求”考虑即需要有相关的数据结构和操作来体现一般应用程序对虚拟内存的“需求”。一般应用程序的对虚拟内存的“需求”与物理内存空间的“供给”没有直接的对应关系ucore是通过page
fault异常处理来间接完成这二者之间的衔接。
page\_fault函数不知道哪些是“合法”的虚拟页原因是ucore还缺少一定的数据结构来描述这种不在物理内存中的“合法”虚拟页。为此ucore通过建立mm\_struct和vma\_struct数据结构描述了ucore模拟应用程序运行所需的合法内存空间。当访问内存产生page
fault异常时可获得访问的内存的方式读或写以及具体的虚拟内存地址这样ucore就可以查询此地址看是否属于vma\_struct数据结构中描述的合法地址范围中如果在则可根据具体情况进行请求调页/页换入换出处理这就是练习2涉及的部分如果不在则报错。mm\_struct和vma\_struct数据结构结合页表表示虚拟地址空间和物理地址空间的示意图如下所示
图 虚拟地址空间和物理地址空间的示意图
![image](lab3/image001.png)
在ucore中描述应用程序对虚拟内存“需求”的数据结构是vma\_struct定义在vmm.h中以及针对vma\_struct的函数操作。这里把一个vma\_struct结构的变量简称为vma变量。vma\_struct的定义如下
```
struct vma_struct {
// the set of vma using the same PDT
struct mm_struct *vm_mm;
uintptr_t vm_start; // start addr of vma
uintptr_t vm_end; // end addr of vma
uint32_t vm_flags; // flags of vma
//linear list link which sorted by start addr of vma
list_entry_t list_link;
};
```
vm\_start和vm\_end描述了一个连续地址的虚拟内存空间的起始位置和结束位置这两个值都应该是PGSIZE 对齐的,而且描述的是一个合理的地址空间范围(即严格确保 vm\_start < vm\_end的关系list\_link是一个双向链表按照从小到大的顺序把一系列用vma\_struct表示的虚拟内存空间链接起来并且还要求这些链起来的vma\_struct应该是不相交的即vma之间的地址空间无交集vm\_flags表示了这个虚拟内存空间的属性目前的属性包括
```
#define VM_READ 0x00000001 //只读
#define VM_WRITE 0x00000002 //可读写
#define VM_EXEC 0x00000004 //可执行
```
vm\_mm是一个指针指向一个比vma\_struct更高的抽象层次的数据结构mm\_struct这里把一个mm\_struct结构的变量简称为mm变量。这个数据结构表示了包含所有虚拟内存空间的共同属性具体定义如下
```
struct mm_struct {
// linear list link which sorted by start addr of vma
list_entry_t mmap_list;
// current accessed vma, used for speed purpose
struct vma_struct *mmap_cache;
pde_t *pgdir; // the PDT of these vma
int map_count; // the count of these vma
void *sm_priv; // the private data for swap manager
};
```
mmap_list是双向链表头链接了所有属于同一页目录表的虚拟内存空间mmap\_cache是指向当前正在使用的虚拟内存空间由于操作系统执行的“局部性”原理当前正在用到的虚拟内存空间在接下来的操作中可能还会用到这时就不需要查链表而是直接使用此指针就可找到下一次要用到的虚拟内存空间。由于
mmap_cache 的引入,可使得 mm_struct 数据结构的查询加速 30% 以上。pgdir
所指向的就是 mm_struct
数据结构所维护的页表。通过访问pgdir可以查找某虚拟地址对应的页表项是否存在以及页表项的属性等。map_count记录
mmap_list 里面链接的 vma_struct
的个数。sm_priv指向用来链接记录页访问情况的链表头这建立了mm_struct和后续要讲到的swap_manager之间的联系。
涉及vma_struct的操作函数也比较简单主要包括三个
* vma_create--创建vma
* insert_vma_struct--插入一个vma
* find_vma--查询vma。
vma\_create函数根据输入参数vm\_start、vm\_end、vm\_flags来创建并初始化描述一个虚拟内存空间的vma\_struct结构变量。insert\_vma\_struct函数完成把一个vma变量按照其空间位置[vma-\>vm\_start,vma-\>vm\_end]从小到大的顺序插入到所属的mm变量中的mmap\_list双向链表中。find\_vma根据输入参数addr和mm变量查找在mm变量中的mmap\_list双向链表中某个vma包含此addr即vma-\>vm\_start<=
addr <vma-\>end这三个函数与后续讲到的page fault异常处理有紧密联系
涉及mm\_struct的操作函数比较简单只有mm\_create和mm\_destroy两个函数从字面意思就可以看出是是完成mm\_struct结构的变量创建和删除。在mm\_create中用kmalloc分配了一块空间所以在mm\_destroy中也要对应进行释放。在ucore运行过程中会产生描述虚拟内存空间的vma\_struct结构所以在mm\_destroy中也要进对这些mmap\_list中的vma进行释放。
## 4.Page Fault异常处理
对于第三节提到的第二个关键问题解决的关键是page
fault异常处理过程中主要涉及的函数 --
do\_pgfault。在程序的执行过程中由于某种原因页框不存在/写只读页等)而使
CPU
无法最终访问到相应的物理内存单元即无法完成从虚拟地址到物理地址映射时CPU
会产生一次页错误异常,从而需要进行相应的页错误异常服务例程。这个页错误异常处理的时机就是求调页/页换入换出/处理的执行时机。当相关处理完成后,页错误异常服务例程会返回到产生异常的指令处重新执行,使得软件可以继续正常运行下去。
具体而言,当启动分页机制以后,如果一条指令或数据的虚拟地址所对应的物理页框不在内存中或者访问的类型有错误(比如写一个只读页或用户态程序访问内核态的数据等),就会发生页错误异常。产生页面异常的原因主要有:
* 目标页面不存在页表项全为0即该线性地址与物理地址尚未建立映射或者已经撤销)
* 相应的物理页面不在内存中页表项非空但Present标志位=0比如在swap分区或磁盘文件上),这将在下面介绍换页机制实现时进一步讲解如何处理;
* 访问权限不符合(此时页表项P标志=1比如企图写只读页面).
当出现上面情况之一那么就会产生页面page
fault\#PF异常。产生异常的线性地址存储在CR2中并且将是page
fault的产生类型保存在 error code 中,比如 bit 0 表示是否 PTE\_P为0bit 1
表示是否 write 操作。
产生页错误异常后CPU硬件和软件都会做一些事情来应对此事。首先页错误异常也是一种异常所以针对一般异常的硬件处理操作是必须要做的即CPU在当前内核栈保存当前被打断的程序现场即依次压入当前被打断程序使用的eflagscseiperrorCode由于页错误异常的中断号是0xE
CPU把异常中断号0xE对应的中断异常服务例程的地址vectors.S中的标号vector14处加载到cs和eip寄存器中开始执行中断服务例程。这时ucore开始处理异常中断首先需要保存硬件没有保存的寄存器。在vectors.S中的标号vector14处先把中断号压入内核栈然后再在trapentry.S中的标号\_\_alltraps处把ds、es和其他通用寄存器都压栈。自此被打断的程序现场被保存在内核栈中。接下来在trap.c的trap函数开始了中断服务例程的处理流程大致调用关系为
trap--\> trap\_dispatch--\>pgfault\_handler--\>do\_pgfault
下面需要具体分析一下do\_pgfault函数。do\_pgfault的调用关系如下图所示
图 do\_pgfault的调用关系图
![image](lab3/image002.png)
产生页错误异常后CPU把引起页错误异常的虚拟地址装到寄存器CR2中并给出了出错码tf-\>tf\_err指示引起页错误异常的存储器访问的类型。而中断服务例程会调用页错误异常处理函数do\_pgfault进行具体处理。页错误异常处理是实现按需分页、swap
in/out的关键之处。
ucore中do\_pgfault函数是完成页错误异常处理的主要函数它根据从CPU的控制寄存器CR2中获取的页错误异常的虚拟地址以及根据
error
code的错误类型来查找此虚拟地址是否在某个VMA的地址范围内以及是否满足正确的读写权限如果在此范围内并且权限也正确这认为这是一次合法访问但没有建立虚实对应关系。所以需要分配一个空闲的内存页并修改页表完成虚地址到物理地址的映射刷新TLB然后调用iret中断返回到产生页错误异常的指令处重新执行此指令。如果该虚地址不再某VMA范围内这认为是一次非法访问。
## 5.页面置换机制的实现
### 5.1 页替换算法
操作系统为何要进行页面置换呢?这是由于操作系统给用户态的应用程序提供了一个虚拟的“大容量”内存空间,而实际的物理内存空间又没有那么大。所以操作系统就就“瞒着”应用程序,只把应用程序中“常用”的数据和代码放在物理内存中,而不常用的数据和代码放在了硬盘这样的存储介质上。如果应用程序访问的是“常用”的数据和代码,那么操作系统已经放置在内存中了,不会出现什么问题。但当应用程序访问它认为应该在内存中的的数据或代码时,如果这些数据或代码不在内存中,则根据上一小节的介绍,会产生页错误异常。这时,操作系统必须能够应对这种页错误异常,即尽快把应用程序当前需要的数据或代码放到内存中来,然后重新执行应用程序产生异常的访存指令。如果在把硬盘中对应的数据或代码调入内存前,操作系统发现物理内存已经没有空闲空间了,这时操作系统必须把它认为“不常用”的页换出到磁盘上去,以腾出内存空闲空间给应用程序所需的数据或代码。
操作系统迟早会碰到没有内存空闲空间而必须要置换出内存中某个“不常用”的页的情况。如何判断内存中哪些是“常用”的页,哪些是“不常用”的页,把“常用”的页保持在内存中,在物理内存空闲空间不够的情况下,把“不常用”的页置换到硬盘上就是页替换算法着重考虑的问题。容易理解,一个好的页替换算法会导致页错误异常次数少,也就意味着访问硬盘的次数也少,从而使得应用程序执行的效率就高。本次实验涉及的页替换算法(包括扩展练习):
* 先进先出(First In First Out,
FIFO)页替换算法该算法总是淘汰最先进入内存的页即选择在内存中驻留时间最久的页予以淘汰。只需把一个应用程序在执行过程中已调入内存的页按先后次序链接成一个队列队列头指向内存中驻留时间最久的页队列尾指向最近被调入内存的页。这样需要淘汰页时从队列头很容易查找到需要淘汰的页。FIFO算法只是在应用程序按线性顺序访问地址空间时效果才好否则效率不高。因为那些常被访问的页往往在内存中也停留得最久结果它们因变“老”而不得不被置换出去。FIFO算法的另一个缺点是它有一种异常现象Belady现象即在增加放置页的页帧的情况下反而使页错误异常次数增多。
* 时钟Clock页替换算法也称最近未使用 (Not Used Recently, NUR)
页替换算法。虽然二次机会算法是一个较合理的算法但它经常需要在链表中移动页面这样做既降低了效率又是不必要的。一个更好的办法是把各个页面组织成环形链表的形式类似于一个钟的表面。然后把一个指针指向最古老的那个页面或者说最先进来的那个页面。时钟算法和第二次机会算法的功能是完全一样的只是在具体实现上有所不同。时钟算法需要在页表项PTE中设置了一位访问位来表示此页表项对应的页当前是否被访问过。当该页被访问时CPU中的MMU硬件将把访问位置“1”。然后将内存中所有的页都通过指针链接起来并形成一个循环队列。初始时设置一个当前指针指向某页比如最古老的那个页面。操作系统需要淘汰页时对当前指针指向的页所对应的页表项进行查询如果访问位为“0”则淘汰该页把它换出到硬盘上如果访问位为“1”这将该页表项的此位置“0”继续访问下一个页。该算法近似地体现了LRU的思想且易于实现开销少。但该算法需要硬件支持来设置访问位且该算法在本质上与FIFO算法是类似的惟一不同的是在clock算法中跳过了访问位为1的页。
* 改进的时钟Enhanced
Clock页替换算法在时钟置换算法中淘汰一个页面时只考虑了页面是否被访问过但在实际情况中还应考虑被淘汰的页面是否被修改过。因为淘汰修改过的页面还需要写回硬盘使得其置换代价大于未修改过的页面。改进的时钟置换算法除了考虑页面的访问情况还需考虑页面的修改情况。即该算法不但希望淘汰的页面是最近未使用的页而且还希望被淘汰的页是在主存驻留期间其页面内容未被修改过的。这需要为每一页的对应页表项内容中增加一位引用位和一位修改位。当该页被访问时CPU中的MMU硬件将把访问位置“1”。当该页被“写”时CPU中的MMU硬件将把修改位置“1”。这样这两位就存在四种可能的组合情况00表示最近未被引用也未被修改首先选择此页淘汰01最近未被使用但被修改其次选择10最近使用而未修改再次选择11最近使用且修改最后选择。该算法与时钟算法相比可进一步减少磁盘的I/O操作次数但为了查找到一个尽可能适合淘汰的页面可能需要经过多次扫描增加了算法本身的执行开销。
### 5.2 页面置换机制
如果要实现页面置换机制,只考虑页替换算法的设计与实现是远远不够的,还需考虑其他问题:
* 哪些页可以被换出?
* 一个虚拟的页如何与硬盘上的扇区建立对应关系?
* 何时进行换入和换出操作?
* 如何设计数据结构已支持页替换算法?
* 如何完成页的换入换出操作?
这些问题在下面会逐一进行分析。注意,在实验三中仅实现了简单的页面置换机制,但现在还没有涉及实验四和实验五才实现的内核线程和用户进程,所以还无法通过内核线程机制实现一个完整意义上的虚拟内存页面置换功能。
#### 1. 可以被换出的页
在操作系统的设计中一个基本的原则是并非所有的物理页都可以交换出去的只有映射到用户空间且被用户程序直接访问的页面才能被交换而被内核直接使用的内核空间的页面不能被换出。这里面的原因是什么呢操作系统是执行的关键代码需要保证运行的高效性和实时性如果在操作系统执行过程中发生了缺页现象则操作系统不得不等很长时间硬盘的访问速度比内存的访问速度慢2\~3个数量级这将导致整个系统运行低效。而且不难想象处理缺页过程所用到的内核代码或者数据如果被换出整个内核都面临崩溃的危险。
但在实验三实现的ucore中我们只是实现了换入换出机制还没有设计用户态执行的程序所以我们在实验三中仅仅通过执行check\_swap函数在内核中分配一些页模拟对这些页的访问然后通过do\_pgfault来调用swap\_map\_swappable函数来查询这些页的访问情况并间接调用相关函数换出“不常用”的页到磁盘上。
#### 2. 虚存中的页与硬盘上的扇区之间的映射关系
如果一个页被置换到了硬盘上那操作系统如何能简捷来表示这种情况呢在ucore的设计上充分利用了页表中的PTE来表示这种情况当一个
PTE
用来描述一般意义上的物理页时,显然它应该维护各种权限和映射关系,以及应该有
PTE\_P
标记;但当它用来描述一个被置换出去的物理页时,它被用来维护该物理页与 swap
磁盘上扇区的映射关系,并且该 PTE 不应该由 MMU 将它解释成物理页映射(即没有
PTE\_P 标记),与此同时对应的权限则交由 mm\_struct
来维护,当对位于该页的内存地址进行访问的时候,必然导致 page
fault然后ucore能够根据 PTE 描述的 swap
项将相应的物理页重新建立起来,并根据虚存所描述的权限重新设置好 PTE
使得内存访问能够继续正常进行。
如果一个页4KB/页被置换到了硬盘某8个扇区0.5KB/扇区),该
PTE的最低位--present位应该为0 (即 PTE\_P
标记为空表示虚实地址映射关系不存在接下来的7位暂时保留可以用作各种扩展而原来用来表示页帧号的高24位地址恰好可以用来表示此页在硬盘上的起始扇区的位置其从第几个扇区开始。为了在页表项中区别
0 和 swap 分区的映射,将 swap 分区的一个 page
空出来不用也就是说一个高24位不为0而最低位为0的PTE表示了一个放在硬盘上的页的起始扇区号见swap.h中对swap\_entry\_t的描述
```
swap_entry_t
--------------
| offset | reserved | 0 |
---------------
24 bits 7 bits 1 bit
```
考虑到硬盘的最小访问单位是一个扇区而一个扇区的大小为5122\^8字节所以需要8个连续扇区才能放置一个4KB的页。在ucore中用了第二个IDE硬盘来保存被换出的扇区根据实验三的输出信息
“ide 1: 262144(sectors), 'QEMU HARDDISK'.”
我们可以知道实验三可以保存262144/8=32768个页即128MB的内存空间。swap
分区的大小是 swapfs\_init 里面根据磁盘驱动的接口计算出来的,目前 ucore
里面要求 swap 磁盘至少包含 1000 个 page并且至多能使用 1<<24 个page
#### 3. 执行换入换出的时机
在实验三中, check\_mm\_struct变量这个数据结构表示了目前
ucore认为合法的所有虚拟内存空间集合而mm中的每个vma表示了一段地址连续的合法虚拟空间。当ucore或应用程序访问地址所在的页不在内存时就会产生
page
fault异常引起调用do\_pgfault函数此函数会判断产生访问异常的地址属于check\_mm\_struct某个vma表示的合法虚拟地址空间且保存在硬盘swap文件中即对应的PTE的高24位不为0而最低位为0则是执行页换入的时机将调用swap\_in函数完成页面换入。
换出页面的时机相对复杂一些针对不同的策略有不同的时机。ucore目前大致有两种策略即积极换出策略和消极换出策略。积极换出策略是指操作系统周期性地或在系统不忙的时候主动把某些认为“不常用”的页换出到硬盘上从而确保系统中总有一定数量的空闲页存在这样当需要空闲页时基本上能够及时满足需求消极换出策略是指只是当试图得到空闲页时发现当前没有空闲的物理页可供分配这时才开始查找“不常用”页面并把一个或多个这样的页换出到硬盘上。
在实验三中的基本练习中支持上述第二种情况。对于第一种积极换出策略即每隔1秒执行一次的实现积极的换出策略可考虑在扩展练习中实现。对于第二种消极的换出策略则是在ucore调用alloc\_pages函数获取空闲页时此函数如果发现无法从物理内存页分配器比如first
fit获得空闲页就会进一步调用swap\_out函数换出某页实现一种消极的换出策略。
#### 4. 页替换算法的数据结构设计
到实验二为止我们知道目前表示内存中物理页使用情况的变量是基于数据结构Page的全局变量pages数组pages的每一项表示了计算机系统中一个物理页的使用情况。为了表示物理页可被换出或已被换出的情况可对Page数据结构进行扩展
```
struct Page {
……
list\_entry\_t pra\_page\_link;
uintptr\_t 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。
为了实现各种页替换算法我们设计了一个页替换算法的类框架swap\_manager:
struct swap_manager
{
const char *name;
/* Global initialization for the swap manager */
int (*init) (void);
/* Initialize the priv data inside mm_struct */
int (*init_mm) (struct mm_struct *mm);
/* Called when tick interrupt occured */
int (*tick_event) (struct mm_struct *mm);
/* Called when map a swappable page into the mm_struct */
int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
/* When a page is marked as shared, this routine is called to delete the addr entry from the swap manager */
int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
/* Try to swap out a page, return then victim */
int (*swap_out_victim) (struct mm_struct *mm, struct Page *ptr_page, int in_tick);
/* check the page relpacement algorithm */
int (*check\_swap)(void);
};
这里关键的两个函数指针是map\_swappable和swap\_out\_vistim前一个函数用于记录页访问情况相关属性后一个函数用于挑选需要换出的页。显然第二个函数依赖与第一个函数记录的页访问情况。tick\_event函数指针也很重要结合定时产生的中断可以实现一种积极的换页策略。
#### 5. swap_check的检查实现
下面具体讲述一下实验三中实现置换算法的页面置换的检查执行逻辑便于大家实现练习2。实验三页面置换的检查过程在函数swap\_check
kern/mm/swap.c中其大致流程如下。
1. 调用mm\_create建立mm变量并调用vma\_create创建vma变量设置合法的访问范围为4KB\~24KB
2. 调用free\_page等操作模拟形成一个只有4个空闲 physical
page并设置了从4KB\~24KB的连续5个虚拟页的访问操作
3. 设置记录缺页次数的变量pgfault\_num=0执行check\_content\_set函数使得起始地址分别对起始地址为0x1000,
0x2000, 0x3000,
0x4000的虚拟页按时间顺序先后写操作访问由于之前没有建立页表所以会产生page
fault异常如果完成练习1则这些从4KB\~20KB的4虚拟页会与ucore保存的4个物理页帧建立映射关系
4. 然后对虚页对应的新产生的页表项进行合法性检查;
5. 然后进入测试页替换算法的主体执行函数check\_content\_access并进一步调用到\_fifo\_check\_swap函数如果通过了所有的assert。这进一步表示FIFO页替换算法基本正确实现
6. 最后恢复ucore环境。
## 6.实验报告要求
从网站上下载lab3.zip后解压得到本文档和代码目录
lab3完成实验中的各个练习。完成代码编写并检查无误后在对应目录下执行
make handin 任务,即会自动生成
lab3-handin.tar.gz。最后请一定提前或按时提交到网络学堂上。
注意有“LAB3”的注释代码中所有需要完成的地方challenge除外都有“LAB3”和“YOUR
CODE”的注释请在提交时特别注意保持注释并将“YOUR
CODE”替换为自己的学号并且将所有标有对应注释的部分填上正确的代码。所有扩展实验的加分总和不超过10分。
附录:正确输出的参考:
```
yuchen@yuchen-PAI4:~/oscourse/2012spring/lab3/lab3-code-2012$ make qemu
(THU.CST) os is loading ...
Special kernel symbols:
entry 0xc010002c (phys)
etext 0xc010962b (phys)
edata 0xc0122ac8 (phys)
end 0xc0123c10 (phys)
Kernel executable memory footprint: 143KB
memory management: default_pmm_manager
e820map:
memory: 0009f400, [00000000, 0009f3ff], type = 1.
memory: 00000c00, [0009f400, 0009ffff], type = 2.
memory: 00010000, [000f0000, 000fffff], type = 2.
memory: 07efd000, [00100000, 07ffcfff], type = 1.
memory: 00003000, [07ffd000, 07ffffff], type = 2.
memory: 00040000, [fffc0000, ffffffff], type = 2.
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
-------------------- BEGIN --------------------
PDE(0e0) c0000000-f8000000 38000000 urw
|-- PTE(38000) c0000000-f8000000 38000000 -rw
PDE(001) fac00000-fb000000 00400000 -rw
|-- PTE(000e0) faf00000-fafe0000 000e0000 urw
|-- PTE(00001) fafeb000-fafec000 00001000 -rw
--------------------- END ---------------------
check_vma_struct() succeeded!
page fault at 0x00000100: K/W [no page found].
check_pgfault() succeeded!
check_vmm() succeeded.
ide 0: 10000(sectors), 'QEMU HARDDISK'.
ide 1: 262144(sectors), 'QEMU HARDDISK'.
SWAP: manager = fifo swap manager
BEGIN check_swap: count 1, total 31992
mm->sm_priv c0123c04 in fifo_init_mm
setup Page Table for vaddr 0X1000, so alloc a page
setup Page Table vaddr 0~4MB OVER!
set up init env for check_swap begin!
page fault at 0x00001000: K/W [no page found].
page fault at 0x00002000: K/W [no page found].
page fault at 0x00003000: K/W [no page found].
page fault at 0x00004000: K/W [no page found].
set up init env for check_swap over!
write Virt Page c in fifo_check_swap
write Virt Page a in fifo_check_swap
write Virt Page d in fifo_check_swap
write Virt Page b in fifo_check_swap
write Virt Page e in fifo_check_swap
page fault at 0x00005000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x1000 to disk swap entry 2
write Virt Page b in fifo_check_swap
write Virt Page a in fifo_check_swap
page fault at 0x00001000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x2000 to disk swap entry 3
swap_in: load disk swap entry 2 with swap_page in vadr 0x1000
write Virt Page b in fifo_check_swap
page fault at 0x00002000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x3000 to disk swap entry 4
swap_in: load disk swap entry 3 with swap_page in vadr 0x2000
write Virt Page c in fifo_check_swap
page fault at 0x00003000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x4000 to disk swap entry 5
swap_in: load disk swap entry 4 with swap_page in vadr 0x3000
write Virt Page d in fifo_check_swap
page fault at 0x00004000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x5000 to disk swap entry 6
swap_in: load disk swap entry 5 with swap_page in vadr 0x4000
check_swap() succeeded!
++ setup timer interrupts
100 ticks
End of Test.
kernel panic at kern/trap/trap.c:20:
EOT: kernel seems ok.
Welcome to the kernel debug monitor!!
Type 'help' for a list of commands.
```

BIN
lab3/.DS_Store vendored Normal file

Binary file not shown.

4
lab3/lab3_1_goals.md Normal file
View File

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

View File

@ -0,0 +1,27 @@
### 2.1 练习
#### 练习0填写已有实验
本实验依赖实验1/2。请把你做的实验1/2的代码填入本实验中代码中有“LAB1”,“LAB2”的注释相应部分。
#### 练习1给未被映射的地址映射上物理页需要编程
完成do\_pgfaultmm/vmm.c函数给未被映射的地址映射上物理页。设置访问权限
的时候需要参考页面所在 VMA
的权限,同时需要注意映射物理页时需要操作内存控制
结构所指定的页表而不是内核的页表。注意在LAB2 EXERCISE
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()
succeeded!”的输出表示练习2基本正确。
#### 扩展练习 Challenge实现识别dirty bit的 extended clock页替换算法需要编程
challenge部分不是必做部分不过在正确最后会酌情加分。需写出有详细的设计、分析和测试的实验报告。完成出色的可获得适当加分。基本实验完成后一周内完成单独提交

68
lab3/lab3_2_2_files.md Normal file
View File

@ -0,0 +1,68 @@
### 2.2 项目组成
表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
```
相对与实验二,实验三主要增加的文件如上表红色部分所示,主要修改的文件如上表紫色部分所示,其他需要用到的重要文件用黑色表示。主要改动如下:
* 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等函数。
* libs/list.h定义了通用双向链表结构以及相关的查找、插入等基本操作这是建立基于链表方法的物理内存管理以及其他内核功能的基础。在lab0文档中有相关描述。其他有类似双向链表需求的内核功能模块可直接使用list.h中定义的函数。在本实验中会多次用到插入删除等操作函数。
* kern/driver/ide.[ch]定义和实现了内存页swap机制所需的磁盘扇区的读写操作支持在本实验中会涉及通过swapfs\_\*函数间接使用文件中的函数。故了解即可。
* kern/fs/\*定义和实现了内存页swap机制所需从磁盘读数据到内存页和写内存数据到磁盘上去的函数 swapfs\_read/swapfs\_write。在本实验中会涉及使用这两个函数。
* 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/mmu.h其中定义额也页表项的各种属性位比如PTE\_P\\PET\_D\\PET\_A等对于实现扩展实验的clock算法会有帮助。
本次实验的主要练习集中在vmm.c中的do\_pgfault函数和swap\_fifo.c中的\_fifo\_map\_swappable函数、\_fifo\_swap\_out\_victim函数。
#### 编译执行
编译并运行代码的命令如下:
```
make
make qemu
```
则可以得到如附录所示的显示内容(仅供参考,不是标准答案输出)

6
lab3/lab3_2_lab2.md Normal file
View File

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

View File

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

View File

@ -0,0 +1,19 @@
### 3.2 实验执行流程概述
本次实验主要完成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就算完成了。
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在实现上述技术时需要解决三个关键问题
1. 当程序运行中访问内存产生page
fault异常时如何判定这个引起异常的虚拟地址内存访问是越界、写只读页的“非法地址”访问还是由于数据被临时换出到磁盘上或还没有分配内存的“合法地址”访问
2. 何时进行请求调页/页换入换出处理?
3. 如何在现有ucore的基础上实现页替换算法
接下来将进一步分析完成lab3主要注意的关键问题和涉及的关键数据结构。

View File

@ -0,0 +1,61 @@
### 3.3 关键数据结构和相关函数分析
对于第一个问题的出现,在于实验二中有关内存的数据结构和相关操作都是直接针对实际存在的资源--物理内存空间的管理没有从一般应用程序对内存的“需求”考虑即需要有相关的数据结构和操作来体现一般应用程序对虚拟内存的“需求”。一般应用程序的对虚拟内存的“需求”与物理内存空间的“供给”没有直接的对应关系ucore是通过page
fault异常处理来间接完成这二者之间的衔接。
page\_fault函数不知道哪些是“合法”的虚拟页原因是ucore还缺少一定的数据结构来描述这种不在物理内存中的“合法”虚拟页。为此ucore通过建立mm\_struct和vma\_struct数据结构描述了ucore模拟应用程序运行所需的合法内存空间。当访问内存产生page
fault异常时可获得访问的内存的方式读或写以及具体的虚拟内存地址这样ucore就可以查询此地址看是否属于vma\_struct数据结构中描述的合法地址范围中如果在则可根据具体情况进行请求调页/页换入换出处理这就是练习2涉及的部分如果不在则报错。mm\_struct和vma\_struct数据结构结合页表表示虚拟地址空间和物理地址空间的示意图如下所示
图 虚拟地址空间和物理地址空间的示意图
![image](../lab3_figs/image001.png)
在ucore中描述应用程序对虚拟内存“需求”的数据结构是vma\_struct定义在vmm.h中以及针对vma\_struct的函数操作。这里把一个vma\_struct结构的变量简称为vma变量。vma\_struct的定义如下
```
struct vma_struct {
// the set of vma using the same PDT
struct mm_struct *vm_mm;
uintptr_t vm_start; // start addr of vma
uintptr_t vm_end; // end addr of vma
uint32_t vm_flags; // flags of vma
//linear list link which sorted by start addr of vma
list_entry_t list_link;
};
```
vm\_start和vm\_end描述了一个连续地址的虚拟内存空间的起始位置和结束位置这两个值都应该是PGSIZE 对齐的,而且描述的是一个合理的地址空间范围(即严格确保 vm\_start < vm\_end的关系list\_link是一个双向链表按照从小到大的顺序把一系列用vma\_struct表示的虚拟内存空间链接起来并且还要求这些链起来的vma\_struct应该是不相交的即vma之间的地址空间无交集vm\_flags表示了这个虚拟内存空间的属性目前的属性包括
```
#define VM_READ 0x00000001 //只读
#define VM_WRITE 0x00000002 //可读写
#define VM_EXEC 0x00000004 //可执行
```
vm\_mm是一个指针指向一个比vma\_struct更高的抽象层次的数据结构mm\_struct这里把一个mm\_struct结构的变量简称为mm变量。这个数据结构表示了包含所有虚拟内存空间的共同属性具体定义如下
```
struct mm_struct {
// linear list link which sorted by start addr of vma
list_entry_t mmap_list;
// current accessed vma, used for speed purpose
struct vma_struct *mmap_cache;
pde_t *pgdir; // the PDT of these vma
int map_count; // the count of these vma
void *sm_priv; // the private data for swap manager
};
```
mmap\_list是双向链表头链接了所有属于同一页目录表的虚拟内存空间mmap\_cache是指向当前正在使用的虚拟内存空间由于操作系统执行的“局部性”原理当前正在用到的虚拟内存空间在接下来的操作中可能还会用到这时就不需要查链表而是直接使用此指针就可找到下一次要用到的虚拟内存空间。由于mmap_cache 的引入,可使得 mm\_struct 数据结构的查询加速 30% 以上。pgdir
所指向的就是 mm\_struct数据结构所维护的页表。通过访问pgdir可以查找某虚拟地址对应的页表项是否存在以及页表项的属性等。map\_count记录mmap\_list 里面链接的 vma\_struct的个数。sm_priv指向用来链接记录页访问情况的链表头这建立了mm\_struct和后续要讲到的swap\_manager之间的联系。
涉及vma_struct的操作函数也比较简单主要包括三个
* vma_create--创建vma
* insert_vma_struct--插入一个vma
* find_vma--查询vma。
vma\_create函数根据输入参数vm\_start、vm\_end、vm\_flags来创建并初始化描述一个虚拟内存空间的vma\_struct结构变量。insert\_vma\_struct函数完成把一个vma变量按照其空间位置[vma-\>vm\_start,vma-\>vm\_end]从小到大的顺序插入到所属的mm变量中的mmap\_list双向链表中。find\_vma根据输入参数addr和mm变量查找在mm变量中的mmap\_list双向链表中某个vma包含此addr即vma-\>vm\_start<=addr <vma-\>end这三个函数与后续讲到的page fault异常处理有紧密联系
涉及mm\_struct的操作函数比较简单只有mm\_create和mm\_destroy两个函数从字面意思就可以看出是是完成mm\_struct结构的变量创建和删除。在mm\_create中用kmalloc分配了一块空间所以在mm\_destroy中也要对应进行释放。在ucore运行过程中会产生描述虚拟内存空间的vma\_struct结构所以在mm\_destroy中也要进对这些mmap\_list中的vma进行释放。

2
lab3/lab3_3_vmm.md Normal file
View File

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

View File

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

View File

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

View File

@ -0,0 +1,103 @@
### 5.2 页面置换机制
如果要实现页面置换机制,只考虑页替换算法的设计与实现是远远不够的,还需考虑其他问题:
* 哪些页可以被换出?
* 一个虚拟的页如何与硬盘上的扇区建立对应关系?
* 何时进行换入和换出操作?
* 如何设计数据结构已支持页替换算法?
* 如何完成页的换入换出操作?
这些问题在下面会逐一进行分析。注意,在实验三中仅实现了简单的页面置换机制,但现在还没有涉及实验四和实验五才实现的内核线程和用户进程,所以还无法通过内核线程机制实现一个完整意义上的虚拟内存页面置换功能。
#### 1. 可以被换出的页
在操作系统的设计中一个基本的原则是并非所有的物理页都可以交换出去的只有映射到用户空间且被用户程序直接访问的页面才能被交换而被内核直接使用的内核空间的页面不能被换出。这里面的原因是什么呢操作系统是执行的关键代码需要保证运行的高效性和实时性如果在操作系统执行过程中发生了缺页现象则操作系统不得不等很长时间硬盘的访问速度比内存的访问速度慢2\~3个数量级这将导致整个系统运行低效。而且不难想象处理缺页过程所用到的内核代码或者数据如果被换出整个内核都面临崩溃的危险。
但在实验三实现的ucore中我们只是实现了换入换出机制还没有设计用户态执行的程序所以我们在实验三中仅仅通过执行check\_swap函数在内核中分配一些页模拟对这些页的访问然后通过do\_pgfault来调用swap\_map\_swappable函数来查询这些页的访问情况并间接调用相关函数换出“不常用”的页到磁盘上。
#### 2. 虚存中的页与硬盘上的扇区之间的映射关系
如果一个页被置换到了硬盘上那操作系统如何能简捷来表示这种情况呢在ucore的设计上充分利用了页表中的PTE来表示这种情况当一个PTE用来描述一般意义上的物理页时显然它应该维护各种权限和映射关系以及应该有PTE\_P标记但当它用来描述一个被置换出去的物理页时它被用来维护该物理页与 swap 磁盘上扇区的映射关系,并且该 PTE 不应该由 MMU 将它解释成物理页映射(即没有 PTE\_P 标记),与此同时对应的权限则交由 mm\_struct 来维护,当对位于该页的内存地址进行访问的时候,必然导致 page fault然后ucore能够根据 PTE 描述的 swap 项将相应的物理页重新建立起来,并根据虚存所描述的权限重新设置好 PTE 使得内存访问能够继续正常进行。
如果一个页4KB/页被置换到了硬盘某8个扇区0.5KB/扇区该PTE的最低位--present位应该为0 (即 PTE\_P 标记为空表示虚实地址映射关系不存在接下来的7位暂时保留可以用作各种扩展而原来用来表示页帧号的高24位地址恰好可以用来表示此页在硬盘上的起始扇区的位置其从第几个扇区开始。为了在页表项中区别 0 和 swap 分区的映射,将 swap 分区的一个 page 空出来不用也就是说一个高24位不为0而最低位为0的PTE表示了一个放在硬盘上的页的起始扇区号见swap.h中对swap\_entry\_t的描述
```
swap_entry_t
-------------------------
| offset | reserved | 0 |
-------------------------
24 bits 7 bits 1 bit
```
考虑到硬盘的最小访问单位是一个扇区而一个扇区的大小为5122\^8字节所以需要8个连续扇区才能放置一个4KB的页。在ucore中用了第二个IDE硬盘来保存被换出的扇区根据实验三的输出信息
```
“ide 1: 262144(sectors), 'QEMU HARDDISK'.”
```
我们可以知道实验三可以保存262144/8=32768个页即128MB的内存空间。swap
分区的大小是 swapfs\_init 里面根据磁盘驱动的接口计算出来的,目前 ucore
里面要求 swap 磁盘至少包含 1000 个 page并且至多能使用 1<<24 个page
#### 3. 执行换入换出的时机
在实验三中, check\_mm\_struct变量这个数据结构表示了目前
ucore认为合法的所有虚拟内存空间集合而mm中的每个vma表示了一段地址连续的合法虚拟空间。当ucore或应用程序访问地址所在的页不在内存时就会产生page fault异常引起调用do\_pgfault函数此函数会判断产生访问异常的地址属于check\_mm\_struct某个vma表示的合法虚拟地址空间且保存在硬盘swap文件中即对应的PTE的高24位不为0而最低位为0则是执行页换入的时机将调用swap\_in函数完成页面换入。
换出页面的时机相对复杂一些针对不同的策略有不同的时机。ucore目前大致有两种策略即积极换出策略和消极换出策略。积极换出策略是指操作系统周期性地或在系统不忙的时候主动把某些认为“不常用”的页换出到硬盘上从而确保系统中总有一定数量的空闲页存在这样当需要空闲页时基本上能够及时满足需求消极换出策略是指只是当试图得到空闲页时发现当前没有空闲的物理页可供分配这时才开始查找“不常用”页面并把一个或多个这样的页换出到硬盘上。
在实验三中的基本练习中支持上述第二种情况。对于第一种积极换出策略即每隔1秒执行一次的实现积极的换出策略可考虑在扩展练习中实现。对于第二种消极的换出策略则是在ucore调用alloc\_pages函数获取空闲页时此函数如果发现无法从物理内存页分配器比如first fit获得空闲页就会进一步调用swap\_out函数换出某页实现一种消极的换出策略。
#### 4. 页替换算法的数据结构设计
到实验二为止我们知道目前表示内存中物理页使用情况的变量是基于数据结构Page的全局变量pages数组pages的每一项表示了计算机系统中一个物理页的使用情况。为了表示物理页可被换出或已被换出的情况可对Page数据结构进行扩展
```
struct Page {
……
list\_entry\_t pra\_page\_link;
uintptr\_t 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。
为了实现各种页替换算法我们设计了一个页替换算法的类框架swap\_manager:
```
struct swap_manager
{
const char *name;
/* Global initialization for the swap manager */
int (*init) (void);
/* Initialize the priv data inside mm_struct */
int (*init_mm) (struct mm_struct *mm);
/* Called when tick interrupt occured */
int (*tick_event) (struct mm_struct *mm);
/* Called when map a swappable page into the mm_struct */
int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
/* When a page is marked as shared, this routine is called to delete the addr entry from the swap manager */
int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
/* Try to swap out a page, return then victim */
int (*swap_out_victim) (struct mm_struct *mm, struct Page *ptr_page, int in_tick);
/* check the page relpacement algorithm */
int (*check\_swap)(void);
};
```
这里关键的两个函数指针是map\_swappable和swap\_out\_vistim前一个函数用于记录页访问情况相关属性后一个函数用于挑选需要换出的页。显然第二个函数依赖与第一个函数记录的页访问情况。tick\_event函数指针也很重要结合定时产生的中断可以实现一种积极的换页策略。
#### 5. swap_check的检查实现
下面具体讲述一下实验三中实现置换算法的页面置换的检查执行逻辑便于大家实现练习2。实验三页面置换的检查过程在函数swap\_checkkern/mm/swap.c中其大致流程如下。
1. 调用mm\_create建立mm变量并调用vma\_create创建vma变量设置合法的访问范围为4KB\~24KB
2. 调用free\_page等操作模拟形成一个只有4个空闲 physical page并设置了从4KB\~24KB的连续5个虚拟页的访问操作
3. 设置记录缺页次数的变量pgfault\_num=0执行check\_content\_set函数使得起始地址分别对起始地址为0x1000, 0x2000, 0x3000, 0x4000的虚拟页按时间顺序先后写操作访问由于之前没有建立页表所以会产生page fault异常如果完成练习1则这些从4KB\~20KB的4虚拟页会与ucore保存的4个物理页帧建立映射关系
4. 然后对虚页对应的新产生的页表项进行合法性检查;
5. 然后进入测试页替换算法的主体执行函数check\_content\_access并进一步调用到\_fifo\_check\_swap函数如果通过了所有的assert。这进一步表示FIFO页替换算法基本正确实现
6. 最后恢复ucore环境。

2
lab3/lab3_5_swapping.md Normal file
View File

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

View File

@ -0,0 +1,86 @@
## 6. 实验报告要求
从网站上下载lab3.zip后解压得到本文档和代码目录 lab3完成实验中的各个练习。完成代码编写并检查无误后在对应目录下执行 make handin 任务,即会自动生成 lab3-handin.tar.gz。最后请一定提前或按时提交到网络学堂上。
注意有“LAB3”的注释代码中所有需要完成的地方challenge除外都有“LAB3”和“YOUR CODE”的注释请在提交时特别注意保持注释并将“YOUR CODE”替换为自己的学号并且将所有标有对应注释的部分填上正确的代码。所有扩展实验的加分总和不超过10分。
附录:正确输出的参考:
```
yuchen@yuchen-PAI4:~/oscourse/2012spring/lab3_figs/lab3-code-2012$ make qemu
(THU.CST) os is loading ...
Special kernel symbols:
entry 0xc010002c (phys)
etext 0xc010962b (phys)
edata 0xc0122ac8 (phys)
end 0xc0123c10 (phys)
Kernel executable memory footprint: 143KB
memory management: default_pmm_manager
e820map:
memory: 0009f400, [00000000, 0009f3ff], type = 1.
memory: 00000c00, [0009f400, 0009ffff], type = 2.
memory: 00010000, [000f0000, 000fffff], type = 2.
memory: 07efd000, [00100000, 07ffcfff], type = 1.
memory: 00003000, [07ffd000, 07ffffff], type = 2.
memory: 00040000, [fffc0000, ffffffff], type = 2.
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
-------------------- BEGIN --------------------
PDE(0e0) c0000000-f8000000 38000000 urw
|-- PTE(38000) c0000000-f8000000 38000000 -rw
PDE(001) fac00000-fb000000 00400000 -rw
|-- PTE(000e0) faf00000-fafe0000 000e0000 urw
|-- PTE(00001) fafeb000-fafec000 00001000 -rw
--------------------- END ---------------------
check_vma_struct() succeeded!
page fault at 0x00000100: K/W [no page found].
check_pgfault() succeeded!
check_vmm() succeeded.
ide 0: 10000(sectors), 'QEMU HARDDISK'.
ide 1: 262144(sectors), 'QEMU HARDDISK'.
SWAP: manager = fifo swap manager
BEGIN check_swap: count 1, total 31992
mm->sm_priv c0123c04 in fifo_init_mm
setup Page Table for vaddr 0X1000, so alloc a page
setup Page Table vaddr 0~4MB OVER!
set up init env for check_swap begin!
page fault at 0x00001000: K/W [no page found].
page fault at 0x00002000: K/W [no page found].
page fault at 0x00003000: K/W [no page found].
page fault at 0x00004000: K/W [no page found].
set up init env for check_swap over!
write Virt Page c in fifo_check_swap
write Virt Page a in fifo_check_swap
write Virt Page d in fifo_check_swap
write Virt Page b in fifo_check_swap
write Virt Page e in fifo_check_swap
page fault at 0x00005000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x1000 to disk swap entry 2
write Virt Page b in fifo_check_swap
write Virt Page a in fifo_check_swap
page fault at 0x00001000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x2000 to disk swap entry 3
swap_in: load disk swap entry 2 with swap_page in vadr 0x1000
write Virt Page b in fifo_check_swap
page fault at 0x00002000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x3000 to disk swap entry 4
swap_in: load disk swap entry 3 with swap_page in vadr 0x2000
write Virt Page c in fifo_check_swap
page fault at 0x00003000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x4000 to disk swap entry 5
swap_in: load disk swap entry 4 with swap_page in vadr 0x3000
write Virt Page d in fifo_check_swap
page fault at 0x00004000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x5000 to disk swap entry 6
swap_in: load disk swap entry 5 with swap_page in vadr 0x4000
check_swap() succeeded!
++ setup timer interrupts
100 ticks
End of Test.
kernel panic at kern/trap/trap.c:20:
EOT: kernel seems ok.
Welcome to the kernel debug monitor!!
Type 'help' for a list of commands.
```

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
lab7/.DS_Store vendored

Binary file not shown.

View File

@ -1,10 +0,0 @@
* [](lab7/lab7_1_goals.md)
* [](lab7/lab7_2_1_exercises.md)
* [](lab7/lab7_2_2_files.md)
* [](lab7/lab7_2_labs.md)
* [](lab7/lab7_3_1_experiment.md)
* [](lab7/lab7_3_2_synchronization_basic_support.md)
* [](lab7/lab7_3_3_semaphore.md)
* [](lab7/lab7_3_4_monitors.md)
* [](lab7/lab7_3_synchronization_implement.md)
* [](lab7/lab7_4_lab_requirement.md)