linux内存管理笔记(四十二)----内存规整
伙伴系統是以頁面為單位管理內存,內存碎片也是基于頁面,即由大量離散且不連續的頁面組成。從內核的角度,出現內存碎片不是什么好的事情,例如
- 有些情況下物理設備需要大量的連續的物理內存,如果內核無法滿足,就會發生內核錯誤
所以內核對于內存碎片化,需要重新規劃調整,因此就出現了本章的主題,內存規整技術,它是為了解決內存碎片化而出現的。本章主要是了解如下內容
- 內存規整的基本原理
- 如何觸發內存規整
- 內存規整的使用方法
- 內存碎片的優化方法
1. 內存規整的基本原理
Linux使用的是虛擬地址,以提供進程地址空間的隔離。它還帶來一個好處,就是像vmalloc這種分配,不用太在乎實際使用的物理內存的分配是否連續,因此也就弱化了物理內存才會面臨的內存碎片化問題。但是如果使用的時kmalloc,則要求物理內存必須連續,系統中空閑內存的總量(比如空閑時10個page),大于申請的內存大小,但是沒有連續的物理內存,我們就可以通過migrate(遷移/移動)空閑的page frame,來聚合形成滿足需求的連續的物理內存。
多年來,內核開發人員已經做出各種嘗試來緩解這個問題;這些嘗試包括[定義新的域 ZONE_MOVABLE和引入 塊狀回收(lumpy reclaim這樣的技術。但是,光有這些還不夠,特別是在解決內存碎片以及生成更大的內存塊方面。在該領域淡出了一段時間的 Mel Gorman 最近又回來了,并帶來了一個新的補丁用于實現 內存規整(memory compaction)。在這里,給大家簡短介紹一下這個補丁的工作原理。
假設存在一個非常小的內存域(zone),如下圖所示:
頁框為白色表示空閑,而紅色的是由于某種用途被分配了的頁框。可以看出,該域(zone)中的空閑頁框非常分散,沒有大于兩頁的連續內存塊;如果要從該域中分配包含連續四頁的內存塊必將失敗。實際上,即便是分配包含兩頁連續的內存也會失敗,因為所有連續兩頁的內存塊都不滿足伙伴系統對內存分配的對齊要求。
下面來演示一下規整(compaction)算法的工作原理。代碼中會運行兩個獨立的掃描;第一個掃描從域的底部(bottom)開始(如下圖所示從左往右進行掃描),一邊掃描一邊將可以移動(movable)的頁框記錄到一個列表中:
時,在區域的頂部(top),另一個掃描(如下圖所示從右往左)創建另一個列表,用于記錄可作為頁框遷移目標的空閑頁框位置:
最終,兩個掃描會在域中間的某個位置相遇(意味著掃描結束)。此時,剩下的工作主要是調用 頁面遷移(page migration)功能(從這里我們可以看到頁面遷移的功能已經不僅僅只針對 NUMA 系統)將左邊掃描得到的已分配的頁框上的內容轉移到右邊空閑的空間中,產生的結果如下如下所示,規整后的內存看上去是不是很整齊?
現在我們得到了一個擁有大小為 8 頁并且連續的可用空間,可用于滿足更 “高階” 的內存分配。當然,這里展示的流程和真實系統比起來已經大大簡化了。特別地,實際的內存域會大得多;這意味著掃描的工作量也會大很多,但由此獲得的空閑區也可能更大。
對于內存規整技術,其核心的思想是把內存頁面按照可移動、可回收、不可移動等特性進行分類
- 可移動的頁面:是指用戶程序分配的內存,移動這些頁面僅僅是需要修改頁表映射關系,代價很低
- 可回收的頁面:是指不可以移動但可以釋放的內存
- 不可移動的頁面:目前的內核使用的物理頁面
所以Linux物理頁面規整機制,類似于磁盤整理,主要是應用了內核的頁面遷移機制,是一種將可移動頁面進行遷移后騰出連續物理內存的方法。
2. 觸發內存規整
Linux內核中觸發內存規整途徑有3個途徑
- 手動觸發:通過寫1到/proc/sys/vm/compact_memory節點,會手動內存規整。它會掃面系統中所有的內存節點上的zone,對每個zone都會做一次內存規整。
- kcompactd內核線程:和頁面回收kswapd內核線程一樣,每個內存節點都會創建一個kcompactd內核線程,名稱為"kcompactd0",“kcompactd1"等。
- 直接內存規整:和頁面回收一樣,當頁面分配器發現在低水位的情況下無法滿足頁面分配時,會進入慢速路徑,在慢速路徑中,除了喚醒kswapd內核線程外,還會調用函數__alloc_pages_direct_compact(),嘗試整合出一大塊空閑內存。
3. 內存規整的使用方法
如果想打開內存規整,內核需要打開相關的配置(默認為y)
3.1 直接內存規整
進程分配內存時發現內存不足從而啟動直接回收內存操作,這種模式下分配和回收是同步的關系,也就是說分配內存的進程會因為等待內存回收而被阻塞。
內存規整的一個重要應用場景是分配大塊連續物理內存,低水位情況下分配失敗時喚醒kswapd內核線程,但依然無法分配出內存,因此調用__alloc_pages_direct_compact,其處理流程如下所示
3.2 kcompactd內核線程
kcompactd是一個內核規整的后臺進程,它跟memory compaction的區別在于:
-
memory compaction的觸發途徑是內存分配進入direct_reclaim(暫不分析costly_order情況)后系統會根據內存剩余判斷是否觸發內存規整,或者用戶手動觸發;
-
kcompactd在喚醒kswapd或者kswapd進入休眠時,主動觸發內存規整。
kcompactd的觸發路徑如下:主要有如下兩個途徑:
- 喚醒kswapd之前觸發規整,觸發的條件是:本次分配不支持direct_reclaim,node內存節點是平衡的,并且kswapd失敗的次數大于MAX_RECLAIM_RETRIES(默認16)。
- kswapd即將進入睡眠時:
4. 使用局限
由于要保持虛擬地址不變,像kernel space中線性映射的這部分物理內存就不能被migrate,因為線性映射中虛擬地址和物理地址之間是固定的偏移,migration導致的物理地址更改勢必也造成虛擬地址的變化。也就是說,并非所有的page都是“可移動”的,這也是為什么buddy系統中劃分了"migrate type"。
5. 優化方法
內核經過不斷的優化,那為何Linux為何還有物理內存外碎片化呢?那是因為物理內存外碎片化雖然是可以不斷優化的,但卻無法得到根除。目前的內核,我覺得導致物理外碎片化還有以下兩個主要原因:
-
不可移動頁面污染了內存環境,導致頁面規整失敗;
-
隨著系統不斷申請和釋放的頁面,導致伙伴系統分配的物理內存頁幀號越發隨機,從而導致內存被隔斷的概率越高,碎片化的程度越高
針對以上兩個原因,以下的優化措施可能達到一定的優化效果:
- 預留法
根據這種情況,可以通過預留的方式進行相應的優化。
-
預留一定的內存專門用于小塊內存分配,經過這個優化措施后,可以有效降低小塊內存分配的物理頁幀號的隨機性,從而降低小塊內存污染內存環境的概率。
-
預留一定的內存專門用于大塊內存分配,經過該優化措施后,預留的內存被小塊內存污染的概率就會降低,可以提升預留內存分配大塊內存的成功率。
目前內核使用CMA和reserve memory的方法可以解決這個問題,CMA也會預留一塊內存區域,但在DMA設備不使用這段內存的時候,它也可以被OS的其他模塊所使用。而在DMA設備真正需要的時候,可以對其他模塊使用的page frame做migration操作,以騰出CMA區域中的空間。
6. 參考文檔
Linux中的Memory Compaction [一]
Linux 物理內存外碎片化淺析
總結
以上是生活随笔為你收集整理的linux内存管理笔记(四十二)----内存规整的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PDApp.log占用C盘几十G空间,原
- 下一篇: 用VSCODE看linux内核代码