在做完lab1、lab2的实验后接着就是lab3,lab3主要是虚拟内存管理相关的内容,按照lab3的实验指导书的要求,开始做lab3的练习
实验目的
- 了解虚拟内存的Page Fault异常处理实现
- 了解页替换算法在操作系统中的实现
实验内容
本次实验是在实验二的基础上,借助于页表机制和实验一中涉及的中断异常处理机制,完成Page Fault异常处理和FIFO页替换算法的实现,结合磁盘提供的缓存空间,从而能够支持虚存管理,提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。这个实验与实际操作系统中的实现比较起来要简单,不过需要了解实验一和实验二的具体实现。实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的,涉及到与进程管理系统、文件系统等的交叉访问。如果大家有余力,可以尝试完成扩展练习,实现extended clock页替换算法。
页面置换算法
局部页面置换算法
置换页面的选择范围仅限于当前进程占用的物理页面内
最佳置换算法(OPT)
Optimal replacement,理想情况下的置换算法,发生缺页时,有些页面在内存中,其中一页将很快被访问,而其他页面可能要到10、100或者1000条指令后才会访问,每个页面都可以用在该页面首次被访问前所要执行的指令数进行标记。
从内存中选择今后不再访问的页面或者在最长一段时间后才需要访问的页面进行淘汰。
先进先出置换算法(FIFO)
先进先出(First In First Out, FIFO)页替换算法:该算法总是淘汰最先进入内存的页,即选择在内存中驻留时间最久的页予以淘汰。只需把一个应用程序在执行过程中已调入内存的页按先后次序链接成一个队列,队列头指向内存中驻留时间最久的页,队列尾指向最近被调入内存的页。这样需要淘汰页时,从队列头很容易查找到需要淘汰的页。FIFO算法只是在应用程序按线性顺序访问地址空间时效果才好,否则效率不高。因为那些常被访问的页,往往在内存中也停留得最久,结果它们因变“老”而不得不被置换出去。FIFO算法的另一个缺点是,它有一种异常现象(Belady现象),即在增加放置页的页帧的情况下,反而使页访问异常次数增多。
最近最少使用(LRU)
Least Recently Used,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
时钟置换算法
时钟(Clock)页替换算法:是LRU算法的一种近似实现。时钟页替换算法把各个页面组织成环形链表的形式,类似于一个钟的表面。然后把一个指针(简称当前指针)指向最老的那个页面,即最先进来的那个页面。另外,时钟算法需要在页表项(PTE)中设置了一位访问位来表示此页表项对应的页当前是否被访问过。当该页被访问时,CPU中的MMU硬件将把访问位置“1”。当操作系统需要淘汰页时,对当前指针指向的页所对应的页表项进行查询,如果访问位为“0”,则淘汰该页,如果该页被写过,则还要把它换出到硬盘上;如果访问位为“1”,则将该页表项的此位置“0”,继续访问下一个页。该算法近似地体现了LRU的思想,且易于实现,开销少,需要硬件支持来设置访问位。时钟页替换算法在本质上与FIFO算法是类似的,不同之处是在时钟页替换算法中跳过了访问位为1的页。
改进的时钟页面置换算法(Clock)
改进的时钟(Enhanced Clock)页替换算法:在时钟置换算法中,淘汰一个页面时只考虑了页面是否被访问过,但在实际情况中,还应考虑被淘汰的页面是否被修改过。因为淘汰修改过的页面还需要写回硬盘,使得其置换代价大于未修改过的页面,所以优先淘汰没有修改的页,减少磁盘操作次数。改进的时钟置换算法除了考虑页面的访问情况,还需考虑页面的修改情况。即该算法不但希望淘汰的页面是最近未使用的页,而且还希望被淘汰的页是在主存驻留期间其页面内容未被修改过的。这需要为每一页的对应页表项内容中增加一位引用位和一位修改位。当该页被访问时,CPU中的MMU硬件将把访问位置“1”。当该页被“写”时,CPU中的MMU硬件将把修改位置“1”。这样这两位就存在四种可能的组合情况:(0,0)表示最近未被引用也未被修改,首先选择此页淘汰;(0,1)最近未被使用,但被修改,其次选择;(1,0)最近使用而未修改,再次选择;(1,1)最近使用且修改,最后选择。该算法与时钟算法相比,可进一步减少磁盘的I/O操作次数,但为了查找到一个尽可能适合淘汰的页面,可能需要经过多次扫描,增加了算法本身的执行开销。
全局置换算法
工作集置换算法
缺页率置换算法(PPF)
练习
练习0:填写已有实验
这个简单,直接合代码即可。
练习1:给未被映射的地址映射上物理页(需要编程)
参考实验文档及映射关系图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwAQGNyR-1657703867129)(https://raw.githubusercontent.com/mingshashan/note-images/doc/image-20220406154748899.png)]
需要关注两个结构体:mm_struct
、vma_struct
。
mm_struct
// the control struct for a set of vma using the same PDT
struct mm_struct {
list_entry_t mmap_list; // vma按照start addr排序的链表 linear list link which sorted by start addr of vma
struct vma_struct *mmap_cache; // 当前的vma,主要用来提升效率 current accessed vma, used for speed purpose
pde_t *pgdir; // 虚拟页对应的PDT the PDT of these vma
int map_count; // 虚拟页的数量 the count of these vma
void *sm_priv; // 用于指向swap manager的某个链表,在FIFO算法中,该双向链表用于将可交换的已分配物理页串起来 the private data for swap manager
};
// the virtual continuous memory area(vma), [vm_start, vm_end),
// addr belong to a vma means vma.vm_start<= addr <vma.vm_end
struct vma_struct {
struct mm_struct *vm_mm; // the set of vma using the same PDT
uintptr_t vm_start; // start addr of vma
uintptr_t vm_end; // end addr of vma, not include the vm_end itself
uint32_t vm_flags; // flags of vma
list_entry_t list_link; // linear list link which sorted by start addr of vma
};
在trapc.中有对mm_struct的声明:
// 管理虚拟内存页面
extern struct mm_struct *check_mm_struct;
kern_init->vmm_init->check_vmm->
swap_manager与之前的pmm_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);
};
给未被映射的地址映射上物理页
完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页。设置访问权限 的时候需要参考页面所在 VMA 的权限,同时需要注意映射物理页时需要操作内存控制 结构所指定的页表,而不是内核的页表。注意:在
代码如下:
int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
// 页访问异常错误码有32位。
// 位0为1表示对应物理页不存在;
// 位1为1表示写异常(比如写了只读页;
// 位2为1表示访问权限异常(比如用户态程序访问内核空间的数据)
// CR2是页故障线性地址寄存器,保存最后一次出现页故障的全32位线性地址。
// CR2用于发生页异常时报告出错信息。当发生页异常时,处理器把引起页异常的线性地址保存在CR2中。
// 操作系统中对应的中断服务例程可以检查CR2的内容,从而查出线性地址空间中的哪个页引起本次异常。
int ret = -E_INVAL;
// try to find a vma which include addr
// 根据addr从vma中查找对应的vma_struct
cprintf("do_pgfault|-------> mm = [%p], error_code = [%x], addr = [%x], ret = [%x]\n", mm, error_code, addr, ret);
struct vma_struct *vma = find_vma(mm, addr);
pgfault_num++;
//If the addr is in the range of a mm's vma?
cprintf("do_pgfault|-------> mm = [%p], vma = [%p], addr = [%x]\n", mm, vma, addr);
if (vma == NULL || vma->vm_start > addr) {
cprintf("not valid addr %x, and can not find it in vma\n", addr);
goto failed;
}
//check the error_code
// 与 00000011
//
switch (error_code & 3) {
default:
/* error code flag : default is 3 ( W/R=1, P=1): write, present */
case 2: /* error code flag : (W/R=1, P=0): write, not present */
if (!(vma->vm_flags & VM_WRITE)) {
cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
goto failed;
}
break;
case 1: /* error code flag : (W/R=0, P=1): read, present */
cprintf("do_pgfault failed: error code flag = read AND present\n");
goto failed;
case 0: /* error code flag : (W/R=0, P=0): read, not present */
if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
goto failed;
}
}
/* IF (write an existed addr ) OR
* (write an non_existed addr && addr is writable) OR
* (read an non_existed addr && addr is readable)
* THEN
* continue process
*/
uint32_t perm = PTE_U;
if (vma->vm_flags & VM_WRITE) {
perm |= PTE_W;
}
addr = ROUNDDOWN(addr, PGSIZE);
ret = -E_NO_MEM;
pte_t *ptep=NULL;
// 根据get_pte来获取pte如果不存在,则分配一个新的
ptep = get_pte(mm->pgdir, addr, 1);
if (ptep == NULL) {
cprintf("get_pte in do_pgfault failed\n");
goto failed;
}
struct Page *p;
// 如果对应的物理页不存在,分配一个新的页,且把物理地址和逻辑地址映射
if (*ptep == 0) {
p = pgdir_alloc_page(mm->pgdir, addr, perm);
if (p == NULL) {
cprintf("alloc_page in do_pgfault failed\n");
goto failed;
}
} else {
// 如果不全为0,则可能被交换到了swap磁盘中
if(swap_init_ok) {
struct Page *page=NULL;
int swapIn;
// 从磁盘中换出
swapIn = swap_in(mm, addr, &page);
if (swapIn != 0) {
cprintf("swap_in in do_pgfault failed\n");
goto failed;
}
// build the map of phy addr of an Page with the linear addr la
page_insert(mm->pgdir, page, addr, perm);
// if (page_insert(mm->pgdir, page, addr, perm) != 0) {
// cprintf("page_insert in do_pgfault failed\n");
// goto failed;
// }
swap_map_swappable(mm, addr, page, 1);
page->pra_vaddr = addr;
} else {
cprintf("no swap_init_ok, but ptep is %x, failed\n", *ptep);
goto failed;
}
}
ret = 0;
failed:
cprintf("ret = [%d]\n", ret);
return ret;
}
实现过程:
1、根据addr查找对应的vma,然后pgfault_num(全局变量pgfault自增加1)
2、校验error_code,设置perm权限(如果可写,perm |= PTE_W)
3、addr以PAGE_SIZE向下取整
4、调用get_pte,来获取PTE(如果没有,则分配一个新的)
5、如果获取的pte为0(对应的物理页不存在),分配新的页(调用pgdir_alloc_page),且把物理地下和逻辑地址映射
6、如果获取的pte不全为0,则可能当前页被交换到了swap磁盘中,则从磁盘中换出
7、页的物理地址与线性地址映射
8、调用swap_map_swappable,把当前页加入队列(链表)尾部
-
请描述页目录项(Page Directory Entry)和页表项(Page Table Entry)中组成部分对ucore实现页替换算法的潜在用处。(lab2学习过PDE与PTE的结构区别)
从结构上可以看出来,页目录项(Page Directory Entry)和页表项(Page Table Entry)差不多,只是12-31位代表的含义不相同。
-
P 存在位(present)
-
R/W 读/写位(Read/Write)
-
U/S 访问该页需要的特权级
-
PWT write through(为1表示此项采用通写方式,表示该页不仅是普通内存,还是高速缓存)
-
PCD 若为1表示该页启用高速缓存,为0表示禁止将该页缓存
-
A 访问位,若为1表示该页被 CPU 访问过啦,所以该位是由 CPU 设置的。
-
D 脏页位,当 CPU 个页面执行写操作时,就会设置对应页表项的D位为 1,此项仅针对页表项有效,并不会修改页目录项中的位。
-
PAT 页属性位,在页一级的粒度上设置内存属性。
-
G 全局位,与TLB有关,为1表示该页是全局页,该页在高速缓存TLB中一直保存。
-
Avail 可用位,为1表示用户进程可用该页,为0则不可用。对操作系统无效。
LAB3中,如果PTE都为0,则需要分配页,而如果高20位不为零,则代表页被换到swap磁盘中。
-
-
如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
- 将发生page fault的线性地址(linear address)保存到CR2寄存器中
- 压栈EFLAGS,CS,EIP,ErrorCode、trapno等到栈(内核栈)中
- 保存上下文
- 执行新的缺页中断程序(pgfault_handler)处理,分配页(可能发生页置换)
- 恢复上下文
- 继续执行上一级的缺页服务例程
练习2:补充完成基于FIFO的页面替换算法(需要编程)
完成vmm.c中的do_pgfault函数,并且在实现FIFO算法的swap_fifo.c中完成map_swappable和swap_out_victim函数。练习2相对比较简单
/*
* (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
*/
static int
_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
list_entry_t *entry=&(page->pra_page_link);
assert(entry != NULL && head != NULL);
//record the page access situlation
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1)link the most recent arrival page at the back of the pra_list_head qeueue.
list_add(head, entry);
return 0;
}
/*
* (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
* then set the addr of addr of this page to ptr_page.
*/
static int
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
assert(head != NULL);
assert(in_tick==0);
/* Select the victim */
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1) unlink the earliest arrival page in front of pra_list_head qeueue
//(2) set the addr of addr of this page to ptr_page
list_entry_t *le = head->prev;
assert(le != NULL);
assert(le != head);
list_del(le);
struct Page *p = le2page(le, pra_page_link);
assert(p != NULL);
*ptr_page = p;
return 0;
}
请在实验报告中简要说明你的设计实现过程。
_fifo_map_swappable即把page加到链表尾。
_fifo_swap_out_victim即把要置换的页(在链表头)找出给ptr_page,删除在链表中对应的元素
请在实验报告中回答如下问题:
-
如果要在ucore上实现"extended clock页替换算法"请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法?如果是,请给你的设计方案。如果不是,请给出你的新的扩展和基此扩展的设计方案。并需要回答如下问题
现有框架可以支持
-
需要被换出的页的特征是什么?
PTE_P(Present)和PTE_D(Dirty)都为0
-
在ucore中如何判断具有这样特征的页?
获取线性地址对应的页表项,再使用位运算判断PTE_P(Present)和PTE_D(Dirty),(addr & PTE_P && addr & PTE_D)
-
何时进行换入和换出操作?
缺页时换入(换入到swap磁盘)
物理页帧满时换出(换出到swap磁盘),不过要注意dirtybit的处理。可以在修改dirtybit的时候写入外存,或者在最终删除该物理页时再写入外存。或者有利于多个写操作的合并,降低缺页代价,但此时的页替换算法退化成普通的clock算法,而不是extended clock算法。
-
扩展练习 Challenge 1:实现识别dirty bit的 extended clock页替换算法(需要编程)
参考:ucoreOS_lab3 实验报告与操作系统-uCore-Lab-3
在FIFO
的基础上,实现swap_out_victim
函数即可。
该函数中查找一块可用于换出的物理页,最多只需要遍历三次:
- 查找!PTE_A&!PTE_D(<0, 0>)同时重置当前的PTE_A,为第二次遍历准备
- 查找!PTE_A&!PTE_D(<0, 0>)同时重置当前的PTE_D,为第三次遍历准备
- 查找!PTE_A&!PTE_D(<0, 0>),肯定能找到(因为之前分别重置了PTE_A与PTE_D)
即:
- 首先,查找标记为
<0,0>
的页面; - 如果上一步没有找到,查找标记
<0,1>
,并将访问过的页面的访问位清零; - 如果上一步没有找到,再次查找标记为
<0,0>
的页面; - 如果上一步没有找到,再次查找标记为
<0,1>
的页面;
这里需要注意对于PTE_D
的操作,若第一次、第二次遍历都找不到符合要求的物理页,则必须对PTE_D
下手,重置该标志位。还有一点需要注意,在每次修改PTE标志位后,都需要重置TLB缓存。
代码如下:
swap_extended_clock.h
#ifndef __KERN_MM_SWAP_EXTEND_CLOCK_H__
#define __KERN_MM_SWAP_EXTEND_CLOCK_H__
#include <swap.h>
extern struct swap_manager swap_manager_extend_clock;
#endif
swap_extended_clock.c
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <swap.h>
#include <swap_extended_clock.h>
#include <list.h>
static pte_t *_get_list_entry_pte(pte_t *pgdir, list_entry_t *le) {
struct Page *p = le2page(le, pra_page_link);
uintptr_t pra_vaddr = p->pra_vaddr;
pte_t *ptep = get_pte(pgdir, pra_vaddr, 0);
return ptep;
}
static int _get_dirty_flag(pte_t *pgdir, list_entry_t *le) {
pte_t *ptep = _get_list_entry_pte(pgdir, le);
return (*ptep) & PTE_D;
}
static int _get_accessed_flag(pte_t *pgdir, list_entry_t *le) {
pte_t *ptep = _get_list_entry_pte(pgdir, le);
return (*ptep) & PTE_A;
}
static _clear_accessed_flag(pte_t *pgdir, list_entry_t *le) {
do {
struct Page *p = le2page(le, pra_page_link);
pte_t *ptep = get_pte(pgdir, p->pra_vaddr, 0);
*ptep = *ptep & ~PTE_A;
tlb_invalidate(pgdir, p->pra_vaddr);
} while (0);
}
static int _extended_clock_init_mm(struct mm_struct *mm)
{
mm->sm_priv = NULL;
return 0;
}
/*
* (3)_extended_clock_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
*/
static int
_extended_clock_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
list_entry_t *head = (list_entry_t *)mm->sm_priv;
list_entry_t *entry = &(page->pra_page_link);
assert(entry != NULL);
if (head == NULL) {
list_init(entry);
mm->sm_priv = entry;
} else {
list_add_before(head, entry);
}
return 0;
}
/*
* (4)_extended_clock_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
* then set the addr of addr of this page to ptr_page.
*/
static int
_extended_clock_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{
list_entry_t *head = (list_entry_t *)mm->sm_priv;
assert(head != NULL);
assert(in_tick == 0);
list_entry_t *selected = NULL, *p = head;
// search <0, 0>
do {
if (_get_accessed_flag(mm->pgdir, p) == 0 && _get_dirty_flag(mm->pgdir, p) == 0) {
selected = p;
break;
}
p = list_next(p);
}while (p != head);
// search <0, 1> and set 'accessed' to 0
if (selected == NULL) {
if (_get_accessed_flag(mm->pgdir, p) == 0 && _get_dirty_flag(mm->pgdir, p)) {
selected = p;
break;
}
_clear_accessed_flag(mm->pgdir, p);
p = list_next(p);
}
// search <0, 0> again
do {
if (_get_accessed_flag(mm->pgdir, p) == 0 && _get_dirty_flag(mm->pgdir, p) == 0) {
selected = p;
break;
}
p = list_next(p);
}while (p != head);
// search <0, 1> and set 'accessed' to 0 again
if (selected == NULL) {
if (_get_accessed_flag(mm->pgdir, p) == 0 && _get_dirty_flag(mm->pgdir, p)) {
selected = p;
break;
}
_clear_accessed_flag(mm->pgdir, p);
p = list_next(p);
}
head = selected;
if (list_empty(head)) {
mm->sm_priv = NULL;
} else {
mm->sm_priv = list_next(head);
list_del(head);
}
*ptr_page = le2page(head, pra_page_link);
return 0;
}
static int
_extended_clock_check_swap(void) {
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==4);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==4);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==4);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==5);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==5);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==6);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==6);
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==7);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==8);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==9);
cprintf("write Virt Page a in fifo_check_swap\n");
assert(*(unsigned char *)0x1000 == 0x0a);
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==9);
cprintf("read Virt Page b in fifo_check_swap\n");
assert(*(unsigned char *)0x2000 == 0x0b);
assert(pgfault_num==10);
cprintf("read Virt Page c in fifo_check_swap\n");
assert(*(unsigned char *)0x3000 == 0x0c);
assert(pgfault_num==11);
cprintf("read Virt Page a in fifo_check_swap\n");
assert(*(unsigned char *)0x1000 == 0x0a);
assert(pgfault_num==12);
cprintf("read Virt Page d in fifo_check_swap\n");
assert(*(unsigned char *)0x4000 == 0x0d);
assert(pgfault_num==13);
cprintf("read Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(*(unsigned char *)0x3000 == 0x0c);
assert(*(unsigned char *)0x4000 == 0x0d);
assert(*(unsigned char *)0x5000 == 0x0e);
assert(*(unsigned char *)0x2000 == 0x0b);
assert(pgfault_num==14);
return 0;
}
static int
_extended_clock_init(void)
{
return 0;
}
static int
_extended_clock_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return 0;
}
static int
_extended_clock_tick_event(struct mm_struct *mm)
{ return 0; }
struct swap_manager swap_manager_extended_clock =
{
.name = "extended clock swap manager",
.init = &_extended_clock_init,
.init_mm = &_extended_clock_init_mm,
.tick_event = &_extended_clock_tick_event,
.map_swappable = &_extended_clock_map_swappable,
.set_unswappable = &_extended_clock_set_unswappable,
.swap_out_victim = &_extended_clock_swap_out_victim,
.check_swap = &_extended_clock_check_swap,
};
扩展练习 Challenge 2:实现不考虑实现开销和效率的LRU页替换算法(需要编程)
challenge部分不是必做部分,不过在正确最后会酌情加分。需写出有详细的设计、分析和测试的实验报告。完成出色的可获得适当加分。
这个后面再补上
参考: