一篇看懂|Linux內(nèi)核反向映射機(jī)制原理
當(dāng)內(nèi)核需要對(duì)申請(qǐng)的page進(jìn)行回收時(shí),在回收頁(yè)表前需要解除該page的映射關(guān)系,即內(nèi)核需要知道這個(gè)物理頁(yè)被映射到了哪些進(jìn)程虛擬地址空間,因此就有了反向映射機(jī)制。反向映射一般分為匿名頁(yè)映射和文件頁(yè)映射,本文先介紹匿名頁(yè)反向映射。
基本數(shù)據(jù)結(jié)構(gòu)
page結(jié)構(gòu)體中涉及反向映射的相關(guān)成員
mapping 因?yàn)橹羔樧兞渴?個(gè)字節(jié),因此可以用最后兩位來區(qū)分不同的映射。對(duì)于匿名映射,最低位為PAGE_MAPPING_ANON,指向anon_vma結(jié)構(gòu)體,每個(gè)匿名頁(yè)對(duì)應(yīng)唯一的anon_vma;對(duì)于文件映射而言,指向address_space結(jié)構(gòu)體。
index表示頁(yè)偏移,對(duì)于匿名映射,index 表示page在vm_areat_struct指定的虛擬內(nèi)存區(qū)域中的頁(yè)偏移;對(duì)于匿名映射,index表示物理頁(yè)中的數(shù)據(jù)在文件中的頁(yè)偏移。
_mapcount 記錄該頁(yè)面被映射到了多少個(gè)vm_struct虛擬內(nèi)存區(qū)域。注意和mm_struct結(jié)構(gòu)體中的map_count做區(qū)分,map_count表示mm_strcut中有多少個(gè)vm_struct區(qū)域。
一般struct anon_vma稱為AV,struct anon_vma_chain稱為AVC,struct vm_area_struct稱為VMA,page找到VMA的路徑一般如下:page->AV->AVC->VMA,其中AVC起到橋梁作用,至于為何需要AVC,主要考慮當(dāng)父進(jìn)程和多個(gè)子進(jìn)程同時(shí)擁有共同的page時(shí)的查詢效率,具體對(duì)比2.6版本時(shí)的實(shí)現(xiàn)方式。
結(jié)構(gòu)anon_vma
結(jié)構(gòu)anon_vma_chain
struct vm_struct中相關(guān)成員
上面幾個(gè)結(jié)構(gòu)體的關(guān)系大致如下:

page通過mapping找到VMA,VMA 遍歷自己管理的紅黑樹rb_root,找到樹上的每個(gè)節(jié)點(diǎn)AVC,AVC通過成員指針anon_vma找到對(duì)應(yīng)的VMA,這個(gè)過程就完成了頁(yè)表映射查找。需要注意的幾點(diǎn):
1.VMA中也有鏈表anon_vma_chain管理各個(gè)AVC,這里主要用在父子進(jìn)程之間的管理,下文會(huì)詳細(xì)介紹。
2.VMA中有成員指針成員anon_vma,同時(shí)AVC中也有成員指針anon_vma,VAC起到橋梁作用所以可以指向VMA和AVC,那VMA中為何又需要指向AV呢?進(jìn)程創(chuàng)建的流程中一般都是新建AV,然后創(chuàng)建AVC及AMV,然后調(diào)用anon_vma_chain_link建立三者之間的關(guān)系,但是當(dāng)一個(gè)VMA沒有對(duì)應(yīng)頁(yè)的時(shí)候,此時(shí)觸發(fā)pagefault,這里可以快速判斷VMA有沒有對(duì)應(yīng)的頁(yè)面。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ?


常用接口
anon_vma_chain_link 1.將VAC中的vma和anon_vma分別指向VMA和AV; 2.將AVC加入到VMA的anon_vma_chain鏈表上; 3.將AVC加入到AV的rb_root紅黑樹上,通常都是通過遍歷這個(gè)紅黑樹找到所有的AVC;
代碼實(shí)現(xiàn)
反向映射跟父子進(jìn)程的寫時(shí)拷貝有關(guān)系,所以先從父子進(jìn)程創(chuàng)建時(shí)對(duì)AV,AVC,VMA的創(chuàng)建開始講。
1.父進(jìn)程創(chuàng)建匿名頁(yè)面

當(dāng)觸發(fā)pagefault的時(shí)候走到handle_pte_fault中,anon_vma_prepare中負(fù)責(zé)創(chuàng)建AVC和AV并建立彼此的關(guān)系;真正將創(chuàng)建的page與av關(guān)聯(lián)在__page_set_anon_map中完成。這樣的話父進(jìn)程新建的page在自己的反向映射中的關(guān)系就算完成了。
至于index的含義看linear_page_index的實(shí)現(xiàn)應(yīng)該就明白了。
2.父進(jìn)程創(chuàng)建子進(jìn)程
當(dāng)父進(jìn)程創(chuàng)建子進(jìn)程的時(shí)候,子進(jìn)程會(huì)復(fù)制父進(jìn)程的VMA作為自己的進(jìn)程地址空間,并且父子進(jìn)程共享相同的頁(yè)面,知道子進(jìn)程往自己的地址空間寫數(shù)據(jù),這就是所謂的COW。這種情況需要完成兩件事情:1.子進(jìn)程需要繼承父進(jìn)程的AVC,AV,VMA及三者之間的關(guān)系;2.創(chuàng)建自己的AV,AVC,VMA。
以上實(shí)現(xiàn)流程在dup_mm->dup_mmap->anon_vma_fork中完成。
dup_mmap中就是組個(gè)創(chuàng)建子進(jìn)程的VMA,并復(fù)制父進(jìn)程對(duì)應(yīng)VMA的信息


anon_vma_clone中新建了AVC,將子進(jìn)程的VMA關(guān)聯(lián)到父進(jìn)程的AV中,所以父進(jìn)程AV的rb樹上就有了子進(jìn)程的AVC,通過遍歷父進(jìn)程AV的rb樹就能找到子進(jìn)程的VMA。一個(gè)VMA可以包含多個(gè)頁(yè)面,但是該區(qū)域內(nèi)的所有頁(yè)面只需要一個(gè)AV來反向映射即可。

具體anon_vma_clone代碼如下
3.子進(jìn)程發(fā)生cow,創(chuàng)建自己的匿名頁(yè)面
當(dāng)新創(chuàng)建的子進(jìn)程寫數(shù)據(jù)時(shí)觸發(fā)pagefault,在wp_page_copy中會(huì)創(chuàng)建新的page,此時(shí)創(chuàng)建的AV和AVC管理子進(jìn)程自己的VMA
4.頁(yè)面回收,解除映射
物理頁(yè)回收時(shí)通過調(diào)用try_to_unmap解除一個(gè)page的頁(yè)表映射。對(duì)于匿名頁(yè)面解除映射而言,走
try_to_unmap->rmap_walk->rmap_walk_anon流程。
rmap_one指向try_to_umap_one,該函數(shù)內(nèi)容比較復(fù)雜,這里只截取了頁(yè)表項(xiàng)解除的操作。
原文作者:Linux內(nèi)核那些事
