add_to_swap函數是為匿名頁面配置設定交換空間.
當對一個匿名頁面page進行記憶體回收時,若該匿名頁面page具有如下狀态
1.該匿名頁未被使用者應用通路.(映射該匿名頁的所有pte頁表項的AF位為0)
2.PageAnon(page) && !PageSwapCache(page)為true.(該page未被配置設定swap空間)
若此時頁面回收則允許頁面進行檔案操作或磁盤I/0操作(sc->gfp_mask & __GFP_IO為true).則頁面回收則會調用add_to_swap函數對page配置設定swap交換空間
- 執行步驟
1.通過get_swap_page在swap area中擷取一個slot(頁槽,一個頁槽用于存儲一個匿名頁swap out到swap area中的内 容資料),并将該slot的位置信 息記錄在swp_entry_t結構體entry中 2.通過add_to_swap_cache函數将1步驟配置設定的slot與匿名頁page關聯 a.将匿名頁page的flag成員的swap cache标志位置位 b.用匿名頁page的private成員記錄步驟1擷取的slot在swap area中的位置資訊(page->private = entry.val) c.通過swp_offset(entry)函數擷取步驟1的slot在swap area中的偏移offset d.通過radix_tree_insert函數将匿名頁page插入到對應swap area區域緩存管理器address_space的page_tree 容器中.
- 源碼
//mm/swap_state.c /** * add_to_swap - allocate swap space for a page * @page: page we want to move to swap * * Allocate swap space for the page and add the page to the * swap cache. Caller needs to hold the page lock. */ int add_to_swap(struct page *page, struct list_head *list) { swp_entry_t entry; int err; VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(!PageUptodate(page), page); /* *從swap磁盤區域擷取一個slot(slot代表一個槽,每個槽對應記憶體區域的一個page),并将該槽的位置資訊記錄 *在swp_entry_t結構體entry中(通過entry可以在從一個swap磁盤區域定位到一個具體的slot,該slot用于存儲 *匿名頁page對應的資料). */ entry = get_swap_page(); ...... err = add_to_swap_cache(page, entry, __GFP_HIGH|__GFP_NOMEMALLOC|__GFP_NOWARN); ...... //成功傳回1,失敗傳回0 }
//mm/swap_state.c /* *int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) * |--->__add_to_swap_cache(page, entry) */ /* * __add_to_swap_cache resembles add_to_page_cache_locked on swapper_space, * but sets SwapCache flag and private instead of mapping and index. */ int __add_to_swap_cache(struct page *page, swp_entry_t entry) { int error; //某個swap 記憶體區域的swap cache管理結構體struct address_space struct address_space *address_space; ..... get_page(page); //将匿名頁page的flag成員的swap cache标志位置位 SetPageSwapCache(page); //匿名頁page的private存儲page對應的swap slot的位置資訊 set_page_private(page, entry.val); //擷取entry所在swap area區域的swap cache管理器,指派給address_space address_space = swap_address_space(entry); //擷取address_space緩存管理器中page_tree的自旋鎖 spin_lock_irq(&address_space->tree_lock); //根據entry指向的slot在對應swap area的偏移,将page插入到緩存管理器的page_tree中 error = radix_tree_insert(&address_space->page_tree, swp_offset(entry), page); if (likely(!error)) { //page插入成功,緩存管理器address_sapce維護的swap cache中匿名緩存頁數量++ address_space->nrpages++; __inc_node_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } spin_unlock_irq(&address_space->tree_lock); if (unlikely(error)) { /* * Only the context which have set SWAP_HAS_CACHE flag * would call add_to_swap_cache(). * So add_to_swap_cache() doesn't returns -EEXIST. */ VM_BUG_ON(error == -EEXIST); set_page_private(page, 0UL); ClearPageSwapCache(page); put_page(page); } return error; }