CMA,memory compaction,migration and fragmation

CMA

 

A reworked contiguous memory allocator

http://lwn.net/Articles/447405/node

June 14, 2011linux

     分配大塊物理上連續內存的問題一直在被討論。虛擬內存,天生的在系統上分散使用內存頁,內核運行一下子就只剩下不多的連續空閒的頁。多年以來,內核開發者處理這個問題的方法是儘可能避免對大的連續物理頁分配的依賴。只有至關少的內核代碼會嘗試分配多於兩個物理連續頁。算法

     最近,對大的連續頁分配的需求持續增加。一個是大頁和THP,另外一個是有新麻煩的舊事:不能執行分散/彙集DMA操做的硬件。任何只能對物理連續區域作DMA的設備(沒有IOMMU)須要一個物理連續的緩存。這種需求通常出如今相對來講比較低端的硬件,有人可能但願這種硬件會隨着時間流逝愈來愈少。但咱們如今看到的倒是在得到能力提高的同時仍有連續DMA緩存需求的設備。舉個例子,能獲取全高清數據的視頻捕捉引擎能執行不少轉換,但它仍須要一個連續的緩存來保存結果。高清視頻的出現加劇了這個問題--這些物理連續的緩存比之前更大也更難分配了。
     大約一年之前,LWN發表過想要解決這個問題的CMA(contiguous memory allocator)補丁。這個補丁仍然遵循了傳統的爲知足大的分配請求的特殊目的而在啓動的時候保留一部份內存的方法。幾年之前,這種技術就已經被用在「bigphysarea」補丁中了,它簡單的傳給內核「mem=」啓動參數來保留一部分物理內存不被使用。安卓的pmem驅動也是從一個保留的區域裏分配內存。近20年的經驗證實這種方法是可行的。不足是保留的內存就不能再被其餘地方使用了;若是設備沒有使用這部份內存,它就保持閒置。這種浪費愈來愈不被內核開發者,還有用戶所歡迎。
     由於這個和其餘的緣由,CMA補丁未被合併。可是問題還沒有解決,而如今也沒人繼續研究這兩種方法。CMA補丁的最新版看起來有點不一樣,儘管還有一些問題須要解決,這個補丁仍是有更大的機會合入主線的。
     CMA分配器仍然能夠工做在一個保留的內存區域,但很明顯這不是你們想要的操做模式。做爲代替,新的CMA嘗試維護內存區域,當有需求的時候連續的內存就會被建立。爲實現這個目的,CMA依賴於內存管理代碼中的「遷移類型」機制。
     每個區裏,頁塊中的頁被標記爲(或非)可移動的或可重聲明的。可移動頁通常是指頁緩存或匿名頁,它們經過頁表和頁緩存基樹來被訪問。只要頁表和基樹作相應的改動,這些頁的內容就能夠被移到其餘地方。可重聲明頁是指可能會按需返回給內核的頁,它們保存數據結構好比inode緩存。不可移動頁通常是內核直接指向的頁,好比,經過kmalloc()得到的內存就不能在不破壞數據的狀況下被移動。
     內存管理子系統會盡可能把可移動頁放在一塊兒。(由於)若是想經過移動頁來釋放大塊內存,只須要一個不可移動的頁就能夠摧毀全部的努力。經過把可移動頁聚在一塊兒,內核但願能按要求釋放大塊區域從而避免了這種問題。內存壓縮代碼依賴這些可移動頁區域來工做。
     CMA經過增長一個新的「CMA」的遷移類型來擴展這種機制,它就像「movable」類型,但有兩點不一樣。「CMA」類型是粘貼的,被標記爲CMA的頁就不會再被內核改變了。內存分配器再也不從CMA區域分配不可移動頁,而且只有在其它方法不可用的狀況下才會爲其餘使用來分配CMA頁。因此,被標記爲用做CMA的內存區域只包含可移動頁,而且有相對較多的空閒頁。
     換句話說,被標記用做CMA的內存仍然能夠被系統的其餘部分使用,但有個限制是它只能包含可移動頁。當一個驅動須要連續的內存時,CMA分配器會嘗試從其中一個內存區域中分配足夠的頁來建立適當大小的連續緩存。若是那些內存區域的頁是可移動的(現實中有時不會知足),驅動就會得到它須要的緩存。但這個緩存不須要時,內存能夠被用做其餘目的。
     有人可能會疑惑爲什麼須要這種機制,內存壓縮已經能夠爲THP建立大塊物理連續的內存了。答案是DMA緩存有一些跟大頁不一樣的需求。它們可能更大,舉個例子,大多數架構上THP是2MB,而DMA緩存能達到10MB或更多。也有可能存在只能映射DMA緩存到特定內存範圍的奇葩硬件--CMA的開發者Marek Szyprowski貌似就有這種硬件。最後,一個2MB的大頁必須2MB對齊,而DMA緩存的對齊要求則更寬鬆一些。CMA分配器能夠得到正好是須要的數量的內存(不用像夥伴分配器那樣擴展到下一個2的冪的大小)而不用擔憂過分嚴格的對齊要求。
     CMA補丁提供了一些函數以方便建立內存區域和爲特定範圍的內存創建「內容/上下文」。而後有cm_alloc()和cm_free()來獲取和釋放緩存。可是,設備驅動最好不要直接調用CMA,而是把感知CMA集成到DMA函數裏。當一個驅動調用好比dma_alloc_coherent()函數,CMA會被自動使用來知足要求。大多數狀況下,這就能夠知足需求了。
     剩餘的關於CMA的疑問中的一個是關於如何在第一時間建立特殊區域。當前的方案是但願在系統的板文件(board file:系統相關的設置)中加入一些特殊的調用,這很像ARM的作事方法。社區的目的是再也不使用板文件,因此得找其它方法。就像Arnd Bergmann指出的,把這些信息移到設備樹也不是一個選項;這其實就是一個策略的問題。Arnd正在推進能夠在大多數系統上設置一些合理的默認設置的方法,特殊挑戰的奇葩系統的修復能夠隨後加上。
     最終的結果是這個補丁進入主線以前起碼還得再有一輪迭代。可是CMA知足了一個真實的需求。這些代碼有潛力使物理連續內存分配更加可靠,同時最小化對系統其它部分的影響。看起來值得擁有。編程

 

 

Compaction

 

Memory compaction

http://lwn.net/Articles/368869/數組

January 6, 2010緩存

     長期存在的內存碎片化問題被討論過好幾回了。簡短來講:隨着系統運行,各個用戶之間的頁趨於分散,致使找到成組的物理連續頁愈來愈難。社區在儘量的避免高階(多頁)內存分配的需求方面作了大量工做,歷來防止大部份內核功能被頁碎片化破壞。但仍然存在須要高階分配需求的存在,須要這種分配的代碼在碎片化的系統上會失敗。
     值得注意的是,某種程度上,這個問題事實上正變得愈來愈糟糕。當前的處理器並不侷限於4K大小的頁,它們能夠在一個進程的一部分地址空間中使用更大的頁(大頁)。使用大頁能夠帶來性能優點,這是由減小處理器TLB壓力來得到的。可是使用大頁須要系統能夠提供物理連續的內存區域,這些內存不但要足夠大並且還要適當的對齊。在運行了一段時間的系統上找到這種空間具備至關大的挑戰。
     幾年以來,內核開發者爲減緩這個問題作了大量努力,帶來了「ZONE_MOVABLE」和「lumpy reclaim」技術。可是還有更多的能夠作,特別是在修復碎片化來恢復更大的大塊內存方面。在這方面沉寂了一段時間後,Mel Gorman最近帶着實現了內存壓縮的補丁歸來了。咱們大致看一下這個補丁是如何工做的。
     想象這樣一個很小的內存區:服務器


     這裏,白色的頁是空閒的,紅色的已經被分配使用了。就像咱們看到的,這個區已經碎片化了,從這個區中分配一個4個頁的塊會失敗。事實上,甚至兩個頁的分配也會失敗,由於沒有一對空閒頁是適當對齊的。
     是時候調用內存壓縮代碼了。這些代碼經過兩個獨立的算法來工做:第一個算法從區的底部開始並建立一個可移動的已分配頁的鏈表:網絡


     同時,在區的頂部,另外一個算法建立一個可被頁遷移使用的空閒頁鏈表:數據結構


     最終在朝着區的中間的方向上會相遇。剩下的就是在相遇點上調用頁遷移代碼(再也不只是用在NUMA系統上了)把已使用的頁移到區頂部的空閒區中,產生像這樣的一個圖:架構


     如今咱們有了一個好的八頁的連續的空閒頁,能夠用來知足高階分配的要求。
     固然,這裏的圖片相對於現實中發生的已經作過簡化了。一開始,內存區會更大,那意味着有更多的工做須要作,但產生的空閒區域也會更大。
     但全部的這些只有在有問題的頁是可移動的才能工做。不是全部的頁都會按照但願的能夠移動,只有那些經過一個間接的層來尋址和未被釘住的頁才能夠移動。因此,大部分用戶空間頁--經過用戶虛擬地址來訪問--能夠移動,所須要作的是對相關的頁表項作相應的調整。大部分被內核直接使用的內存是不能移動的--但一些是能夠重聲明的,意味着它們能夠按需被釋放。只要一個不可移動的頁就能夠毀壞一個連續的內存段。好消息是內核已經把可移動的和不可移動的頁分開了,因此,事實上,不可移動頁的問題比想象中的要小。
     內存壓縮算法能夠經過二者之一來觸發。一是往/proc/sys/vm/compact_node(CentOS 6.5上是一個只寫的compact_memory)中寫入節點號,就會在指定的NUMA節點上調用內存壓縮。另外一個是在分配高階頁失敗時,這種狀況下,相對於經過直接重聲明來釋放頁,內存壓縮會被優先使用。若是沒有明確的觸發,內存壓縮不會被調用,移動頁會有消耗,因此在不須要的時候最好避免使用。
     Mel運行了一些簡單的測試顯示,在內存壓縮使能的狀況下,他能夠把超過90%的系統內存分配成大頁同時減小重聲明活動的數量。因此這看起來是有用的工做。可是這是內存管理代碼,因此合入主線的時間很差提早預計。

 

 

Migration

 

Page migration

http://lwn.net/Articles/157066/

October 25, 2005

     NUMA系統從設計上就有相對於特定節點(一組處理器)的本地內存。儘管全部的內存都是可訪問的,可是本地內存工做起來比遠程內存更快。內核考慮到NUMA的這種行爲會盡可能爲進程分配本地內存,同時儘可能避免在節點間移動進程。可是有時進程必須移動,從而致使本地分配優化反而變成了壞的。在這種狀況下,若是在進程被移到了一個新的節點時,進程的內存也能夠隨之移動那就行了。
     內存遷移補丁已經存在一段時間了。最新版是Christoph Lameter發佈的。這個補丁其實並無解決整個問題,不過它志在創建一個足夠最終的徹底的遷移方案被引入的基礎架構。
     這個補丁不會自動的爲被移動的進程遷移內存,而是把遷移的決定留給用戶空間。一個新的系統調用:

long migrate_pages(pid_t pid, unsigned long maxnode,
                       unsigned long *old_nodes,
                       unsigned long *new_nodes);

     這個調用會試圖把任何屬於給定進程的頁從old_nodes移到new_nodes。同時也有一個新的MPOL_MF_MOVE選項給set_mempolicy()系統調用,這會起到相同的左右。任何一種方法,用戶空間均可以請求爲給定進程釋放一批節點。這種操做能夠被執行以響應一個明確的進程移動自己(舉個例子,被一個系統調度守護進程移動),或響應其餘事件,好比一個節點即將關閉和移除。

     目前實現還比較簡單:代碼會重複掃描進程的內存並試圖強制每一個須要遷移的頁交換出去。當進程在頁上發生錯誤,而後相應頁就會被分配到目前進程所在的節點。強制換出進程實際上執行了一些流程,開始它略過被鎖定的頁只關注那些能夠容易釋放的頁。而後它等待被鎖定的頁並把最後的頁換出內存。
經過換出設備遷移頁不是在NUMA系統上最有效的方法。補丁的後期目標是增長直接節點到節點遷移,固然還有其餘特性。可是同時,開發者想把這個補丁合入2.6.15.可是Andrew Morton發表了一些保留意見:他想看到一個關於這個補丁如何可靠工做的解釋。有不少東西會阻止頁遷移,包括被用戶空間鎖住的頁,執行直接IO的頁,還有更多。Christoph響應說補丁最終會解決這些問題。這個聲明是否能說服從而合入2.6.15還有待觀察。

 

VM followup: page migration and fragmentation avoidance(part)

http://lwn.net/Articles/160201/

November 8, 2005

     頁遷移是把進程的頁從系統的一部分移到另外一部分。通常,在NUMA節點間移動頁的動機是但願提高性能。上次提到它是經過強制目標頁換出到swap設備工做的。當相應的進程在頁上發生錯誤,這些頁就會被換回到但願的節點上。這種技術能夠工做但不是最優的:避免把頁寫到磁盤而後再讀回來將會更好。
     Christoph Lameter帶來了直接遷移補丁,它把swap設備放到了一邊。讓咱們看看爲何這個補丁不首先使用這種方式,直接頁遷移不僅僅引入了複製數據。第一步,選定目標頁後,鎖定頁以防止其它人訪問它。頁可能正在執行IO,因此內核必須等待IO完成。而後真正的遷移纔開始執行。
     內核必須爲頁創建一個換出緩存,即便它想避免把頁寫入swap。若是進程在它正在被移動的過程當中發生錯誤,這個緩存就能夠保證一切正常。而後全部對這個頁的引用(頁表項)都取消映射。幸運的話,全部的引用都沒了,若是因爲某種緣由引用還在,頁就不能移動。
     事實上,移動一個頁包括複製一部分頁狀態,複製頁數據自己,而後複製剩餘的狀態。舊頁被清理並釋放。若是有任何寫回已經在新頁上排隊,它們就會被執行。而後就是簡單的清理,頁成功的移動了。
     若是內核在目標節點上用光了空閒頁,那就退回到基於swap的機制。因此補丁前期的階段仍然有用。
     使用這些代碼,內核能夠保持一個進程的頁在本地內存中。遷移代碼在熱插拔內存方面也證實是有用的,它須要指定範圍內全部的頁都必須被回收。事實上,這些代碼有一部分原本就是爲熱插拔應用而寫的。可是,從這點上,遷移有一個很好的基礎。對NUMA系統來講,移動頁失敗會致使更壞的性能,但沒什麼特別壞的地方。而對熱插拔內存,這種失敗會徹底阻礙一個內存刪除操做。100%的肯定能夠移動一個範圍的全部頁仍然是個很難的問題,目前尚未完整的方案。

 

 

Fragmentation 

 

Kswapd and high-order allocations

http://lwn.net/Articles/101230/

September 8, 2004

     內核裏的核心內存分配機制是基於頁的,它會試圖找到必定數量連續的頁以響應一個請求(這個地方的「必定數量」是指2的冪)。可是隨之系統運行了一段時間,對多個連續頁的更高階分配請求變得很難知足。虛擬內存系統碎片化了物理內存以致於空閒頁都相互分離了。
好奇的讀者能夠查詢/proc/buddyinfo看看目前空閒頁是多麼碎片化了。在一個1G的系統上,我看到:

Node 0, zone   Normal 258 9 5 0 1 2 0 1 1 0 0

     在這個系統上,258單個頁可當即分配,但只有9個連續的雙頁和5個4連續頁。若是須要不少高階分配的東西到來,可用內存很快就會用光,這些分配可能就會失敗。

     Nick Piggin最近看到了這個問題並發現有一個地方能夠作提高。問題在於kswapd進程,它在後臺運行併爲內存分配器提供空閒頁(經過釋放用戶頁)。目前的kswapd代碼只是注意可用空閒頁的數量,若是這個數目足夠高,kswapd就休息而無論這些頁是不是互相連續的。這會致使即便高階分配失敗,但系統也不打算作任何特別的努力來釋放更多的連續頁。
     Nick的補丁很直接,它只是簡單的阻止kswapd休息直到一個足夠數量的高階分配是可用的。
     可是有人指出kswapd使用的方法其實沒有真的改變:它選擇頁來釋放但沒考慮這些頁是否能夠合併成更大的組。結果,它可能不得不釋放不少頁才偶爾建立一些高階的頁組。在之前的內核,沒有更好的可用方法。但2.6內核包含翻轉映射代碼。使用翻轉映射,就有可能定位連續內存來釋放並在這方面大幅度提高系統性能。
     Linus反對這種方法由於它推翻了目前的頁替換策略,這種策略盡力釋放在不遠的未來不會使用的頁。改變這種策略來定位連續的塊會使高階分配更容易,但它一樣也會經過釋放有用的頁而對整個系統的性能不利。因此,Linus說,若是一個「反碎片化」模式必定要實現,那它應該不多運行並做爲一個獨立的進程。
     另外一個針對這個問題的方法是簡單的在一開始就避免高階分配。轉向4K的內核棧是這個方向上的進展,它消除了爲進程執行一個兩頁的分配。在目前的內核,高階分配最大的用戶之一是高性能網絡適配器驅動。這些適配器處理的大包不適合單個頁,因此內核必須執行多頁分配來保存這些包。
     實際上,這種分配只有在驅動(和它的硬件)不能處理分散在內存裏的「非線性」包的時候才須要。大多數現代硬件可執行分散/彙集DMA操做,因此不關心包是否是保存在單個的連續內存裏。可是,當向驅動寫入時使用硬件的分散/彙集功能不但須要額外的工做,而且對不少驅動來講,這些工做還沒完成。可是,從需求方面解決高階分配問題可能比給頁重聲明代碼增長另一個東西更有效率。

 

Active memory defragmentation

http://lwn.net/Articles/105021/

October 5, 2004

     在內核裏,高階分配是指試圖爲一個須要多於單個物理連續塊的應用獲取多個連續的頁。這種分配一直是內核的一個問題,一旦系統運行了一段時間,物理內存通常被碎片化從而幾乎沒有連續空閒頁存在。上個月,Nick Piggin嘗試對kswapd作修改來必定程度上緩解這個問題。可是,也有其餘人的方案。
     其中之一是Marcelo Tosatti,他發佈了一個給內核增長主動內存反碎片化的補丁。在一個高的級別上,它使用的算法相對簡單:爲得到N階空閒塊,首先找到最大的、比N略小的塊,而後試着重定位塊先後的頁的內容。若是有足夠的頁可被移動,一個更大的空閒頁塊就會被建立。
     天然地,這種方法仔細看的話會至關複雜。不是全部的頁時可重定位的,舉個例子,那些被鎖住或保留的,是不能動的。這個補丁也排除了正在寫回的頁,除非寫回IO完成,不然這些頁是不能移動的。不少更復雜的例子,好比移動屬於非線性映射的頁,這個補丁目前也不能處理。
     若是一個頁是不能重定位的,就必須先把它鎖住而後把它的內容複製到一個新頁上。而後全部引用舊頁的頁表必須從新指向新頁。翻轉映射信息,若是有的話,就必須被正確設置。若是在swap裏有這個頁的複製,那這個複製必須鏈接到新頁。諸如此類。Marcelo的補丁針對不少更復雜的狀況只是簡單地拒絕移動這些頁。即便如此,Marcelo報告了在建立大的連續空閒內存的好的結果。
     固然,有些小故障,包括在SMP系統上的問題。可是Marcelo說,不用擔憂:

可是在UP上它工做的很好,能夠很輕鬆的建立大的物理連續的內存區域。

     有人指出這個補丁跟另外一項工做有一些共同的特性:支持熱插拔內存。當內存要從系統中移除,目前存儲在這塊內存裏的全部的頁必須被重定位。本質上,熱插拔內存補丁尋找並建立正好覆蓋一片特定物理地址的一大塊空閒內存。

     Dave Hansen描述了增長熱插拔內存支持的兩個補丁--一個來自IBM,另外一個來自FUJITSU。每一個都有它的優勢和不足。
     在Marcelo和熱插拔補丁之間,有不少在移動頁來釋放大塊內存的經驗。在任何一個合入主線以前,把這些補丁的優勢合到一塊兒多是必須的。但最終的結果多是對高階分配問題的一個終結。

 

Yet another approach to memory fragmentation

http://lwn.net/Articles/121618/

February 1, 2005

     不少開發者嘗試解決內存碎片化和在內核中分配大的連續的內存塊的問題。最近的方法包括Marcelo Tosatti的主動反碎片化和Nick Piggin的kswapd提高。如今Mel Gorman採用不一樣的方法加入爭論。
     在很高的級別上,內核以下圖的方式管理空閒頁。


     系統的物理內存被分紅區:在x86系統上,這些區包括可被ISA設備訪問的小的空間(ZONE_DMA),正常內存區(ZONE_NORMAL)和內核非直接訪問的內存(ZONE_HIGHMEM)。NUMA系統經過爲每一個節點建立區來進一步區分。每一個節點裏,內存被分紅塊(chunks)並按「階數」(order)排列--每一個塊的大小的基於2算法。對每個階數都有一個這種大小的可用的塊的鏈表。因此,這個數組的底部,階數0鏈表包含單個頁,階數1鏈表包含雙頁,等等,直到系統的最大階數。當一個給定階數的分配申請到來時,一個塊會從相應的鏈表上取下。若是這種大小的塊沒有了,一個更大的塊會被拆分。當塊被釋放了,夥伴分配器試着把它們跟它們附近的塊合併來建立更高階的塊。
     在現實中的linux系統,隨着時間流逝,較大的塊趨於被拆分以致於較大的分配變得很難。在一個運行的系統上能夠經過/proc/buddyinfo看到不少0階的頁可用,但相對少的更大的塊。爲此,高階分配在一個運行了一段時間的系統上更可能會失敗。
     Mel的方法是把內存分配劃分爲三種,用新增長的GFP_標誌來標明,這些標誌能夠在內存請求的時候提供。帶__GFP_USERRCLM標誌的內存分配用於用戶空間,且很容易重聲明。通常,重聲明一個用戶空間的頁就是把它寫到後備存儲(若是它已經被修改了)。__GFP_KERNRCLM標誌用在可重聲明的內核內存,好比從slab中獲取的和用在可按需釋放的緩存裏的。最後,未被標明的分配是被認爲用簡單的方法沒法重聲明的。
     而後,夥伴分配器的數據結構被擴展成這樣:


     當分配器被初始化,全部未經使用的內存還沒碎片化,free_area_global域指向一個長的最大大小塊內存的鏈表。三個free_area數據--每種分配類型一個--一開始是空的。每一個分配請求會在相應的可用free_area數組獲得知足,不然,一個來自free_area_global的MAX_ORDER塊就會被拆分。塊未被分配的部分會被放到跟目前分配類型相同的數組裏。
     當內存被釋放而且塊被合併,它們仍然留在相應類型數組裏直到達到最大大小,而後被放回到全局數據裏。
     這種組織的一個直接好處是最難回收的頁--那些內核不可重聲明的--被組織到本身的塊裏。一個釘住的頁就能阻止一個大塊的合併,因此,彙集這些難的內核也讓剩餘內存的管理更容易。除了這個,這種組織讓主動頁釋放變得可能。若是一個高階請求不能被知足,只是簡單的從一個較小的塊並釋放其附近的頁。但主動釋放還沒有在Mel當前的補丁裏實現。
即便沒有主動釋放,這個補丁也幫助了內核知足大的分配。Mel給出了他跑的測試結果。沒打補丁,160次10階分配申請裏成功3次,打補丁後,81次成功。因此,新的分配技術和數據結構確實幫助了這種狀況。可是,接下來發生的還有待觀察,要想讓這個補丁合入主線貌似尚有一個很大的障礙須要克服。

 

Fragmentation avoidance

http://lwn.net/Articles/158211/

November 2, 2005

     Mel Gorman的避免碎片化補丁把全部的內存分配劃分爲三類:「用戶可重聲明」,「內核可重聲明」和「內核不可重聲明」。這個主意是經過把可重聲明分配分到一塊兒來支持多個連續頁分配。若是沒有連續內存可用,能夠經過強制換出可重聲明頁建立一個。因爲不可重聲明頁都被分到了它們本身的地方,一個這種頁阻礙一個連續空閒頁建立的概率相對很小。
     Mel最近發佈了第19版避免碎片化補丁並請求合入-mm內核。這個請求引發了關於這個補丁是不是好主意的大討論。對這部分代碼是否屬於內核好像還存在至關多的不肯定。有一些須要避免碎片化的緣由,在每一個緣由上的爭論也不一樣。
     這些緣由的第一個是它增長了內核中高階分配的可能性。沒人否定Mel的補丁達到了這個目的,但有些開發者聲明說一個更好的方法是簡單的消除這種分配。事實上,大多數多頁分配在之前被處理了。但有一些仍存在,包括兩頁的內核棧仍然在大多數系統上被默認使用。當內核棧分配失敗,它會阻塞新進程的建立。內核可能最終在全部狀況下都轉而使用單頁棧,但一些高階分配仍會遺留。把請求的內存分紅單頁的塊也不是總會成功。
     下一個緣由,跟第一個有很大的關聯,就是大頁。大頁機制被用來在大型系統上爲某些應用提高性能,目前只有不多用戶,可是若是大頁很容易工做的話狀況可能會改變。大頁不能在缺少大的且適當對齊的連續內存的系統上分配。事實上,它們很難在運行了一段時間的系統上建立。分配大頁失敗相對來講沒多大事,應用程序只需簡單的用常規頁並承受性能打擊。可是,若是你有一個大頁機制,讓它更可靠的工做是很值得的。
     避免碎片化補丁對高階分配和大頁都有幫助。可是存在它是否是這個問題的正確方法的爭論。常常被討論的可選方法是爲可重聲明內存建立一個或更多新的內存區。這種方法會用到已內建在內核裏的區系統,因此避免了一個新層的建立。一個基於區的系統也可能會避免潛在的避免碎片化補丁帶來的性能影響(儘管未經證明)。考慮到這種影響出如今很關鍵的應用場景--內核編譯--這個爭論在內核開發者中引發了共鳴。
     可是基於區的方法並非沒有問題。目前的內存區是靜態的,結果,人們不得不決定怎麼劃份內存爲可重聲明和不可重聲明的。這種調整看起來很難用可靠的方法實現。在過去,區系統也是不少性能問題的來源,大多數與區以前的分配平衡相關。增長區系統的複雜性和增長區極可能讓這些問題重現。
     另外一個避免碎片化的動機是帶來不一樣限制的對熱插拔內存的支持。這種特性在高可用系統上有用,但它同時在虛擬化領域用的不少。一個跑了不少虛擬的linux實例的主機能夠經過熱插拔的方式在不一樣實例之間轉移內存資源以知足各個實例的要求。
     在內存能夠從一個運行着的系統從移除以前。它的內容必須被移到別處--至少,若是你還想你的系統能繼續運行的話。避免碎片化補丁能經過只把可重聲明的分配放到可能會移除的內存上來起到幫助。只要一個區域裏的全部頁均可以被重聲明,那這個區域就是可移動的。
     一個很不一樣的爭論出現了:Ingo Molnar強調任何聲明支持熱插拔內存的機制都要保證100%的成功率。目前的代碼沒必要按照這個標準,可是須要一個照着這個目標的清晰的路徑。不然,內核開發者就是在冒險推廣一個還沒提供可靠支持的特性。避免碎片化的支持者但願合併補丁以解決90%的問題,把剩餘的90%留到之後(評論:這是內核特性有技巧的東西,對一個解決方法來講,第二個90%通常比第一個90%更難,有時甚至有第三個90%須要考慮。)。然而,Ingo擔憂第二個90%,想知道怎麼實現它。
     爲何目前的補丁不能提供100%的可靠性若是隻把可重聲明內存放在可熱插拔區域?有可能鎖定已經被標明可重聲明的頁,這包括DMA操做和被用戶空間明確鎖住的頁。也有可能在內核用光不可重聲明內存時出現問題。不是讓一個不可重聲明的分配失敗,內核會在可重聲明區域分配頁。這種回退在防止破壞內核其餘方面的可靠性方面是必須的。 可是在一個可重聲明區域裏的一個不可重聲明頁會阻止系統清空這塊區域。
     這個問題能夠經過徹底放棄不可重聲明分配來解決。這能夠經過改變內核地址空間工做方式來作。目前,內核運行在一個單獨的連續的虛擬地址空間,它是直接映射到物理內存的--常用一個單獨的大頁表項。(vmalloc()是例外,不在討論之列)。若是內核使用就像系統其餘地方使用的常規大小的頁,它的內存就再也不須要是物理連續的了。而後,若是一個內核頁阻礙了,它就能夠簡單的被移動一個更方便的地方。
     這種方法除了在根本上改變了內核的內存模型外,還有兩個小問題。更高的轉換緩存使用致使的性能破壞和爲儲存內核頁表的內存數量的增長。某些內核操做--特別是DMA--不能容忍物理地址可能在任意時間發生改變。因此不得不增長一個新的API,驅動能夠用它來申請物理上釘住的區域,並被內核告知把它們釋放。換句話說,破壞內核的地址空間會帶來大量的蠕蟲障礙。若是沒有強烈的動機是不會被接受的,熱插拔內存還不是一個足夠引人注目的理由。
     因此對避免碎片化尚未最終的結論。可是近期看,Andrew Morton的避免爭論機制可能會阻止補丁進入-mm樹。可是對這種能力的渴望是有正當理由的,問題也不會消失。除非有人提出更好的方法,否則很難永遠不合並Mel的補丁。

 

More on fragmentation avoidance

http://lwn.net/Articles/159110/

November 8, 2005

     上週關於避免碎片化的文章總結以下:

 可是對這種能力的渴望是有正當理由的,問題也不會消失。除非有人提出更好的方法,否則很難永遠不合並Mel的補丁。

     可是一個能阻止補丁進入內核的事情是Linus的反對,它就發生在這個補丁上。他認爲避免碎片化是「徹底沒用的」並總結說:

 不要這樣作。咱們從沒這樣作,咱們一直很好。

     根據Linus的想法,正確的方法是在有釋放大的,連續內存的需求的系統(不多)上建立一個特殊的內存區。在這個區裏不能進行內核內存分配,因此它只包含用戶空間頁。這些頁在須要的時候相對容易移動,因此大部分需求會被知足。會須要一些內核調優,但這是爲運行高特殊化應用程序的代價。

     這種方法不被全部的人接受。Andi Kleen提醒:     

 一個負載用光了內核可分配頁時你有兩個選擇。要麼使用可重聲明區或讓分配失敗。第一個意味着大頁之類的是不可靠的,第二個意味着全部的關於受限lowmem的問題將會復現。

     其餘人則提醒爲全部類型負載來調優一個機器是很難的,特別是在有不少用戶的系統上。儘管有反對,看起來主動避免碎片化不會很快進入2.6內核。

 

VM followup: page migration and fragmentation avoidance(part)

http://lwn.net/Articles/160201/

November 8, 2005

     這種方法的其中之一多是主動內存反碎片化,它不在可能會移除的內存區域中執行不可移動的內存分配。當咱們上週看主動反碎片化時,這個補丁看起來有麻煩了。反碎片代碼的負載可能過高,並且還有不少開發者(包括Linus)感受這種功能應該用內核的區系統來實現,而不是在內存分配器中增長一個新的層。
     可是反碎片化的做者Mel Gorman不會輕易放棄。他發表了一個新的反碎片化補丁的輕量級版本,他但願它會更容易被接受。就像他描述的:     

   這是一個簡化了的反碎片方法,它簡單的試着讓內核分配以2^(MAX_ORDER-1)爲組和容易重聲明的分配以2^(MAX_ORDER-1)爲組。它不使用起平衡做用的可調整的特殊保留內存,也沒在主路徑裏引入新的分支。對小內存系統來講,它能夠經過一個配置選項關掉。它一共增長了275行代碼同時只對主路徑作了最小的改動。

     這個版本的補丁增長了一個新的GFP標誌(__GFP_EASYRCLM),它的存在標明一個在須要的時候會很容易的被內核回收的分配。它用於用戶空間頁(通常可強制換出到後備存儲中)和一些其餘的場合,好比一些內核緩存(buffer)。夥伴分配器已經能夠跟蹤成塊的內存,新的代碼簡單的控制在一些塊上的可重聲明分配,同時把不可重申明分配放在其餘塊上。它但願經過這種方法解決一個不可移動的頁阻塞大的連續區域的釋放的問題。

     這補丁經過建立一個跟蹤每一個大塊內存上執行那種分配的「usemap」數組來工做。Mel也不得不拆分用來執行快速單頁分配的per-CPU空閒鏈表,如今有兩個這樣的鏈表,每種分配類型一個。而後就只是依靠__GFP_EASYRCLM標誌在適當的內存裏執行分配了。
     這個版本固然減小了反碎片化補丁的足跡和負載。但還不是其餘一些人但願的基於區的方法。因此主動反碎片化是否比它的前者更易被接受還有待觀察。

 

Avoiding - and fixing - memory fragmentation

http://lwn.net/Articles/211505/

November 28, 2006

     內存碎片化是一個好久的內核編程問題。隨着系統運行,頁分配給各個進程從而致使內存碎片化。一個運行了很長時間的繁忙的系統可能只有不多的物理上連續的頁了。因爲linux是虛擬內存系統,碎片化通常也不是問題,物理上分散的內存能夠經過頁表實現虛擬的連續。
     可是存在一些須要物理上連續內存的場合。這包括大的內核數據結構(除了經過vmalloc()分配的)和任何須須爲外部設備保持連續的內存。若是一個大的(高階)內存塊在須要的時候不可用,一些東西就會失敗,用戶也開始考慮轉向BSD。
     幾年以來,你們考慮了不少針對內存碎片化的方法,但尚未一個被合併。給核心內存管理代碼增長任何負載都很難被接受。可是這並不意味着人們中止了嘗試。在這個領域最持久的人之一是Mel Gorman,他已經在一個反碎片化補丁上工做了好幾年。Mel帶着他的補丁的27版回來了,從新命名爲「頁集羣化」。這個版本的補丁吸引了一些興趣並可能合入主線。
     Mel補丁的核心內容仍然是一些類型的內存比另外一些更容易重聲明。好比,一個爲文件系統作緩存的頁是準備好被刪掉和被從新使用的,而一個保存一個進程的task數據結構的頁則被釘住了。一個頑固的頁就能阻止一整個大內存塊被用做物理連續的總體。可是若是全部容易重聲明的頁被放在一塊兒,不可重聲明的頁被放在另外一個單獨的內存區域,建立一個更大的空閒內存塊就更容易了。
     因此Mel的補丁把每一個內存區分紅了三類內存塊:不可重聲明的,容易重聲明的和可移動的。「可移動的」是這個補丁裏的一個新的特性,它被用在那些使用內核的頁遷移機制就能夠容易的轉移到其餘地方的頁。不少狀況下,移動一個頁可能比重聲明它更容易,由於不須要用到後備存儲設備。經過這種方式給頁分組頁可使更大的塊的建立發生在當一個進程從一個NUMA節點遷移到另外一個時。
     因此,這個補丁裏,可移動的頁(使用__GFP_MOVABLE標記的)通常是那些屬於用戶空間進程的頁。移動一個用戶空間頁只是複製數據和修改頁表項。因此相對容易。而可重聲明頁(使用__GFP_RECLAIMABLE標記)則通常屬於內核。它們要麼是短暫存在(好比,一些只在IO操做期間存在的DMA緩存)的分配或者是在須要的時候被刪掉(各類緩存)的分配。其餘任何東西都被認爲是很難重聲明的。
     經過簡單的用這種方式把不一樣種類的分配分組,Mel獲得了一些好的結果:     

在基準和壓力測試裏,咱們能夠在測試後找到80%的內存用於連續的塊。做爲比較,一個桌面系統上的標準內核能夠獲得小於1%的可用做大頁的內存和在壓力測試結束後大約8-12%的內存用做大頁。

     在過去,Linus一直反對減小內存碎片化的努力。但此次他的評論更注重細節:分配默認是可移動的仍是不可移動的?答案應該是「不可移動的」,由於有人老是不得不爲了確保一個特定的分配是可移動的而作一些努力。因爲討論已經到了這個程度,一些避免碎片化補丁可能找到了進入內核的方法。

     針對碎片化的一個相關的方法是Andy Whitcroft發表的但最初是Peter Zijlstra的「塊狀重聲明」機制。linux裏的內存重聲明通常是經過一個最少-最近-使用(LRU)鏈表,想法是,若是須要刪除一個頁,用最少最近使用的頁會最小化扔掉一個很快就用到的頁的概率。可是這種機制會釋放隨機分散在物理地址空間的頁,讓建立更大的空閒內存快更難。
塊狀重聲明補丁嘗試經過輕微的修改LRU算法來解決這個問題。當須要內存時,仍是像之前同樣從LRU鏈表上選擇下一個目標。而後重聲明代碼會觀察目標附近的頁(它們足夠構成一個更高階的塊)並同時試着釋放它們。若是成功了,塊狀重聲明會很快建立一個更大的空閒塊的同時重聲明最小數量的頁。
     很明顯,這種方法在附近的頁能夠被釋放的狀況下會工做的更好。結果顯示,它跟一個集羣化機制,好比像Mel Gorman那樣的,結合的很好。LRU變形的方法會有性能影響,由於附近的頁在塊狀重聲明代碼運行的時候可能正處在重負荷下。一個最小化這種影響的方法是塊狀重聲明代碼只在內核知足大塊內存申請有困難時才執行。
     是否--什麼時候--這些補丁被合入還有待觀察。核心內存管理補丁須要很當心,它們很容易在暴露給現實負載時致使混亂。可是問題不會本身消失,因此有些東西早晚會發生。

  

Short topics in memory management

http://lwn.net/Articles/224829/

March 6, 2007

     內存管理在2.6.x版本內核中是個相對平靜的話題。不少最壞的問題已經解決了,內存管理的開發者繼續作其餘事情了。可是這並不意味着沒有問題了,事實上,有些問題將要熱起來了。一些最近的討論揭露了一些可能致使在不遠的未來引發新的興趣的壓力。
     Mel Gorman的避免碎片化補丁在過去已經討論好幾回了。Mel的核心思想是區分哪些能夠容易移動或重聲明的頁並把它們分組。可移動的頁包括那些分配給用戶空間的頁,移動它們只須要修改相關的頁表項。可重聲明頁包括能夠按需釋放的內核緩存。把它們放在一塊兒讓內核釋放大的內存塊更容易,這會對使能高階分配或清空整個內存區域都有用。
     在過去,Mel補丁的審覈者在它工做方式上有不一樣意見。一些人支持爲不一樣的分配類型維護單獨的空閒鏈表,而其餘的感受這種內存分區的方法正是內核的區系統的建立目的。因此此次,Mel發佈了兩個補丁:基於鏈表的分組機制和一個僅限於可移動分配的新的ZONE_MOVABLE區。
     此次的不一樣是這兩個補丁是設計一塊兒工做的。默認的,沒有可移動區,因此基於鏈表的機制處理把類似分配放在一塊兒的所有工做。管理員能夠在啓動的時候用kernelcore=選項配置ZONE_MOVABLE,它用來指定*不*放在這個區裏的內存的數量。另外,Mel發佈了一些關於這些補丁是怎麼影響性能的有理解力的信息。用不一樣尋常的方法,Mel使用了一些視頻,這些視頻顯示了內存分配是如何響應使用不一樣分配機制的系統壓力的。演示是有說服力的,可是爲了在未來進入內核而建立多媒體演示是沒必要要的。
     這些補丁找到了它們進入-mm樹的方法,但Andrew Morton仍然不清楚它們是否有價值的。另外,他還擔憂它們與其餘相關工做的適應狀況,特別是內存熱拔除和每容器內存限制。儘管解決這兩個問題的補丁已經發布了,但都還沒達到合入內核的程度。
     Mel的工做很明顯對內存的熱移除是有幫助的--可能被移除的內存能夠被限於可移動的和可重聲明的分配,這會運行它在須要的時候被清空。但不是全部人都認爲熱拔除是一個有用的特性。特別是Linus反對這種想法。熱拔除最大的潛在用途是在虛擬化,它容許一個管理器能夠在客戶端在需求改變的時候移動內存資源。Linus指出大多數虛擬化機制已經擁有容許客戶端增長和刪除單個頁的機制,因此,他說,不必再爲內存變更提供其餘支持。
     這種技術的另外一個用途是容許系統經過在一些內存不使用的時候把它們關掉來保存電量。很明顯,必須在關閉以前把全部有用的數據移出內存。Linus更加不一樣意這種作法:

整個DRAM電源的故事就是一個給容易受騙的孩子們的睡前故事。不要陷進去。它是不真實的。支持它的硬件如今還不存在,或許幾年後也不存在。真正的修復在其餘地方...

     簡短說來,Linus認爲關掉整個NUMA節點比單獨的內存更行得通。儘管如此,Mark Gross發佈了一個包含一些基本的反碎片化技術的使能內存掉電的補丁。Mark說:

內存電源管理是沒用的除非你有利用它的工做環境。驗證過的工做環境不是桌面環境。可是,有一些感興趣的用戶有一些合適的工做環境,這些工做環境讓使能內存掉電補丁進入社區是值得的。這些工做環境通常在用內存利用率來跟蹤流量負載的網絡元素和服務器裏。

     有人也建議常駐集合大小(resident set size)限制(通常跟容器聯繫在一塊兒)能夠解決不少反碎片化工做想要解決的問題。Rik van Riel迴應抱怨說RSS限制會加劇目前內存管理系統正在經歷的可伸縮性問題。這引發了之前不知道這些問題的人的--好比Andrew--疑問。Rik用一些相對模糊的例子迴應,很明顯,他的特定的行爲受限於經歷這些問題的用戶的協議。

     這致使了關於在沒有測試實例來驗證內存管理問題的狀況下解決這些問題是否說的通的大討論。Rik爭論說修復測試實例會破壞現實世界的東西。Andrew迴應說:

我不相信一個沒能力提供哪怕一個簡單的測試實例的人或組織會有能力在不破壞東西的狀況下修復這樣的問題。

Rik爲讓討論繼續提供了一些有問題的工做場景。

     Andrew的觀點之一是在內核裏修復由特定場景引發的內存管理問題老是很難,內核不老是有信息來知道哪些頁會很快被用到和哪些會被刪掉。可能,他說,正確的方法是讓用戶空間更容易的聯繫它未來但願的需求。爲了這個目的,他爲測試提出了一個頁緩存管理工具。它做爲一個LD_PRELOAD庫來工做,截取文件相關的系統調用,跟蹤應用程序使用量並通知內核在使用後從緩存裏扔掉頁。結果是通常操做(好比複製內核樹)能夠在不強制其餘有用數據換出頁緩存的狀況下被實現。
     有一些對這個的有懷疑的回覆。也存在一些關於怎麼引入更智能的、特定應用的策略到這個工具的興趣和討論。好比,一個可能的備份工具策略會強制輸出文件當即刷出內存,跟蹤從其餘文件讀出的頁並強制它們刷出--但它們必須尚未在頁緩存裏,等等。是否有人會用這個工具解決真實的工做環境問題還有待觀察,可是有潛力。內核不老是什麼都知道的。

 

(轉載本站文章請註明做者和出處 http://www.cnblogs.com/baiyw/,請勿用於任何商業用途)

相關文章
相關標籤/搜索