// kern/mm/vmm.h
//pre define
struct mm_struct;
// 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
uint_t vm_flags; // flags of vma
list_entry_t list_link; // linear list link which sorted by start addr of vma
};
#define le2vma(le, member) \
to_struct((le), struct vma_struct, member)
#define VM_READ 0x00000001
#define VM_WRITE 0x00000002
#define VM_EXEC 0x00000004
// the control struct for a set of vma using the same Page Table
struct mm_struct {
list_entry_t mmap_list; // linear list link which sorted by start addr of vma
struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
pde_t *pgdir; // the Page Table of these vma
int map_count; // the count of these vma
void *sm_priv; // the private data for swap manager
};
// kern/mm/vmm.c
int do_pgfault(struct mm_struct *mm, uint_t error_code, uintptr_t addr) {
//addr: 访问出错的虚拟地址
int ret = -E_INVAL;
//try to find a vma which include addr
struct vma_struct *vma = find_vma(mm, addr);
//我们首先要做的就是在mm_struct里判断这个虚拟地址是否可用
pgfault_num++;
//If the addr is not in the range of a mm's vma?
if (vma == NULL || vma->vm_start > addr) {
cprintf("not valid addr %x, and can not find it in vma\n", addr);
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_R | PTE_W);
}
addr = ROUNDDOWN(addr, PGSIZE); //按照页面大小把地址对齐
ret = -E_NO_MEM;
pte_t *ptep=NULL;
ptep = get_pte(mm->pgdir, addr, 1); //(1) try to find a pte, if pte's
//PT(Page Table) isn't existed, then
//create a PT.
if (*ptep == 0) {
if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
cprintf("pgdir_alloc_page in do_pgfault failed\n");
goto failed;
}
} else {
/*
* Now we think this pte is a swap entry, we should load data from disk
* to a page with phy addr,
* and map the phy addr with logical addr, trigger swap manager to record
* the access situation of this page.
*
* swap_in(mm, addr, &page) : alloc a memory page, then according to
* the swap entry in PTE for addr, find the addr of disk page, read the
* content of disk page into this memroy page
* page_insert : build the map of phy addr of an Page with the virtual addr la
* swap_map_swappable : set the page swappable
*/
if (swap_init_ok) {
struct Page *page = NULL;
//在swap_in()函数执行完之后,page保存换入的物理页面。
//swap_in()函数里面可能把内存里原有的页面换出去
swap_in(mm, addr, &page); //(1)According to the mm AND addr, try
//to load the content of right disk page
//into the memory which page managed.
page_insert(mm->pgdir, page, addr, perm); //更新页表,插入新的页表项
//(2) According to the mm, addr AND page,
// setup the map of phy addr <---> virtual addr
swap_map_swappable(mm, addr, page, 1); //(3) make the page swappable.
//标记这个页面将来是可以再换出的
page->pra_vaddr = addr;
} else {
cprintf("no swap_init_ok but ptep is %x, failed\n", *ptep);
goto failed;
}
}
ret = 0;
failed:
return ret;
}