1. 程式人生 > >2.9 linux儲存管理-頁面的換入

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. }