2.9 linux儲存管理-頁面的換入
阿新 • • 發佈:2018-11-23
線性地址-->實體地址
若對映已經建立,但相應頁表|頁目錄的P(present)位為0,表示相應的物理頁面不再記憶體,從而無法訪問記憶體。 對於這種情況和未建立對映的情況,CPU的MMU硬體不對其進行區分,都進行“頁面異常”處理
在handle_page_fault函式中: 說明: 首先區分的是pte_present,也就是檢查表項的P標誌位,檢視物理頁面是否在記憶體中。 若不在記憶體中,則通過pte_none檢測表項是否為空(all 0), 若為空,則說明頁面的對映尚未建立,呼叫do_no_page 如果非空,則說明對映已經建立,只是物理頁面不再記憶體中,呼叫do_swap_page,將該頁面換入記憶體 程式碼:
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 程式碼:
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)增加了共享計數) 程式碼:
若對映已經建立,但相應頁表|頁目錄的P(present)位為0,表示相應的物理頁面不再記憶體,從而無法訪問記憶體。 對於這種情況和未建立對映的情況,CPU的MMU硬體不對其進行區分,都進行“頁面異常”處理
在handle_page_fault函式中: 說明: 首先區分的是pte_present,也就是檢查表項的P標誌位,檢視物理頁面是否在記憶體中。 若不在記憶體中,則通過pte_none檢測表項是否為空(all 0), 若為空,則說明頁面的對映尚未建立,呼叫do_no_page 如果非空,則說明對映已經建立,只是物理頁面不再記憶體中,呼叫do_swap_page,將該頁面換入記憶體 程式碼:
static inline int handle_pte_fault(struct mm_struct *mm,
struct vm_area_struct * vma, unsigned long address,
int write_access, pte_t * pte)
{
pte_t entry;
* We need the page table lock to synchronize with kswapd
* and the SMP-safe atomic PTE updates.
*/
spin_lock(&mm->page_table_lock);
entry = *pte;
if (!pte_present(entry)) {
/*
* If it truly wasn't present, we know that kswapd
* and the PTE updates will not touch it later. So
* drop the lock.
*/
spin_unlock(&mm->page_table_lock
if (pte_none(entry))
return do_no_page(mm, vma, address, write_access, pte);
return do_swap_page(mm, vma, address, pte, pte_to_swp_entry(entry), write_access);
}
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 程式碼:
static int do_swap_page(struct mm_struct * mm,
struct vm_area_struct * vma, unsigned long address,
pte_t * page_table, swp_entry_t entry, int write_access)
{
struct page *page = lookup_swap_cache(entry);
pte_t pte;
if (!page) {
lock_kernel();
swapin_readahead(entry);
page = read_swap_cache(entry);
unlock_kernel();
if (!page)
return -1;
flush_page_to_ram(page);
flush_icache_page(vma, page);
}
mm->rss++;
pte = mk_pte(page, vma->vm_page_prot);
/*
* Freeze the "shared"ness of the page, ie page_count + swap_count.
* Must lock page before transferring our swap count to already
* obtained page count.
*/
lock_page(page);
swap_free(entry);
if (write_access && !is_page_shared(page))
pte = pte_mkwrite(pte_mkdirty(pte));
UnlockPage(page);
set_pte(page_table, pte);
/* No need to invalidate - it was non-present before */
update_mmu_cache(vma, address, pte);
return 1; /* Minor fault */
- }
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)增加了共享計數) 程式碼:
struct page * read_swap_cache_async(swp_entry_t entry, int wait)
{
struct page *found_page = 0, *new_page;
unsigned long new_page_addr;
/*
*Make sure the swap entry is still in use.
*/
if(!swap_duplicate(entry)) /* Account for the swap cache */
goto out;
/*
*Look for the page in the swap cache.
*/
found_page = lookup_swap_cache(entry);
if (found_page)
goto out_free_swap;
new_page_addr = __get_free_page(GFP_USER);
if (!new_page_addr)
goto out_free_swap;
/*Out of memory */
new_page= virt_to_page(new_page_addr);
/*
*Check the swap cache again, in case we stalled above.
*/
found_page = lookup_swap_cache(entry);
if(found_page)
goto out_free_page;
/*
*Add it to the swap cache and read its contents.
*/
lock_page(new_page);
add_to_swap_cache(new_page, entry);
rw_swap_page(READ, new_page, wait);
return new_page;
- out_free_page:
page_cache_release(new_page);
out_free_swap:
swap_free(entry);
out:
return found_page;
}