虛擬化與容器

巢狀分頁(nested paging)

一台普通電腦本來就在玩一場位址轉譯的遊戲:程式用的是虛擬位址,CPU 的記憶體單元走訪一張分頁表,把每個虛擬位址變成真正的實體位址。如今把這台機器放進一台虛擬機裡,麻煩就來了——「兩層」假象。客體以為它的位址變成了「實體」位址,但客體的「實體」記憶體本身就是假的;它還得再被轉譯一次,才能找到主機那真正真實的記憶體。巢狀分頁就是那個用矽硬體、一趟就把兩次轉譯都做完的硬體功能。

把這兩層走一遍。第一層,在客體內部,客體自己的分頁表把一個客體虛擬位址變成一個客體實體位址——跟平常一模一樣,因為這些表是客體自己管的。第二層,超管理器維護另一組獨立的表(Intel 叫它擴充分頁表 EPT;AMD 叫它巢狀分頁表 NPT),把每個客體實體位址變成一個主機實體位址——也就是真正的 RAM 位置。有了巢狀分頁,CPU 的分頁走訪器會為每一次記憶體存取「自動」跟隨「兩」層,把客體虛擬→客體實體→主機實體串起來。關鍵的勝利是:客體現在可以自由讀寫「自己」的分頁表而不必陷入,因為超管理器只擁有第二組表。

它為何是個轉捩點:在巢狀分頁之前,超管理器用的是影子分頁表,超管理器得攔截客體每一次分頁表的改動,才能讓一張合併過的影子保持正確——這是一波又一波昂貴的 VM-exit,在行程頻繁生滅或開機期間尤其嚴重。巢狀分頁讓硬體直接把兩次轉譯組合起來,從而刪掉了一整類退出。誠實的代價是:一次 TLB 未命中如今得走「兩」棵分頁表樹而非一棵,所以一次轉譯未命中更昂貴;在巢狀分頁下,大型分頁(large page)與良好的 TLB 行為就更顯重要了。

一個客體行程碰了客體虛擬位址 0x4000。客體的分頁表把它映射到客體實體位址 0x9000;超管理器的 EPT 再把客體實體位址 0x9000 映射到主機實體位址 0x3A2000——真正的 RAM。硬體一趟把兩層都走完,而客體從不知道第二步發生過。

兩層分頁表——客體自己的,再加上超管理器的 EPT——由硬體在一趟走訪中組合完成。

巢狀分頁用「更貴的 TLB 未命中」換來「更少的 VM-exit」:一次未命中如今走兩棵樹、而非一棵。它在歷史上也並非「消除」了影子分頁表——而是「取代」了它們;你只會在缺乏 EPT/NPT 的硬體上、或某些特例中才碰到影子表。

又称
Intel EPTExtended Page TablesAMD NPTNested Page Tablessecond-level address translation二級位址轉譯(EPT / NPT)