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