2.9 linux存储管理-页面的换入

线性地址-->物理地址
若映射已经建立,但相应页表|页目录的P(present)位为0,表示相应的物理页面不再内存,从而无法访问内存。 对于这种情况和未建立映射的情况,CPU的MMU硬件不对其进行区分,都进行“页面异常”处理

在handle_page_fault函数中:
说明:
    首先区分的是pte_present,也就是检查表项的P标志位,查看物理页面是否在内存中。
        若不在内存中,则通过pte_none检测表项是否为空(all 0),
            若为空,则说明页面的映射尚未建立,调用do_no_page
            如果非空,则说明映射已经建立,只是物理页面不再内存中,调用do_swap_page,将该页面换入内存
代码:
 
    
  1. static inline int handle_pte_fault(struct mm_struct *mm,
  2. struct vm_area_struct * vma, unsigned long address,
  3. int write_access, pte_t * pte)
  4. {
  5. pte_t entry;
  6. /*
  7. * We need the page table lock to synchronize with kswapd
  8. * and the SMP-safe atomic PTE updates.
  9. */
  10. spin_lock(&mm->page_table_lock);
  11. entry = *pte;
  12. if (!pte_present(entry)) {
  13. /*
  14. * If it truly wasn't present, we know that kswapd
  15. * and the PTE updates will not touch it later. So
  16. * drop the lock.
  17. */
  18.     spin_unlock(&mm->page_table_lock);
  19.    if (pte_none(entry))
  20. return do_no_page(mm, vma, address, write_access, pte);
  21. return do_swap_page(mm, vma, address, pte, pte_to_swp_entry(entry), write_access);
  22. }

do_swap_page函数代码:
说明:
        address为映射失败的线性地址
        page_table为映射失败的页面表项,entry为表项内容
        (  当页面在物理内存中时,页面表项为pte_t结构
            当page不再物理内存时,页面表项为swap_entry_t结构)
            write_access表示映射失败时所进行的访问种类(read/write)
            lookup_swap_cache:先检查页面是否在换进/换出队列中(尚未换出到磁盘)
            swapin_readahead:从磁盘上预读多个页面到 换进/换出队列 和 活跃页面队列(效率问题)
            预读可能由于无法分配到足够页面而失败(重读,并只读entry页面)
            read_swap_cache:从活跃页面队列读取entry
代码:     
 
    
  1. static int do_swap_page(struct mm_struct * mm,
  2. struct vm_area_struct * vma, unsigned long address,
  3. pte_t * page_table, swp_entry_t entry, int write_access)
  4. {
  5. struct page *page = lookup_swap_cache(entry);
  6. pte_t pte;
  7. if (!page) {
  8. lock_kernel();
  9. swapin_readahead(entry);
  10. page = read_swap_cache(entry);
  11. unlock_kernel();
  12. if (!page)
  13. return -1;
  14. flush_page_to_ram(page);
  15. flush_icache_page(vma, page);
  16. }
  17. mm->rss++;
  18. pte = mk_pte(page, vma->vm_page_prot);
  19. /*
  20. * Freeze the "shared"ness of the page, ie page_count + swap_count.
  21. * Must lock page before transferring our swap count to already
  22. * obtained page count.
  23. */
  24. lock_page(page);
  25. swap_free(entry);
  26. if (write_access && !is_page_shared(page))
  27. pte = pte_mkwrite(pte_mkdirty(pte));
  28. UnlockPage(page);
  29. set_pte(page_table, pte);
  30. /* No need to invalidate - it was non-present before */
  31. update_mmu_cache(vma, address, pte);
  32. return 1; /* Minor fault */
  33. }

read_swap_cache函数:
说明:
        read_swap_cache函数只是调用了read_swap_cache_async(),并把参数wait设置为1
        lookup_swap_cache执行了两次
            第一次:因为swapin_readahead已经把目标读取进来,需要在swaper_space队列中寻找一次
            第二次:lookup_swap_cache并未找到页面,使用__get_free_page申请页面,
                  但是申请过程中可能会被阻塞(可能有其他进程将entry换入内存),需要再次
                  在swaper_space和avtive_list中查找一次,若找到说明被其他进程换入,
                  跳转到out_free_page,释放__get_free_page申请的页面,
                  并使用swap_free减少entry的共享计数(swap_duplicate(entry)增加了共享计数)
代码:      
 
    
  1. struct page * read_swap_cache_async(swp_entry_t entry, int wait)
  2. {
  3. struct page *found_page = 0, *new_page;
  4. unsigned long new_page_addr;
  5. /*
  6. *Make sure the swap entry is still in use.
  7. */
  8. if(!swap_duplicate(entry)) /* Account for the swap cache */
  9. goto out;
  10. /*
  11. *Look for the page in the swap cache.
  12. */
  13. found_page = lookup_swap_cache(entry);
  14. if (found_page)
  15. goto out_free_swap;
  16. new_page_addr = __get_free_page(GFP_USER);
  17. if (!new_page_addr)
  18. goto out_free_swap;
  19. /*Out of memory */
  20. new_page= virt_to_page(new_page_addr);
  21. /*
  22. *Check the swap cache again, in case we stalled above.
  23. */
  24. found_page = lookup_swap_cache(entry);
  25. if(found_page)
  26. goto out_free_page;
  27. /*
  28. *Add it to the swap cache and read its contents.
  29. */
  30. lock_page(new_page);
  31. add_to_swap_cache(new_page, entry);
  32. rw_swap_page(READ, new_page, wait);
  33. return new_page;
  34.  
  35. out_free_page:
  36. page_cache_release(new_page);
  37. out_free_swap:
  38. swap_free(entry);
  39. out:
  40. return found_page;
  41. }

你可能感兴趣的:(Linux内核情景分析)