Linux記憶體管理之反向對映RMAP
1. Linux反向對映有三個常用資料結構,可以簡稱AV, VMA,AVC
struct anon_vma {
struct anon_vma *root;//指向紅黑樹最頂層AV,可以理解為祖宗程序的AV
unsigned degree;//樹深度,表示是第幾代程序,也可以理解為在紅黑樹的層級
struct anon_vma *parent; //指向父程序AV
struct rb_root rb_root;//所有子孫程序的AVC紅黑樹,有多少個子孫程序,就有多少個AVC通過rb插入到此紅黑樹
};
struct anon_vma_chain {
struct vm_area_struct *vma;//指向same_vam鏈入的VMA,其實也就是當前程序的VMA
struct anon_vma *anon_vma;//指向rb節點插入的AV
struct list_head same_vma; //鏈入VMA的anon_vma_chain連結串列
struct rb_node rb; //插入AV的rb_root紅黑樹.
};
一個VMA表示程序的一段虛擬地址空間.
struct vm_area_struct {
/*VMA的AVC連結串列,比如第三代程序(連續fork兩次),那麼就會有三個AVC在這個連結串列,表示三個程序有相同的VMA,
前兩個AVC通過rb結點依次插入父程序和父父程序AV的rb_root節點.而第三個AVC插入到當前程序AV的rb_root節點
*/
struct list_head anon_vma_chain;
struct anon_vma *anon_vma; //VMA的AV,只有私有的匿名對映才有一個AV指標
}
結論:
1. 假如程序有N個子孫程序,那麼VMA->AV的rb_root紅黑樹,就有N個AVC插入紅黑樹(包含自身的AVC)
2.假如VMA的anon_vma_chain連結串列有N個AVC,那麼當前程序就屬於第N代程序(連續forkN次)
2.反向對映應用場景
2.1 核心回收時,需要斷開page的所有對映
2.2 頁面遷移時,也要斷開所有頁面的對映
3. 斷開對映處理流程
這裡只分析匿名對映unmap過程
try_to_unmap->rmap_walk->rmap_walk_anon
static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
{
/*獲取page->maping的AV */
anon_vma = rmap_walk_anon_lock(page, rwc);
if (!anon_vma)
return ret;
/*計算頁面在map中的偏移 */
pgoff = page_to_pgoff(page);
/*遍歷AV的rb_root記錄著所有子程序的AVC */
anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {
struct vm_area_struct *vma = avc->vma;/*取子程序的VMA */
unsigned long address = vma_address(page, vma);
/*這裡有直接呼叫rmap_one和done函式
rmap_one: try_to_unmap_one :解除一個對映
done:page_not_mapped 檢查page的mapcount是否大於0,
*/
ret = rwc->rmap_one(page, vma, address, rwc->arg);
if (ret != SWAP_AGAIN)
break;
/*如果mapcount為-1,則直接結束unmap流程 */
if (rwc->done && rwc->done(page))
break;
}
anon_vma_unlock_read(anon_vma);
return ret;
}
主要做了三件事
1. 檢查address是否指向物理頁面page(PFN相同)
2. 重新設定page的pte頁表項
3. 減少page的mapcount和count計數
static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
unsigned long address, void *arg)
{
/*對比address和page是否指向同一個物理頁面,如果發生過COW,PTE內容肯定不一樣,這裡會返回NULL,如果沒有發生COW,這裡檢查通過 */
pte = page_check_address(page, mm, address, &ptl, 0);
if (!pte)
goto out;
/*重新設定頁表項 */
swp_entry_t entry = { .val = page_private(page) };
pte_t swp_pte;
swp_pte = swp_entry_to_pte(entry);
if (pte_soft_dirty(pteval))
swp_pte = pte_swp_mksoft_dirty(swp_pte);
set_pte_at(mm, address, pte, swp_pte);
/*減少mapcount和count引用計數 */
page_remove_rmap(page);
page_cache_release(page);
return ret;
}
4. mapcout計數
do_anonymous_page :缺頁異常
page->_mapcount //初始化為-1,第一次建立對映時設定為0(page_add_new_anon_rmap)
fork後
page->_mapcount++(page_dup_rmap),page->_count++(get_page),並設定PTE為防寫pte_wrprotect
發生了COW
對old page的mapcount--
unmap時
1.如果page還沒有發生COW,則會對page所有的對映進行unmap操作
try_to_unmap_one執行page->_mapcount--
2. COW發生後,page的mapcount相應的減少(page_remove_rmap)