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