lab3_练习1/2

当缺页中断发生时,调用alloc_page申请物理页时需要检查是否还存在空闲物理页(在default_alloc_pages中),当不存在时,调用swap_out,将fifo队首的页换出内存,将页写入磁盘,在pte中记录磁盘索引。

int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
    int ret = -E_INVAL;
    //try to find a vma which include addr
    struct vma_struct *vma = find_vma(mm, addr);

    pgfault_num++;
    //If the addr is 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;
    }
    //check the error_code
    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");
    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;
        }
    }

    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;
    pde_t *pgdir = mm->pgdir;

    ptep = get_pte(pgdir, addr, 1);              //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
    if (ptep == NULL)
    {
        cprintf("get pte in do_faultpage failed\n");
        goto failed;
    }

//缺页中断发生 访问逻辑页不在内存
    if (*ptep == 0) {

//当访问到没有物理页与之对应的逻辑页
//可能在程序中未使用
//调用pagdir_alloc_page分配一个物理页,并建立映射,可能需要进行换出操作
        struct Page *page = pgdir_alloc_page(pgdir, addr, perm);

        if (page == NULL)
        {
            cprintf("alloc a page failed\n");
            goto failed;
        }
    else {

//逻辑页不再内存,pte中存储了在外存交换区的索引 swap_entry_t
 
        if(swap_init_ok) {
            struct Page *page=NULL;
//将外存的页读入,分配物理页,可能需要换出操作
            swap_in(mm, addr, &page);
            //建立隐射
            if (page_insert(pgdir, page, addr, perm) != 0)
            {
                ret = -E_NO_MEM;
                goto failed;
            }
            //插入fifo队列
            swap_map_swappable(check_mm_struct, addr, page, 0);
            page->pra_vaddr = addr;
        }
        else {
            cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
            goto failed;
        }
   }
   ret = 0;
failed:
    return ret;
}


int
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
{
    //call graph: -> alloc(page) -> alloc_pages(1)
    //分配物理页,可能需要换出操作
     struct Page *result = alloc_page();
     assert(result!=NULL);

     pte_t *ptep = get_pte(mm->pgdir, addr, 0);
     // cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
    

//磁盘io,根据pte中的所有读对应页
     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
page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) {
    pte_t *ptep = get_pte(pgdir, la, 1);
    if (ptep == NULL) {
        return -E_NO_MEM;
    }
    page_ref_inc(page);
    if (*ptep & PTE_P) {
        struct Page *p = pte2page(*ptep);
        if (p == page) {
            //如果是已建立映射 不用增加引用
            page_ref_dec(page);
        }
        else {
        //逻辑页上次分配的物理页 更换物理页
            page_remove_pte(pgdir, la, ptep);
        }
    }
    //pte中记录物理页对应的物理地址 标记未persent 设置访问权限
    *ptep = page2pa(page) | PTE_P | perm;
    tlb_invalidate(pgdir, la);
    return 0;
}


struct Page *
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
    //分配物理页,可能需要换出操作
    struct Page *page = alloc_page();
    if (page != NULL) {
        //建立映射
        if (page_insert(pgdir, page, la, perm) != 0) {
            free_page(page);
            return NULL;
        }
        if (swap_init_ok){
            //插入fifo队列
            swap_map_swappable(check_mm_struct, la, page, 0);
            page->pra_vaddr=la;
            assert(page_ref(page) == 1);
            //cprintf("get No. %d  page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
        }

    }

    return page;
}
struct Page *
alloc_pages(size_t n) {
    struct Page *page=NULL;
    bool intr_flag;
    
    while (1)
    {
         local_intr_save(intr_flag);
         {
              //调用default_alloc_pages申请连续的n页物理空间
              page = pmm_manager->alloc_pages(n);
         }
         local_intr_restore(intr_flag);
         //如果成功申请退出循环
         //不成功且申请多页(不肯发生在缺页中断中)退出循环
         //缺页中断只申请1页
         if (page != NULL || n > 1 || swap_init_ok == 0) break;
         //缺页中断发生,且没有空闲物理页则进行换出操作
         extern struct mm_struct *check_mm_struct;
         //cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
         swap_out(check_mm_struct, n, 0);
    }
    //cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
    return page;
}



 

你可能感兴趣的:(ucore)