FIFO置换算法
所谓FIFO(First in, First out)页面置换算法,相当简单,就是把所有页面排在一个队列里,每次换入页面的时候,把队列里最靠前(最早被换入)的页面置换出去。
换出页面的时机相对复杂一些,针对不同的策略有不同的时机。ucore 目前大致有两种策略,即积极换出策略和消极换出策略。积极换出策略是指操作系统周期性地(或在系统不忙的时候)主动把某些认为“不常用”的页换出到硬盘上,从而确保系统中总有一定数量的空闲页存在,这样当需要空闲页时,基本上能够及时满足需求;消极换出策略是指,只是当试图得到空闲页时,发现当前没有空闲的物理页可供分配,这时才开始查找“不常用”页面,并把一个或多个这样的页换出到硬盘上。
目前的框架支持第二种情况,在alloc_pages()
里面,没有空闲的物理页时,尝试换出页面到硬盘上。
// kern/mm/pmm.c
// alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
struct Page *alloc_pages(size_t n) {
struct Page *page = NULL;
bool intr_flag;
while (1) {
local_intr_save(intr_flag);
{ page = pmm_manager->alloc_pages(n); }
local_intr_restore(intr_flag);
//如果有足够的物理页面,就不必换出其他页面
//如果n>1, 说明希望分配多个连续的页面,但是我们换出页面的时候并不能换出连续的页面
//swap_init_ok标志是否成功初始化了
if (page != NULL || n > 1 || swap_init_ok == 0) break;
extern struct mm_struct *check_mm_struct;
swap_out(check_mm_struct, n, 0);//调用页面置换的”换出页面“接口。这里必有n=1
}
return page;
}
类似pmm_manager
, 我们定义swap_manager
, 组合页面置换需要的一些函数接口。
// kern/mm/swap.h
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);
};
我们来看swap_in()
, swap_out()
如何换入/换出一个页面.注意我们对物理页面的 Page
结构体做了一些改动。
// kern/mm/memlayout.h
struct Page {
int ref; // page frame's reference counter
uint_t flags; // array of flags that describe the status of the page frame
unsigned int property; // the num of free block, used in first fit pm manager
list_entry_t page_link; // free list link
list_entry_t pra_page_link; // used for pra (page replace algorithm)
uintptr_t pra_vaddr; // used for pra (page replace algorithm)
};
// kern/mm/swap.c
int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
{
struct Page *result = alloc_page();//这里alloc_page()内部可能调用swap_out()
//找到对应的一个物理页面
assert(result!=NULL);
pte_t *ptep = get_pte(mm->pgdir, addr, 0);//找到/构建对应的页表项
//将物理地址映射到虚拟地址是在swap_in()退出之后,调用page_insert()完成的
int r;
if ((r = swapfs_read((*ptep), result)) != 0)//将数据从硬盘读到内存
{
assert(r!=0);
}
cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
*ptr_result=result;
return 0;
}
int swap_out(struct mm_struct *mm, int n, int in_tick)
{
int i;
for (i = 0; i != n; ++ i)
{
uintptr_t v;
struct Page *page;
int r = sm->swap_out_victim(mm, &page, in_tick);//调用页面置换算法的接口
//r=0表示成功找到了可以换出去的页面
//要换出去的物理页面存在page里
if (r != 0) {
cprintf("i %d, swap_out: call swap_out_victim failed\n",i);
break;
}
cprintf("SWAP: choose victim page 0x%08x\n", page);
v=page->pra_vaddr;//可以获取物理页面对应的虚拟地址
pte_t *ptep = get_pte(mm->pgdir, v, 0);
assert((*ptep & PTE_V) != 0);
if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) {
//尝试把要换出的物理页面写到硬盘上的交换区,返回值不为0说明失败了
cprintf("SWAP: failed to save\n");
sm->map_swappable(mm, v, page, 0);
continue;
}
else {
//成功换出
cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1);
*ptep = (page->pra_vaddr/PGSIZE+1)<<8;
free_page(page);
}
//由于页表改变了,需要刷新TLB
//思考: swap_in()的时候插入新的页表项之后在哪里刷新了TLB?
tlb_invalidate(mm->pgdir, v);
}
return i;
}
kern/mm/swap.c
里其他的接口基本都是简单调用swap_manager
的具体实现。值得一提的是swap_init()
初始化里做的工作。
// kern/mm/swap.c
static struct swap_manager *sm;
int swap_init(void)
{
swapfs_init();
// Since the IDE is faked, it can only store 7 pages at most to pass the test
if (!(7 <= max_swap_offset &&
max_swap_offset < MAX_SWAP_OFFSET_LIMIT)) {
panic("bad max_swap_offset %08x.\n", max_swap_offset);
}
sm = &swap_manager_fifo;//use first in first out Page Replacement Algorithm
int r = sm->init();
if (r == 0)
{
swap_init_ok = 1;
cprintf("SWAP: manager = %s\n", sm->name);
check_swap();
}
return r;
}
int swap_init_mm(struct mm_struct *mm)
{
return sm->init_mm(mm);
}
int swap_tick_event(struct mm_struct *mm)
{
return sm->tick_event(mm);
}
int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
return sm->map_swappable(mm, addr, page, swap_in);
}
int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return sm->set_unswappable(mm, addr);
}
kern/mm/swap_fifo.h
完成了FIFO置换算法最终的具体实现。我们所做的就是维护了一个队列(用链表实现)。
// kern/mm/swap_fifo.h
#ifndef __KERN_MM_SWAP_FIFO_H__
#define __KERN_MM_SWAP_FIFO_H__
#include <swap.h>
extern struct swap_manager swap_manager_fifo;
#endif
// kern/mm/swap_fifo.c
/* Details of FIFO PRA
* (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can
* link these pages into pra_list_head according the time order. At first you should
* be familiar to the struct list in list.h. struct list is a simple doubly linked list
* implementation. You should know howto USE: list_init, list_add(list_add_after),
* list_add_before, list_del, list_next, list_prev. Another tricky method is to transform
* a general list struct to a special struct (such as struct page). You can find some MACRO:
* le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.
*/
list_entry_t pra_list_head;
/*
* (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head.
* Now, From the memory control struct mm_struct, we can access FIFO PRA
*/
static int
_fifo_init_mm(struct mm_struct *mm)
{
list_init(&pra_list_head);
mm->sm_priv = &pra_list_head;
return 0;
}
/*
* (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
//(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 */
//(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* entry = list_prev(head);
if (entry != head) {
list_del(entry);
*ptr_page = le2page(entry, pra_page_link);
} else {
*ptr_page = NULL;
}
return 0;
}
static int _fifo_init(void)//初始化的时候什么都不做
{
return 0;
}
static int _fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return 0;
}
static int _fifo_tick_event(struct mm_struct *mm)//时钟中断的时候什么都不做
{ return 0; }
struct swap_manager swap_manager_fifo =
{
.name = "fifo swap manager",
.init = &_fifo_init,
.init_mm = &_fifo_init_mm,
.tick_event = &_fifo_tick_event,
.map_swappable = &_fifo_map_swappable,
.set_unswappable = &_fifo_set_unswappable,
.swap_out_victim = &_fifo_swap_out_victim,
.check_swap = &_fifo_check_swap,
};
我们通过_fifo_check_swap()
, check_swap()
, check_vma_struct()
, check_pgfault()
等接口对页面置换机制进行了简单的测试。具体测试的细节这里地方太小了,写不下。
目前的参考代码可以在这里找到。
最后更新于