1. 程式人生 > >Away From Comfort Zone

Away From Comfort Zone

Linux記憶體管理中core VM程式碼中,關於頁表(page tables)管理的程式碼是個重點,是虛擬記憶體(Virtual Memory, VM)的基石,本文探討Linux的頁表實現及發展過程。

頁表概覽

在虛擬記憶體中,頁表是個對映表的概念, 即從程序能理解的線性地址(linear address)對映到儲存器上的實體地址(phisical address)。很顯然,這個頁表是需要常駐記憶體的東西, 以應對頻繁的查詢對映需要(實際上,現代支援VM的處理器都有一個叫TLB的硬體級頁表快取部件,本文不討論)。

設想一個典型的32位的X86系統,它的虛擬記憶體使用者空間(user space)

大小為3G, 並且典型的一個頁表項(page table entry, pte)大小為4 bytes,每一個頁(page)大小為4k bytes。那麼這3G空間一共有(3G/4k=)786432個頁面,每個頁面需要一個pte來儲存對映資訊,這樣一共需要786432個pte!

如何儲存這些資訊呢?一個直觀的做法是用陣列來儲存,這樣每個頁能儲存(4k/4=)1K個,這樣一共需要(786432/1k=)768個連續的物理頁面(phsical page)。而且,這只是一個程序,如果要存放所有N個程序,這個數目還要乘上N! 這是個巨大的數目,哪怕記憶體能提供這樣數量的空間,要找到連續768個連續的物理頁面在系統執行一段時間後碎片化的情況下,也是不現實的。

Linux的頁表實現

上面這種理論上的討論顯然不是實際情況。由於程式存在區域性化特徵, 這意味著在特定的時間內只有部分記憶體會被頻繁訪問,具體點,程序空間中的text段(即程式程式碼), , 共享庫都是固定在程序空間的某個特定部分,這樣導致程序空間其實是非常稀疏的, 於是,從硬體層面開始,頁表的實現就是採用分級頁表的方式,Linux核心當然也這麼做。所謂分級簡單說就是,把整個程序空間分成區塊,區塊下面可以再細分,這樣在記憶體中只要常駐某個區塊的頁表即可,這樣可以大量節省記憶體

Linux最初的二級頁表

Linux最初是在一臺i386機器上開發的,這種機器是典型的32位X86架構,支援兩級頁表。如下圖:

圖片

一個32位虛擬地址如上圖劃分。當在進行地址轉換時,

  • 結合在CR3暫存器中存放的頁目錄(page directory, PGD)的這一頁的實體地址,再加上從虛擬地址中抽出高10位叫做頁目錄表項(核心也稱這為pgd)的部分作為偏移, 即定位到可以描述該地址的pgd;

  • 從該pgd中可以獲取可以描述該地址的頁表的實體地址,再加上從虛擬地址中抽取中間10位作為偏移, 即定位到可以描述該地址的pte;

  • 在這個pte中即可獲取該地址對應的頁的實體地址, 加上從虛擬地址中抽取的最後12位,即形成該頁的頁內偏移, 即可最終完成從虛擬地址實體地址的轉換。

從上述過程中,可以看出,對虛擬地址的分級解析過程,實際上就是不斷深入頁表層次,逐漸定位到最終地址的過程,所以這一過程被叫做page talbe walk

至於這種做法為什麼能節省記憶體,舉個更簡單的例子更容易明白。比如要記錄16個球場的使用情況,每張紙能記錄4個場地的情況。採用4+4+4+4,共4張紙即可記錄,但問題是球場使用得很少,有時候一整張紙記錄的4個球場都沒人使用。於是,採用4 x 4方案,即把16個球場分為4組,同樣每張紙剛好能記錄4組情況。這樣,使用一張紙A來記錄4個分組球場情況,當某個球場在使用時,只要額外使用多一張紙B來記錄該球場,同時,在A上記錄"某球場由紙B在記錄"即可。這樣在大部分球場使用很少的情況下,只要很少的紙即困記錄,當有球場被使用,有需要再用額外的紙來記錄,當不用就擦除。這裡一個很重要的前提就是:區域性性

Linux的三級頁表

當X86引入實體地址擴充套件(Pisycal Addrress Extension, PAE)後,可以支援大於4G的實體記憶體(36位),但虛擬地址依然是32位,原先的頁表項不適用,它實際多4 bytes被擴充到8 bytes,這意味著,每一頁現在能存放的pte數目從1024變成512了(4k/8)。相應地,頁表層級發生了變化,Linus新增加了一個層級,叫做頁中間目錄(page middle directory, PMD), 變成:

  1. 312920110
  2. +-----+---------------+--------------+--------------+
  3. | PGD | PMD | PTE | page offset |
  4. +-----+---------------+--------------+--------------+

實際的page table walk依然類似,只不過多了一級。

現在就同時存在2級頁表和3級頁表,在程式碼管理上肯定不方便。巧妙的是,Linux採取了一種抽象方法:所有架構全部使用3級頁表: 即PGD -> PMD -> PTE。那隻使用2級頁表(如非PAE的X86)怎麼辦?

辦法是針對使用2級頁表的架構,把PMD抽象掉,即虛設一個PMD表項。這樣在page table walk過程中,PGD本直接指向PTE的,現在不了,指向一個虛擬的PMD,然後再由PMD指向PTE。這種抽象保持了程式碼結構的統一。

Linux的四級頁表

硬體在發展,3級頁表很快又捉襟見肘了,原因是64位CPU出現了, 比如X86_64, 它的硬體是實實在在支援4級頁表的。它支援48位的虛擬地址空間[1](不過Linux核心最開始只使用47位)。如下:

  1. 47382920110
  2. +------+------------+------------+------------+--------------+
  3. | PML4 | PGD | PMD | PTE | page offset |
  4. +------+------------+------------+------------+--------------+

Linux核心針為使用原來的3級列表(PGD->PMD->PTE),做了折衷。即採用一個唯一的,共享的頂級層次,叫PML4[2]。這個PML4沒有編碼在地址中,這樣就能套用原來的3級列表方案了。不過代價就是,由於只有唯一的PML4, 定址空間被侷限在(239=)512G, 而本來PML4段有9位, 可以支援512個PML4表項的。現在為了使用3級列表方案,只能限制使用一個, 512G的空間很快就又不夠用了,解決方案呼之欲出。

在2004年10月,當時的X86_64架構程式碼的維護者Andi Kleen提交了一個叫做4level page tables for Linux的PATCH系列,為Linux核心帶來了4級頁表的支援。在他的解決方案中,不出意料地,按照X86_64規範,新增了一個PML4的層級, 在這種解決方案中,X86_64擁一個有512條目的PML4, 512條目的PGD, 512條目的PMD, 512條目的PTE。對於仍使用3級目錄的架構來說,它們依然擁有一個虛擬的PML4,相關的程式碼會在編譯時被優化掉。 這樣,就把Linux核心的3級列表擴充為4級列表。這系列PATCH工作得不錯,不久被納入Andrew Morton的-mm樹接受測試。

不出意外的話,它將在v2.6.11版本中釋出。但是,另一個知名開發者Nick Piggin提出了一些看法,他認為Andi的Patch很不錯,不過他認為最好還是把PGD作為第一級目錄,把新增加的層次放在中間,並給出了他自己的Patch:alternate 4-level page tables patches。Andi更想保持自己的PATCH, 他認為Nick不過是玩了改名的遊戲,而且他的PATCH經過測試很穩定,快被合併到主線了,不宜再折騰。

不過Linus卻表達了對Nick Piggin的支援,理由是Nick的做法conceptually least intrusive。畢竟作為Linux的扛把子,穩定對於Linus來說意義重大。

最終,不意外地,最後Nick Piggin的PATCH在v2.6.11版本中被合併入主線。在這種方案中,4級頁表分別是:PGD -> PUD -> PMD -> PTE

參考:

  • 程式碼:
    • include/asm-generic/pgtable-nopud.h
    • include/asm-generic/pgtable-nopmd.h
    • arch/x86/include/asm/pgtable-2level*.h
    • arch/x86/include/asm/pgtable-3level*.h
    • arch/x86/include/asm/pgtable_64*.h
    * * * * * * 全文完 * * * * * *

[1]: 一開始就實現全64位的虛擬空間,一是實際用不到這麼大的空間,二是技術實現上也有困難,所以首先推出X86_64架構CPU的AMD只只支援48位(256TB),但虛擬地址仍使用64位,只不過對高16位作位擴充套件,跟第48位值一樣。這樣方便以後擴充套件,另外一個副作用是:自然地分出使用者與核心態兩個空間。不多詳述。

[2]: Page Map Layer 4, 這是首先引入X86_64 CPU的AMD的叫法。

相關推薦

Away From Comfort Zone

Linux記憶體管理中core VM程式碼中,關於頁表(page tables)管理的程式碼是個重點,是虛擬記憶體(Virtual Memory, VM)的基石,本文探討Linux的頁表實現及發展過程。 頁表概覽 在虛擬記憶體中,頁表是個對映表的概念, 即從程序能理

Maya: Away from Crowds

cati 秘密 row ttl 保護 set 世界 nature commit Central America: 中美洲 Secret treasure: 秘密寶藏 Ancient Maya settlements: 古代瑪雅遺址 Nature reserve: 自然保護區

[USACO 12DEC]Running Away From the Barn

cee cos have 所在 only length des script hat Description It‘s milking time at Farmer John‘s farm, but the cows have all run away! Farmer J

洛谷P3066 [USACO12DEC]逃跑的BarnRunning Away From

code int ace for 當前 pri con cst 遠的 題面鏈接 一句話題意:給出以1號點為根的一棵有根樹,問每個點的子樹中與它距離小於等於l的點有多少個。 我:似乎並不好做啊。。。看了題解後大霧。。。 sol:考慮樹上差分,對於一個點,在他那個位置++,再找

[USACO12DEC] 逃跑的BarnRunning Away From…(主席樹)

[USACO12DEC]逃跑的BarnRunning Away From… 題目描述 It's milking time at Farmer John's farm, but the cows have all run away! Farmer John needs to round them all u

P3066 [USACO12DEC]逃跑的BarnRunning Away From

目錄 題目 思路 錯誤&&注意 程式碼 題目 luoguP3066 思路 雖說這個題目有多種做法,但 左偏樹演算法: 我們發現這個合併的時候並不好合並,因為存的值不是固定的 那我們是不是可以lazy陣列呢 因為是兩個顆樹合併,顯然是步闊以的 那就轉換一下思路,什

P3066 [USACO12DEC]逃跑的BarnRunning Away From… 樹上差分_樹上倍增

code: #include <cstdio> using namespace std; #define ll long long const int N=200005; int n,fa[N][23],re[N]; ll d[N],up; int main() { //freo

[USACO12DEC]逃跑的BarnRunning Away From… - 題解

題意簡述:給一棵以\(1\)為根的樹,節點有\(n\)個,求每個點以它為根的子樹中與它距離小於等於\(l\)的點有多少個。 解法:主席樹。按樹的\(dfs\)序建立一個主席樹(離散化)記錄深度,在同一子樹中的點一定在連續一段,計算與它距離等於\(l\)的點(假想的點)的排名。 程式碼: #include

AI Pathology Startup Fuels Shift Away from Physical Slides

Hundreds of millions of tissue biopsies are performed worldwide each year — most of which are diagnosed as non-cancerous. But for the few days or weeks it

Let’s Get Away From Slime Sourcing

Let’s Get Away From Slime SourcingSlime sourcing: it’s to the letter of libre licenses, but it’s certainly not to the spirit. Let’s talk about a trend in f

#15 Meetings. Madness. Comfort Zone.

#15 Meetings. Madness. Comfort Zone.Where should I start? Well, our last week began with lots of user stories that wanted to be finalized and polished. Tha

[USACO12DEC]逃跑的BarnRunning Away From

blank org usaco 維護 size 離線 線段樹合並 求一個 變化 [USACO12DEC]逃跑的BarnRunning Away From… 一個經典問題: 邊沒有邊權,多次詢問,查子樹中距離x為L的點的個數 n<=1e5,q<=1e5 1.離線,

Japan's hopping Ryugu robots send back video from the surface of an asteroid 180million miles away

A pair of Japanese robots have captured stunning new photographs and video from the surface of an asteroid 180 million miles away. The cookie tin-shaped ro

linux下weblogic11g成功安裝後,啟動報錯Getting boot identity from user

tro ace author reason tac ica ons pst pri <2015-7-1 下午05時46分33秒 CST> <Info> <Management> <BEA-141107> <Versi

NumberFormatException: Invalid int類型不匹配異常——使用SQL數據庫查詢語句select * from blacknumber order by _id desc limit ?,20;出現

rom add ray 修改 java turn 技術分享 data color 異常:類型不匹配 05-06 08:12:38.151: E/AndroidRuntime(14904): java.lang.NumberFormatException: Invalid i

CentOS7使用ssh不能登錄,報錯:Read from socket failed: Connection reset by peer

read from socket failed: connection reset by peer使用xshell登錄CentOS7,不能登錄,使用另外一臺Linux主機,telent 22端口是同的,ssh連接報以下錯誤:Read from socket failed: Connection reset b

C# LINQ 詳解 From Where Select Group Into OrderBy Let Join

分享 str 關聯 例如 數據 lln ole inf emp 目錄 1. 概述 2. from子句 3. where子句 4. select子句 5. group子句 6. into子句 7. 排序子句 8. let子句 9. join子句 10. 小結 1. 概述

ArcGIS “Error HRESULT E_FAIL has been returned from a call to a COM component.” 異常的解決

exce run sys 簡單 mpc mco pre amp back 錯誤提示內容: {System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been re

select * from sys.sysprocesses

batch 標示 run 發生 用戶 大於 process 連接字符串 是否 MSDN:包含正在 SQL Server 實例上運行的進程的相關信息。這些進程可以是客戶端進程或系統進程。 視圖中主要的字段: 1. Spid:Sql Servr 會話ID 2. Kpid:Wi

HDU1813:Escape from Tetris(IDA)

memset onos 。。 字符 每一個 題目 push pri [1] Problem Description 因為整日整夜地對著這個棋盤,Lele最終走火入魔。每天一睡覺。他就會夢到自己會被人被扔進一個棋盤中,一直找不到出路,然後從夢中驚醒。久而久之,Lele被搞