2.6 Linux 內核使用了許多技術來改進對大量內存的使用,使得 Linux 比以往任何時候都更適用於企業。本文列出了一些更重要的改變,包括反向映射(reverse mapping)、使用更大的內存頁、頁表條目存儲在高端內存中,以及更穩定的內存管理器。 隨著 Linux 內核的發展和成熟,更多的用戶期待著 Linux 可以運行非常大的系統來處理科學分析應用程序或者甚至海量數據庫。這些企業級的應用程序通常需要大量的內存才能好好運行。2.4 Linux 內核有識別相當大數量的內存的功能,但是 2.5 內核發生了很多改變,使其有能力以更有效的方式處理更大量的內存。
反向映射 在 Linux 內存管理器中,頁表保持對進程使用的內存物理頁的追蹤,它們將虛擬頁映射到物理頁。這些頁中有一些可能不是長時間使用,它們應該被交換出去。不過,在它們可以被交換出去之前,必須找到映射那個頁的每一個進程,這樣那些進程中相應頁的頁表條目才可以被更新。在 Linux 2.4 內核中,這是一項令人生畏的任務,因為為了確定某個頁是否被某個進程映射,必須遍歷每個進程的頁表。隨著在系統中運行的進程數量的增加,將這些頁交換出去的工作量也會增加。 反向映射,或者說是 RMAP,就是為解決此問題而在 2.5 內核中實現的。反向映射提供了一個發現哪些進程正在使用給定的內存物理頁的機制。不再是遍歷每個進程的頁表,內存管理器現在為每一個物理頁建立了一個鏈表,包含了指向當前映射那個頁的每一個進程的頁表條目(page-table entries, PTE)的指針。這個鏈表叫做 PTE 鏈 。PTE 鏈極大地提高了找到那些映射某個頁的進程的速度,如圖 1 所示 當然,沒有什麼是免費的:用反向映射獲得性能提高也要付出代價。反向映射最重要、明顯的代價是,它帶來了一些內存開銷。不得不用一些內存來保持對所有那些反向映射的追蹤。PTE 鏈的每一個條目使用 4 個字節來存儲指向頁表條目的指針,用另外 4 個字節來存儲指向鏈的下一個條目的指針。這些內存必須使用低端內存,而這在 32 位硬件上有點不夠用。有時這可以優化到只使用一個條目而不使用鏈表。這種方法叫做 p頁直接方法(page-direct approach)。如果只有一個到這個頁的映射,那麼可以用一個叫做“direct”的指針來代替鏈表。只有在某個頁只是由一個惟一的進程映射時才可以進行這種優化。如果稍後這個頁被另一個進程所映射,它將不得不再去使用 PTE 鏈。一個標記設置用來告訴內存管理器什麼時候這種優化對一個給定的頁有效。 反向映射還帶來了一些其他的復雜性。當頁被一個進程映射時,必須為所有那些頁建立反向映射。同樣,當一個進程釋放對頁的映射時,相應的映射也必須都刪除掉。這在退出時尤其常見。所有這些操作都必須在鎖定情況下進行。對那些執行很多派生和退出的應用程序來說,這可能會非常浪費並且增加很多開銷。 盡管有一些折衷,但可以證明反向映射是對 Linux 內存管理器的一個頗有價值的修改。通過這一途徑,查找定位映射某個頁的進程這一嚴重瓶頸被最小化為只需要一個簡單的操作。當大型應用程序向內核請求大量內存和多個進程共享內存時,反向映射幫助系統繼續有效地運行和擴展。當前還有更多對反向映射的改進正在研究中,可能會出現在未來的 Linux 內核版本中。
大內存頁 典型地,內存管理器在 x86 系統上處理的內存頁為 4 KB。實際的頁大小是與體系結構相關的。對大部分用途來說,內存管理器以這樣大小的頁來管理內存是最有效的。不過,有一些應用程序要使用特別多的內存。大型數據庫就是其中一個常見的例子。由於每個頁都要由每個進程映射,必須創建頁表條目來將虛擬地址映射到物理地址。如果您的一個進程要使用 4KB 的頁來映射 1 GB 內存,這將用到 262,144 個頁表條目來保持對那些頁的追蹤。如果每個頁表條目消耗 8 個字節,那些每映射 1 GB 內存需要 2 MB 的開銷。這本身就已經是非常可觀的開銷了,不過,如果有多個進程共享那些內存時,問題會變得更嚴重。在這種情況下,每個映射到同一塊 1 GB 內存的進程將為頁表條目付出自己 2 MB 的代價。如果有足夠多的進程,內存在開銷上的浪費可能會超過應用程序請求使用的內存數量。 解決這一問題的一個方法是使用更大的頁。大部分新的處理器都支持至少一個小的和一個大的內存頁大小。在 x86 上,大內存頁的大小是 4 MB,或者,在物理地址擴展(PAE)打開的系統上是 2 MB。假定在前面的中使用頁大小為 4 MB 的大內存頁,同樣 1 GB 內存只用 256 個頁表條目就可以映射,而不需要 262,144 個。這樣開銷從 2 MB 變為 2,048 個字節。 大內存頁的使用還可以通過減少 變換索引緩沖(translation lookaside buffer, TLB) 的失敗次數來提高性能。TLB 是一種頁表的高速緩存,讓那些在表中列出的頁可以更快地進行虛擬地址到物理地址的轉換。大內存頁可以用更少的實際頁來提供更多的內存,相當於較小的頁大小,使用的大內存頁越多,就有越多的內存可以通過 TLB 引用。
在高端內存中存儲頁表條目 在 32 位機器上頁表通常只可以存儲在低端內存中。低端內存只限於物理內存的前 896 MB,同時還要滿足內核其余的大部分要求。在應用程序使用了大量進程並映射了大量內存的情況下,低端內存可能很快就不夠用了。 現在,在 2.6 內核中有一個配置選項叫做 Highmem PTE,讓頁表條目可以存放在高端內存中,釋放出更多的低端內存區域給那些必須放在這裡的其他內核數據結構。作為代價,使用這些頁表條目的進程會稍微慢一些。不過,對於那些在大量進程在運行的系統來說,將頁表存儲到高端內存中可以在低端內存區域擠出更多的內存。
穩定性 更好的穩定性是 2.6 內存管理器的另一個重要改進。當 2.4 內核發布時,用戶幾乎馬上就開始遇到內存管理相關的穩定性問題。從內存管理對整個系統的影響來看,穩定性是至關重要的。問題大部分已經解決,但是解決方案必須從根本上推翻原來的內存管理器並重寫一個簡單的多的管理器來取代它。這為 Linux 的發行者改進自己特定發行版本的 Linux 的內存管理器留下了很大的空間。不過,那些改進的另一方面是,在 2.4 中的內存管理部件由於使用的發行版本不同而很不相同。為避免再發生這樣的事情,內存管理成為 2.6 中內核開發的最細致的一部分。從很低端的桌面系統到大型的、企業級的、多處理器的系統,新的內存管理代碼已經在它們上面都已經進行了測試和優化。
結束語 Linux 2.6 內核中內存管理的改進遠遠不只本文中提到的這些特性。很多變化是細微的,卻相當重要。這些變化一起促生了 2.6 內核中的內存管理器,它的設計目標是更高的性能、效率和穩定性。有一些變化,比如 Highmem PTE 和大內存頁,目的是減少內存管理帶來的開銷。其他變化,比如反向映射,提高了某些關鍵領域的性能。之所以選擇這些特別的例子,是因為它們舉例說明了 Linux 2.6 內核得到了怎樣的調整和增強,以便更好地處理企業級的硬件和應用程序。