一文圖解|內(nèi)存頁面遷移技術(shù)
1. 概述
頁面遷移(page migrate)
最早是為?NUMA
?系統(tǒng)提供一種將進程頁面遷移到指定內(nèi)存節(jié)點的能力用來提升訪問性能。后來在內(nèi)核中廣泛被使用,如內(nèi)存規(guī)整、CMA、內(nèi)存hotplug等。
頁面遷移對上層應(yīng)用業(yè)務(wù)來說是不可感知的,因為其遷移的是物理頁面,而應(yīng)用只訪問的是虛擬內(nèi)存。內(nèi)核遷移完成后,更新修改對應(yīng)頁表指向遷移后的頁面即可。當然了這里說的不可感知是指業(yè)務(wù)不太關(guān)注,也不需要做對應(yīng)修改。實際上有些場景發(fā)生頁面遷移是業(yè)務(wù)性能是有影響的,下面會詳細描述。
2. 典型場景
我們列舉2個內(nèi)核中發(fā)生頁面遷移的典型場景。
2.1 NUMA Balancing引起的頁面遷移
在典型 NUMA 中,存在多個 node, 本地 CPU 訪問本地 node 節(jié)點對應(yīng)的 memory 性能會快一些。

Linux 的 NUMA 自動均衡機制會嘗試將內(nèi)存遷移到正在訪問它的 CPU 節(jié)點所在的 node。如下圖所示,?CPU24 ~ CPU47
?訪問不是本地 node 對應(yīng)的 memory,性能會比較慢,系統(tǒng)會將其遷移到本地 node 對應(yīng)的 memory 以提升訪問性能。

遷移后如下圖:

2.2 內(nèi)存碎片整理
系統(tǒng)使用一段時候后,由于內(nèi)存碎片的原因,較難滿足連續(xù)內(nèi)存需求,如果需要分配連續(xù)大塊內(nèi)存,需要進行內(nèi)存規(guī)整以形成大塊連續(xù)內(nèi)存,頁面遷移是內(nèi)存碎片整理的基礎(chǔ)。
3. 實現(xiàn)分析
3.1 遷移模式
內(nèi)核中通過接口?migrate_pages
?實現(xiàn)頁而遷移, 分為3個模式。

3.2 實現(xiàn)流程
內(nèi)核文檔有描述這個API是怎么工作的。不過這個描述著實是不太友好, 不容易在腦海形成畫面。

我們通過結(jié)合代碼實現(xiàn),把這個轉(zhuǎn)化為流程圖:

總結(jié)一下,頁面遷移過程本質(zhì)就是分配一個 new_page, 解除原有 page 映射,把舊 page 復制到新 page 并建立新 page 的映射。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)??


4. 頁面遷移過程用戶態(tài)訪問處理
到這里可能會有疑問:如果在頁面遷移過程中,應(yīng)用發(fā)生發(fā)訪問這個遷移中的頁面,會發(fā)生什么?
情景1: 舊頁面的頁表還未解映射, 此時發(fā)生缺頁可以正常訪問原來頁面。

情景2: 舊頁面解除了映射,但新頁面還未建立映射。這時訪問會發(fā)生等待,需要等新頁面建立映射并copy完成頁面后才能訪問。

情景3: 完成了頁面遷移動作,可以正常訪問新頁面了。

下面我們重點分析一下,當舊頁面解除了映射,且新頁面未建立映射這個過程中發(fā)生了用戶態(tài)訪問,內(nèi)核的處理流程是怎樣的。
首先我們看一下舊頁面解除了映射的過程:
解除映射后,再次發(fā)生映射就走到?do_swap_page
?中了。
總結(jié)一下:
頁面遷移前,首先會獲取舊頁面和新頁面的頁面鎖?PG_lock
,在解除映射的時候傳入了由于頁面遷移導致的解映射標記?TTU_MIGRATION
,設(shè)置了此標記會生成一個帶頁面遷移標識的?swap_entry
?設(shè)置到?pte
?中。在設(shè)置好的那一刻走,應(yīng)用進程無法很順利地訪問這個頁面了,需要通過?do_swap_entry
?路徑。
假如此時應(yīng)用進程訪問了這個頁面,會走進到?do_swap_entry
,取出帶遷移標識的?swap_entry
,識別到這個標識,會等待頁面鎖釋放。頁面鎖只有在頁面遷移完成后才會被釋放,也就是會發(fā)生等待直到頁面遷移完成。
5. 用戶態(tài)如何避免發(fā)生頁面遷移
上面我們已經(jīng)知道,如果有頁面遷移過程中發(fā)生用戶態(tài)訪問,很可能是需要發(fā)生等待其遷移完成, 這個過程需要一定耗時。而有時的場景我們是需要避免此種時延抖動,那有什么辦法呢?
方法就是讓這個頁面短時間內(nèi)變得不可移動。
可以看到當發(fā)生頁面復制過程中,如果 page 的引用計數(shù)不符合預期(期望為0)時,這時系統(tǒng)認為有人在使用,不適用做遷移。那么,我們只需要增加 page 的引用計數(shù)就可以。
可以在不想被遷移的時間段開始前通過?pin_user_pages
?這樣的接口,結(jié)束時?unpin
?就可以了。接口最終會調(diào)到?try_grab_page
?增加引用計數(shù)。
原文作者:五花肉
