眾所周知,Linux 會以頁面為單位對記憶體進行管理。不論是將磁碟中的資料載入到記憶體中,還是將記憶體中的資料寫回磁碟中,作業系統都會以頁面為單位進行操作,這也意味著如果我們只向磁碟中寫入一個位元組的數據,作業系統也需要將整個頁面中的全部資料刷入磁碟中。
值得注意的是,在Linux 中,我們既可以使用正常大小的內存頁進行操作,也可以使用大內存頁(Huge Page),儘管絕大多數處理器上的內存頁默認大小為4KB,但部分處理器會使用8KB、16KB 或64KB 作為預設的頁面大小。除了正常的記憶體頁大小之外,不同的處理器還支援不同的大頁面大小。例如,在 x86 處理器上,我們可以使用 2MB 大小的記憶體頁。
4KB 記憶體頁面已經成為一個歷史遺留問題,這一頁大小從上個世紀 80 年代開始被廣泛採用,而至今仍然保留下來。雖然現在的硬體比過去更加豐富,但是我們仍然延續著過去流傳下來的 4KB 記憶體頁大小。通常情況下,我們可以在安裝記憶體時清楚地看到記憶體條的規格,如下圖所示:
圖 1 – 隨機存取記憶體
在今天,4KB 的記憶體頁大小可能不是最佳的選擇,8KB 或 16KB 說不定是更好的選擇,但是這是過去在特定場景下做出的權衡。我們在這篇文章中不要過於糾結於4KB 這個數字,應該更重視決定這個結果的幾個因素,這樣當我們在遇到類似場景時才可以從這些方面考慮當下最佳的選擇,我們在這篇文章中會介紹以下兩個影響記憶體頁大小的因素,它們分別是:
過小的頁面大小會帶來較大的頁表項目增加尋址時 TLB(Translation lookaside buffer)的查找速度和額外開銷;
過大的頁面大小會浪費記憶體空間,造成記憶體碎片,降低記憶體的使用率;
上個世紀在設計內存頁大小時充分考慮了上述的兩個因素,最終選擇了4KB 的內存頁作為操作系統最常見的頁大小,我們接下來將詳細介紹以上它們對操作系統性能的影響。
我們在為什麼Linux 需要虛擬內存一文中曾經介紹過Linux 中的虛擬內存,每個進程能夠看到的都是獨立的虛擬內存空間,虛擬內存空間只是邏輯上的概念,進程仍然需要訪問虛擬記憶體對應的實體內存,從虛擬記憶體到實體記憶體的轉換就需要使用每個行程持有頁表。
為了儲存64 位元作業系統中128 TiB 虛擬記憶體的映射數據,Linux 在2.6.10 中引入了四層的頁表輔助虛擬位址的轉換[,在4.11 中引入了五層的頁表結構,在未來也可能引入更多層的頁表結構以支援64 位元的虛擬位址。
圖 2 – 四層頁表結構
在如上圖所示的四層頁表結構中,作業系統會使用最低的12 位元作為頁面的偏移量,剩下的36 位元會分成四組分別表示目前層級在上一層的索引,所有的虛擬位址都可以用上述的多層頁表查找到對應的實體位址。
因為作業系統的虛擬位址空間大小都是一定的,整片虛擬位址空間被均勻分成了N 個大小相同的記憶體頁,所以記憶體頁的大小最終會決定每個行程中頁表項的層級結構和具體數量,虛擬頁的大小越小,單一行程中的頁表項目和虛擬頁就越多。
PagesCount=VirtualMemory ÷ PageSize
因為目前的虛擬頁大小為4096 字節,所以虛擬位址末端的12 位元可以表示虛擬頁中的位址,如果虛擬頁的大小降到了512 位元組,那麼原本的四層頁表結構或五層頁表結構會變成五層或六層,這不僅會增加記憶體存取的額外開銷,還會增加每個行程中頁表項所佔用的記憶體大小。
因為記憶體對映設備會在記憶體頁的層級運作,所以作業系統認為記憶體分配的最小單元就是虛擬頁。即使用戶程式只是申請了1 位元組的內存,作業系統也會為它申請一個虛擬頁,如下圖所示,如果內存頁的大小為24KB,那麼申請1 字節的內存會浪費~99.9939% 的空間。
圖 3 – 大記憶體的碎片化
隨著記憶體頁大小的增加,記憶體的碎片化情況會越來越嚴重,小的記憶體頁會減少記憶體空間中的記憶體碎片,提高記憶體的使用率。上個世紀的記憶體資源還沒有像今天這麼豐富,在大多數情況下,記憶體都不是限製程式運行的資源,多數的線上服務都需要更多的CPU,而不是更多的記憶體。不過在上個世紀記憶體其實也是稀缺資源,所以提高稀缺資源的使用率是我們必須考慮的:
圖 4 – 記憶體的價格
上個世紀八九十年代的記憶體只有512KB 或2MB,價格也貴得離譜,但是幾GB 的內存在今天卻非常常見,所以雖然內存的利用率仍然十分重要,但是在內存的價格大幅降低的今天,碎片化的記憶體不再是需要解決的關鍵問題了。
除了記憶體的使用率之外,較大的記憶體頁面也會增加記憶體拷貝時的額外開銷,因為Linux 上的寫時拷貝機制,在多個進程共享同一塊記憶體時,當其中的一個進程修改了共享的虛擬記憶體會觸發記憶體頁的拷貝,這時作業系統的記憶體頁越小,寫時拷貝帶來的額外開銷就越小。
總結
就像我們在上面提到的,4KB 的內存頁是上個世紀決定的默認設置,從今天的角度來看,這很可能已經是錯誤的選擇了,arm64、ia64 等架構已經可以支持8KB、16KB 等大小的記憶體頁,隨著記憶體的價格變得越來越低、系統的記憶體變得越來越大,更大的記憶體可能是作業系統更好的選擇,我們重新回顧兩個決定記憶體頁大小的要素:
到最後,我們還是來看一些比較開放的相關問題,有興趣的讀者可以仔細思考一下下面的問題:
Linux 中的磁區、區塊和頁有什麼差別和連結?
Linux 中的區塊大小是如何決定的?常見的大小有哪些?
以上是為什麼 Linux 系統預設頁大小是 4KB的詳細內容。更多資訊請關注PHP中文網其他相關文章!