|
內(nèi)存運行機(jī)制 1)linux系統(tǒng)會不時地進(jìn)行頁面交換操作,保持盡可能多的空閑物理內(nèi)存,即使并不需要內(nèi)存,linux會交換出暫時不用的內(nèi)存頁面。 2)內(nèi)核根據(jù)“最近最經(jīng)常使用”算法,僅僅將一些不經(jīng)常使用的頁面文件交換到虛擬內(nèi)存。有時我們會看到這么一個現(xiàn)象:Linux物理內(nèi)存還有很多,但是交換空間也使用了很多。其實,這并不奇怪,例如,一個占用很大內(nèi)存的進(jìn)程運行時,需要耗費很多內(nèi)存資源,此時就會有一些不常用頁面文件被交換到虛擬內(nèi)存中,但后來這個占用很多內(nèi)存資源的進(jìn)程結(jié)束并釋放了很多內(nèi)存時,剛才被交換出去的頁面文件并不會自動的交換進(jìn)物理內(nèi)存,除非有這個必要,那么此刻系統(tǒng)物理內(nèi)存就會空閑很多,同時交換空間也在被使用,就出現(xiàn)了剛才所說的現(xiàn)象了。 3)交換空間的頁面在使用時會首先被交換到物理內(nèi)存,如果此時沒有足夠的物理內(nèi)存來容納這些頁面,它們又會被馬上交換出去,如此以來,虛擬內(nèi)存中可能沒有足夠空間來存儲這些交換頁面,最終會導(dǎo)致Linux出現(xiàn)假死機(jī)、服務(wù)異常等問題,Linux雖然可以在一段時間內(nèi)自行恢復(fù),但是恢復(fù)后的系統(tǒng)已經(jīng)基本不可用了。 內(nèi)存監(jiān)控常用命令 Linux監(jiān)控內(nèi)存最常使用的命令有free、top等。如圖: ![]() 第一行: total:物理內(nèi)存的總大小 used:已經(jīng)分配的物理內(nèi)存大小 free:空閑的物理內(nèi)存大?。ㄎ幢徊僮飨到y(tǒng)分配) shared:多個進(jìn)程共享的內(nèi)存大小,主要用于進(jìn)程間的通訊 buffers/cached:共同表示磁盤緩存的大小 第二行Mem:代表物理內(nèi)存使用情況 第三行(-/+ buffers/cached):代表磁盤緩存使用狀態(tài) 第四行:Swap表示交換空間內(nèi)存使用狀態(tài) 第二行的數(shù)據(jù)實際上是從操作系統(tǒng)的角度來看的。used僅僅代表操作系統(tǒng)已經(jīng)分配的物理內(nèi)存大小,而不一定是已被使用的內(nèi)存大小。例如,一個應(yīng)用申請了1000KB的內(nèi)存,不會立刻全部使用,可能當(dāng)前只使用了600KB的內(nèi)存,剩余的400KB就可以被“借”給操作系統(tǒng)來作為緩存使用。這些就以第二行的buffers和cashed的形式表示出來。 buffers與cached都是內(nèi)存操作,用來保存系統(tǒng)曾經(jīng)打開過的文件以及文件屬性信息,這樣當(dāng)操作系統(tǒng)需要讀取某些文件時,會首先在buffers與cached內(nèi)存區(qū)查找,如果找到,直接讀出傳送給應(yīng)用程序,如果沒有找到需要數(shù)據(jù),才從磁盤讀取,這就是操作系統(tǒng)的緩存機(jī)制,通過緩存,大大提高了操作系統(tǒng)的性能。但buffers與cached緩沖的內(nèi)容卻是不同的。 buffers是用來緩沖塊設(shè)備做的,它只記錄文件系統(tǒng)的元數(shù)據(jù)(metadata)以及 tracking in-flight pages,而cached是用來給文件做緩沖。更通俗一點說:buffers主要用來存放目錄里面有什么內(nèi)容,文件的屬性以及權(quán)限等等。而cached直接用來記憶我們打開過的文件和程序。 Linux會在需要內(nèi)存的時候,或在系統(tǒng)運行逐步推進(jìn)時,將buffers和cached狀態(tài)的內(nèi)存變?yōu)閒ree狀態(tài)的內(nèi)存,以供系統(tǒng)使用。 第三行的數(shù)據(jù)則是從應(yīng)用程序的角度來看內(nèi)存分配。這里的“-/+”實際上分別指的是兩個部分: -buffers/cashed = used(第二行) - buffers - cached ; (即當(dāng)前系統(tǒng)所有程序真實使用的物理內(nèi)存大小) +buffers/cashed = buffers + cached; (暫時借給系統(tǒng)作為緩沖區(qū)的內(nèi)存大?。?。 從上面的論述也可以看出,第三行的free對應(yīng)的一欄實際上是應(yīng)用程序可以使用的內(nèi)存大小,它的值 = free(第二行)+ buffers(第二行) + cached(第二行)。 如果那些“借給”系統(tǒng)內(nèi)存的程序需要“借出去”的內(nèi)存,則從第二行的free一欄分給該程序。如果有必要的話,就會從buffers/cashed中釋放內(nèi)存,同時將釋放內(nèi)存中的數(shù)據(jù)寫回到硬盤中。如果連buffers/cashed也沒有了,則從硬盤處借,分配SWAP空間。 你也可以通過命令行強(qiáng)制要求系統(tǒng)歸還這些內(nèi)存,命令如下: echo 3 > /proc/sys/vm/drop_caches (3代表釋放所有bufferes/cashed能釋放的空間) 不過你需要root權(quán)限?,F(xiàn)象如圖: ![]() 可以看見buffers/cashed兩個部分的空間大大縮小。 內(nèi)存緊缺回收及短期回收內(nèi)存管理 內(nèi)存管理的目標(biāo)是提供一種方法,為實現(xiàn)各種目的而在各個用戶之間實現(xiàn)內(nèi)存共享。內(nèi)存管理方法應(yīng)該實現(xiàn)以下兩個功能: 1)最小化管理內(nèi)存所需的時間 2)最大化用于一般應(yīng)用的可用內(nèi)存(最小化管理開銷) 內(nèi)存管理實際上是一種關(guān)于權(quán)衡的零和游戲。您可以開發(fā)一種使用少量內(nèi)存進(jìn)行管理的算法,但是要花費更多時間來管理可用內(nèi)存。也可以開發(fā)一個算法來有效地管理內(nèi)存,但卻要使用更多的內(nèi)存。最終,特定應(yīng)用程序的需求將促使對這種權(quán)衡作出選擇。 每個內(nèi)存管理器都使用了一種基于堆的分配策略。在這種方法中,大塊內(nèi)存(稱為 堆)用來為用戶定義的目的提供內(nèi)存。當(dāng)用戶需要一塊內(nèi)存時,就請求給自己分配一定大小的內(nèi)存。堆管理器會查看可用內(nèi)存的情況(使用特定算法)并返回一塊內(nèi)存。搜索過程中使用的一些算法有 first-fit(在堆中搜索到的第一個滿足請求的內(nèi)存塊)和 best-fit(使用堆中滿足請求的最合適的內(nèi)存塊)。當(dāng)用戶使用完內(nèi)存后,就將內(nèi)存返回給堆。(注意 :這里的堆和數(shù)據(jù)結(jié)構(gòu)中的堆不是一個概念) 這種基于堆的分配策略的根本問題是碎片(fragmentation)。當(dāng)內(nèi)存塊被分配后,它們會以不同的順序在不同的時間返回。這樣會在堆中留下一些洞,需要花一些時間才能有效地管理空閑內(nèi)存。這種算法通常具有較高的內(nèi)存使用效率(分配需要的內(nèi)存),但是卻需要花費更多時間來對堆進(jìn)行管理。 另外一種方法稱為 buddy memory allocation,是一種更快的內(nèi)存分配技術(shù),它將內(nèi)存劃分為 2 的冪次方個分區(qū),并使用 best-fit 方法來分配內(nèi)存請求。當(dāng)用戶釋放內(nèi)存時,就會檢查 buddy 塊,查看其相鄰的內(nèi)存塊是否也已經(jīng)被釋放。如果是的話,將合并內(nèi)存塊以最小化內(nèi)存碎片。這個算法的時間效率更高,但是由于使用 best-fit 方法的緣故,會產(chǎn)生內(nèi)存浪費。 Slab分配器 Linux 所使用的 slab 分配器的基礎(chǔ)是 Jeff Bonwick 為 SunOS 操作系統(tǒng)首次引入的一種算法。 ![]() 每個緩存都包含一個slabs列表,存在三種slab:slabs_full、slabs_patial、slabs_empty. 由于對象是從 slab 中進(jìn)行分配和釋放的,因此單個 slab 可以在 slab 列表之間進(jìn)行移動。例如,當(dāng)一個 slab 中的所有對象都被使用完時,就從 slabs_partial 列表中移動到 slabs_full 列表中。當(dāng)一個 slab 完全被分配并且有對象被釋放后,就從 slabs_full 列表移動到 slabs_partial 列表。當(dāng)所有對象都被釋放后,就從 slabs_partial 列表移動到 slabs_empty 列表. 內(nèi)存分配 回收目標(biāo) 不是所有的物理內(nèi)存都可以參與回收的,一般內(nèi)核代碼段、數(shù)據(jù)段、內(nèi)核kmalloc()出來的內(nèi)存,內(nèi)核線程占用的內(nèi)存都是不可以回收的,除此之外的內(nèi)存都是回收的對象。回收的內(nèi)存主要是由用戶態(tài)進(jìn)程占用的內(nèi)存和內(nèi)核自己在運行時所使用的一些內(nèi)存組成。用戶態(tài)進(jìn)程占用的內(nèi)存主要是我們常見的進(jìn)程代碼段,數(shù)據(jù)段,堆棧等,內(nèi)核運行使用的內(nèi)存主要是磁盤高速緩存(如索引節(jié)點,目錄項高速緩存),頁面高速緩存(訪問文件時系統(tǒng)生成的頁面cache),mmap()文件時所用的有名映射所使用的物理內(nèi)存。 回收時機(jī) 1)內(nèi)存緊缺回收:grow_buffers()無法獲取緩沖區(qū)頁,alloc_page_buffers()無法獲取頁臨時緩沖區(qū)首部,__alloc_pages()無法再給定的內(nèi)存區(qū)分配一組連續(xù)頁框。 2)周期回收:必要時,激活相應(yīng)內(nèi)核線程執(zhí)行內(nèi)存回收算法:kswapd()內(nèi)核線程,檢查某個內(nèi)存管理區(qū)的空閑頁框數(shù)是否已低于pages_high值的標(biāo)高。events內(nèi)核線程,一個工作者線程,回收位于高速內(nèi)存緩存中的所有空閑的slab。 回收的分類 主要回收兩類內(nèi)存:最近最少使用內(nèi)存以及高速內(nèi)存緩存中空閑的slab。前者主要包括用戶態(tài)進(jìn)程的代碼段,數(shù)據(jù)段,堆棧,文件映射內(nèi)存,頁高速內(nèi)存,后者主要包括磁盤高速緩存及一些其他的空閑內(nèi)存高速緩存。 ![]() shrink_caches():調(diào)用shrink_zone()對傳入的zone鏈表中的每個zone,進(jìn)行l(wèi)ru上面的頁面回收。 shrink_slab():對磁盤索引節(jié)點cache和目錄項索引節(jié)點等磁盤高速緩存進(jìn)行回收,由于磁盤索引節(jié)點和目錄項索引節(jié)點都是從slab高速緩存中分配的,這樣就會導(dǎo)致空閑slab的產(chǎn)生,空閑slab后續(xù)會在周期性回收的cache_reap工作隊列中被回收。估計也就是因為最終會清零空閑slab,才會起這么一個函數(shù)名。^_^ shrink_zone():對內(nèi)存管理區(qū)上的lru鏈表中的非活躍頁面進(jìn)行回收,在非活躍頁面不足的時候,調(diào)用refill_inactive_zone()對lru上的inactive鏈表補(bǔ)充非活躍頁面,同時shrink_zone()調(diào)用shrink_cache()來進(jìn)行頁面的回收,該函數(shù)的具體解析可以參照下面的源碼淺析。 shrink_list():該輔助函數(shù)在shrink_cache()中被調(diào)用,該函數(shù)對在shrink_cache()中傳入的非活躍page列表進(jìn)行遍歷,對每個頁面進(jìn)行回收工作,該函數(shù)的具體解析可以參考下面的源碼解析。 refill_inactie_zone():該輔助函數(shù)根據(jù)一定的規(guī)則將處于lru active鏈表上的活躍頁面移動到inactive鏈表上,以補(bǔ)充可以回收的頁面,在lru鏈表里有兩類頁,一類是屬于用戶態(tài)空間的頁,比如用戶態(tài)進(jìn)程的代碼段,數(shù)據(jù)段,一類是在頁高速緩存中的頁,系統(tǒng)為了降低對應(yīng)用程序的影響,將要優(yōu)先將頁高速緩存頁進(jìn)行回收,同時為了系統(tǒng)整體性能也會適當(dāng)回收用戶態(tài)進(jìn)程頁。 |
|
|