哈嘍大家好我是鹹魚,在《Linux 記憶體管理 pt.1》中我們學習了什麼是物理記憶體、虛擬記憶體,瞭解了記憶體映射、缺頁異常等內容 那麼今天我們來接著學習 Linux 記憶體管理中的多級頁表和大頁 多級頁表&大頁 在《Linux 記憶體管理 pt.1》中我們知道了內核為每個進程都維護了一張頁表,這張頁表用來記 ...
哈嘍大家好我是鹹魚,在《Linux 記憶體管理 pt.1》中我們學習了什麼是物理記憶體、虛擬記憶體,瞭解了記憶體映射、缺頁異常等內容
那麼今天我們來接著學習 Linux 記憶體管理中的多級頁表和大頁
多級頁表&大頁
在《Linux 記憶體管理 pt.1》中我們知道了內核為每個進程都維護了一張頁表,這張頁表用來記錄進程虛擬記憶體與物理記憶體的映射關係
頁表實際上存儲在 MMU 當中。MMU(Memory Management Unit,記憶體管理單元)是CPU內部的一個硬體模塊
MMU 負責將虛擬地址轉換為物理地址,從而實現進程間記憶體地址隔離和虛擬記憶體的實現
每個進程都有一張頁表,一張頁表中有很多頁表項(頁),每個頁表項大小為 4KB
也就是說,每一個記憶體映射關係,都需要一個 4 KB 或者 4 KB 整數倍的記憶體空間
小伙伴們有沒有想過這樣一個疑問:為什麼 Linux 預設頁大小是 4KB ?
這其實是一個歷史遺留問題,後續鹹魚有時間的話會單獨寫一篇來聊聊
現在我們應該把目光放到另一個點上:一個 32 位系統會為每個進程分配 4G 的虛擬地址空間(虛擬記憶體),這樣的話會導致一張頁表裡面會有特別多頁(一百多萬)
而且每個頁為一個地址,占用 4 個位元組,32 位系統中一張頁表有 1048576 張頁,那就是一張頁表占 1048276 * 4 / 1024 = 4M
也就是說一個進程啥都不幹,光是頁表大小就占了 4M,如果每張頁都有映射關係那也就算了,問題是絕大部分程式僅僅就使用了幾張頁
先不說這樣會導致一個頁表裡面有大量的頁,占用大量的空間。如果想要找到存儲了對映關係的那一張頁,得從頭開始查找,這樣會導致查詢效率很慢
為瞭解決頁表項過多這個問題,Linux 提供了兩種機制,也就是多級頁表和大頁
多級頁表
我們知道,每個進程自身都會維護一個虛擬記憶體,而每個進程虛擬記憶體比物理記憶體要大得多,只有在使用的時候才會被分配到物理記憶體
多級頁表就是把被分配了物理記憶體的虛擬記憶體記憶體分成了一塊一塊,將原來的映射關係改成了區塊索引和區塊內的偏移量
多級頁表將頁表分為多級,每級頁表僅用於管理對應的物理記憶體空間,這樣就可以大大減少頁表中的項數以及頁表大小,從而減輕系統負擔
多級頁表通常由多個頁目錄和多個頁表組成,每個頁表存儲了該頁的物理地址、讀寫許可權等信息;而頁目錄項則存儲了指向該頁表的地址
Linux 採用四級頁表來管理記憶體頁,如下圖所示
多級頁表和一級頁表的區別
在Linux中,多級頁表和一級頁表的最大區別在於多級頁表只存儲有映射關係(即被分配了物理記憶體)的頁,而一級頁表存儲了所有頁表項
用一級頁表的話,整個頁表都得存放在記憶體當中,而使用多級頁表的話,只有被分配了物理記憶體的頁會存在記憶體中
舉個例子,一級頁表就相當於一本厚厚的字典,我們在一級頁表中查找存儲了映射關係的頁就相當於在這本字典中從開始位置查找 而多級頁表相當於把這本厚厚的字典拆成了多本字典,如果要查東西,直接去對應的小字典上查找即可,減少了大字典中要從開始處查找的不必要時間,提高了效率
大頁
比普通頁更大的記憶體塊,常見的大小有 2MB 和 1GB
大頁通常用在使用大量記憶體的進程上,比如 Oracle、DPDK 等
通過上面這些機制,在頁表的映射下進程就可以通過虛擬記憶體來訪問物理記憶體了,那麼進程是如何使用被分配了物理記憶體的虛擬記憶體呢
我們來看下虛擬記憶體中的用戶空間記憶體
上圖所示,用戶空間記憶體被分割成了五個不同的記憶體段:
- 只讀段:代碼和常量等
- 數據段:全局變數等
- 堆:動態分配的記憶體
- 文件映射段:動態庫、共用記憶體等
- 棧:局部變數和函數調用的上下文等。棧的大小是固定的,一般是 8 MB
感謝閱讀,喜歡作者就動動小手[一鍵三連],這是我寫作最大的動力