物理內存的分配

物理內存的分配在內核分配內存時,必須記錄頁幀的已分配或空閒狀態,以免兩個進程使用同樣的內存區域。由於內存分配和釋放非常頻繁,內核還必須保證相關操作儘快完成。內核可以只分配完整的頁幀。將內存劃分爲更小的部分的工作,則委託給用戶空間中的標準庫。標準庫將來源於內核的頁幀拆分爲小的區域,併爲進程分配內存。
1. 夥伴系統
內核中很多時候要求分配連續頁。爲快速檢測內存中的連續區域,內核採用了一種古老而歷經檢驗的技術:夥伴系統。
系統中的空閒內存塊總是兩兩分組,每組中的兩個內存塊稱作夥伴。夥伴的分配可以是彼此獨立的。但如果兩個夥伴都是空閒的,內核會將其合併爲一個更大的內存塊,作爲下一層次上某個內存塊的夥伴。
內核對所有大小相同的夥伴(1、2、4、8、16或其他數目的頁),都放置到同一個列表中管理。各有8頁的一對夥伴也在相應的列表中。
如果系統現在需要8個頁幀,則將16個頁幀組成的塊拆分爲兩個夥伴。其中一塊用於滿足應用程序的請求,而剩餘的8個頁幀則放置到對應8頁大小內存塊的列表中。
如果下一個請求只需要2個連續頁幀,則由8頁組成的塊會分裂成2個夥伴,每個包含4個頁幀。其中一塊放置回夥伴列表中,而另一個再次分裂成2個夥伴,每個包含2頁。其中一個回到夥伴系統,另一個則傳遞給應用程序。
在應用程序釋放內存時,內核可以直接檢查地址,來判斷是否能夠創建一組夥伴,併合併爲一個更大的內存塊放回到夥伴列表中,這剛好是內存塊分裂的逆過程。這提高了較大內存塊可用的可能性。
在系統長期運行時,服務器運行幾個星期乃至幾個月是很正常的,許多桌面系統也趨向於長期開機運行,那麼會發生稱爲碎片的內存管理問題。頻繁的分配和釋放頁幀可能導致一種情況:系統中有若干頁幀是空閒的,但卻散佈在物理地址空間的各處。換句話說,系統中缺乏連續頁幀組成的較大的內存塊,而從性能上考慮,卻又很需要使用較大的連續內存塊。通過夥伴系統可以在某種程度上減少這種效應,但無法完全消除。如果在大塊的連續內存中間剛好有一個頁幀分配出去,很顯然這兩塊空閒的內存是無法合併的。在內核版本2.6.24開發期間,增加了一些有效措施來防止內存碎片,我會在第3章更詳細地討論相關的底層實現機制。
2. slab緩存
內核本身經常需要比完整頁幀小得多的內存塊。由於內核無法使用標準庫的函數,因而必須在夥伴系統基礎上自行定義額外的內存管理層,將夥伴系統提供的頁劃分爲更小的部分。該方法不僅可以分配內存,還爲頻繁使用的小對象實現了一個一般性的緩存--slab緩存。它可以用兩種方法分配內存。
(1) 對頻繁使用的對象,內核定義了只包含了所需類型對象實例的緩存。每次需要某種對象時,可以從對應的緩存快速分配(使用後釋放到緩存)。slab緩存自動維護與夥伴系統的交互,在緩存用盡時會請求新的頁幀。
(2) 對通常情況下小內存塊的分配,內核針對不同大小的對象定義了一組slab緩存,可以像用戶空間編程一樣,用相同的函數訪問這些緩存。不同之處是這些函數都增加了前綴k,表明是與內核相關聯的:kmalloc和kfree。
雖然slab分配器在各種工作負荷下的性能都很好,但在真正規模龐大的超級計算機上使用時,出現了一些可伸縮性問題。另一方面,對真正微小的嵌入式系統來說,slab分配器的開銷可能又太大了。內核提供了slab分配器的兩種備選方案,可用於在相應的場景下替換slab分配器並提供更好的性能。對內核的其他部分而言,這3種方案的接口相同,因而不必關注內核中實際編譯進來的底層分配器是哪一個。由於slab分配仍然是內核的標準方法,因此我不會詳細討論備選方案。圖1-9綜述了夥伴系統、slab分配器以及內核其他方面之間的關聯。
3. 頁面交換和頁面回收
頁面交換通過利用磁盤空間作爲擴展內存,從而增大了可用的內存。在內核需要更多內存時,不經常使用的頁可以寫入硬盤。如果再需要訪問相關數據,內核會將相應的頁切換回內存。通過缺頁異常機制,這種切換操作對應用程序是透明的。換出的頁可以通過特別的頁表項標識。在進程試圖訪問此類頁幀時,CPU則啓動一個可以被內核截取的缺頁異常。此時內核可以將硬盤上的數據切換到內存中。接下來用戶進程可以恢復運行。由於進程無法感知到缺頁異常,所以頁的換入和換出對進程是完全不可見的。
頁面回收用於將內存映射被修改的內容與底層的塊設備同步,爲此有時也簡稱爲數據回寫。數據刷出後,內核即可將頁幀用於其他用途(類似於頁面交換)。內核的數據結構包含了與此相關的所有信息,當再次需要該數據時,可根據相關信息從硬盤找到相應的數據並加載。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章