Linux 內(nèi)核的內(nèi)存管理 - 概念

Concepts overview — The Linux Kernel documentation

Linux中的內(nèi)存管理是一個(gè)復(fù)雜的系統(tǒng),經(jīng)過(guò)多年的發(fā)展,它包含越來(lái)越多的功能,以支持從MMU-less microcontrollerssupercomputers的各種系統(tǒng)。
沒(méi)有MMU內(nèi)存管理的系統(tǒng)被稱為nommu,它值得寫(xiě)一份專門的文檔進(jìn)行描述。
盡管有些概念是相同的,這里我們假設(shè)MMU可用,CPU可以將虛擬地址轉(zhuǎn)換為物理地址。

Virtual Memory Primer

計(jì)算機(jī)系統(tǒng)中的物理內(nèi)存是有限資源,即便支持內(nèi)存熱插拔,其可以安裝的內(nèi)存也有限的。物理內(nèi)存不一定必須是連續(xù)的;它可以作為一組不同的地址范圍被訪問(wèn)。此外,不同的CPU架構(gòu),甚至同架構(gòu)的不同實(shí)現(xiàn)對(duì)如何定義這些地址范圍都是不同的。

這使得直接處理物理內(nèi)存異常復(fù)雜,為了避免這種復(fù)雜性,開(kāi)發(fā)了虛擬內(nèi)存 (virtual memory) 的概念。

虛擬內(nèi)存從應(yīng)用軟件中抽象出物理內(nèi)存的細(xì)節(jié),只允許在物理內(nèi)存中保留需要的信息(demand paging),并提供一種機(jī)制來(lái)保護(hù)和控制進(jìn)程之間的數(shù)據(jù)共享。

通過(guò)虛擬內(nèi)存,每次內(nèi)存訪問(wèn)都訪問(wèn)一個(gè)虛擬地址。當(dāng)CPU對(duì)從系統(tǒng)內(nèi)存讀?。ɑ?qū)懭耄┑闹噶钸M(jìn)行解碼時(shí),它將該指令中編碼的虛擬地址轉(zhuǎn)換為內(nèi)存控制器可以理解的物理地址。

物理內(nèi)存被切分為 頁(yè)幀 page frames頁(yè) pages。頁(yè)的大小是基于架構(gòu)的。一些架構(gòu)允許從幾個(gè)支持的值中選擇頁(yè)大?。淮诉x擇在內(nèi)核編譯時(shí)設(shè)置到內(nèi)核配置。

每個(gè)物理內(nèi)存頁(yè)都可以映射為一個(gè)或多個(gè)虛擬頁(yè)(virtual pages)。映射關(guān)系描述在頁(yè)表(page tables)中,頁(yè)表將程序使用的虛擬地址轉(zhuǎn)換為物理內(nèi)存地址。頁(yè)表以層次結(jié)構(gòu)組織。

最底層的表包含軟件使用的實(shí)際內(nèi)存頁(yè)的物理地址。較高層的表包含較低層表頁(yè)的物理地址。頂層表的指針駐留在寄存器中。
當(dāng)CPU進(jìn)行地址轉(zhuǎn)換的時(shí)候,它使用寄存器訪問(wèn)頂級(jí)頁(yè)表。

虛擬地址的高位,用于頂級(jí)頁(yè)表的條目索引。然后,通過(guò)該條目訪問(wèn)下級(jí),下級(jí)的虛擬地址位又作為其下下級(jí)頁(yè)表的索引。虛擬地址的最低位定義實(shí)際頁(yè)內(nèi)的偏移量。

Huge Pages

地址轉(zhuǎn)換需要多次內(nèi)存訪問(wèn),而內(nèi)存訪問(wèn)相對(duì)于CPU速度來(lái)說(shuō)比較慢。為了避免在地址轉(zhuǎn)換上花費(fèi)寶貴的處理器周期,CPU維護(hù)著一個(gè)稱為TLB(Translation Lookaside Buffer)的用于地址轉(zhuǎn)換緩存(cache)。通常TLB是非常稀缺的資源,需要大內(nèi)存工作應(yīng)用程序會(huì)因?yàn)門LB未命中而影響性能。

很多現(xiàn)代CPU架構(gòu)允許頁(yè)表的高層直接映射到內(nèi)存頁(yè)。例如,x86架構(gòu),可以通過(guò)二級(jí)、三級(jí)頁(yè)表的條目映射2M甚至1G內(nèi)存頁(yè)。在Linux中,這些內(nèi)存頁(yè)稱為大頁(yè) (Huge)。大頁(yè)的使用顯著降低了TLB的壓力,提高了TLB命中率,從而提高了系統(tǒng)的整體性能。

Linux提供兩種機(jī)制開(kāi)啟使用大頁(yè)映射物理內(nèi)存。

第一個(gè)是HugeTLB文件系統(tǒng),即hugetlbfs。它是一個(gè)偽文件系統(tǒng),使用RAM作為其存儲(chǔ)。在此文件系統(tǒng)中創(chuàng)建的文件,數(shù)據(jù)駐留在內(nèi)存中,并使用大頁(yè)進(jìn)行映射。
關(guān)于 HugeTLB Pages

另一個(gè)被稱為THP (Transparent HugePages),后出的開(kāi)啟大頁(yè)映射物理內(nèi)存的機(jī)制。
hugetlbfs不同,hugetlbfs要求用戶和/或系統(tǒng)管理員配置系統(tǒng)內(nèi)存的哪些部分應(yīng)該并可以被大頁(yè)映射;THP透明地管理這些映射并獲取名稱。
關(guān)于 Transparent Hugepage Support

Zones

通常,硬件對(duì)不同物理內(nèi)存范圍的訪問(wèn)方式有所限制。某些情況下,設(shè)備不能對(duì)所有可尋址內(nèi)存執(zhí)行DMA。在其他情況下,物理內(nèi)存的大小超過(guò)虛擬內(nèi)存的最大可尋址大小,需要采取特殊措施來(lái)訪問(wèn)部分內(nèi)存。還有些情況,物理內(nèi)存的尺寸超過(guò)了虛擬內(nèi)存的最大可尋址尺寸,需要采取特殊措施來(lái)訪問(wèn)部分內(nèi)存。

Linux根據(jù)內(nèi)存頁(yè)的使用情況,將其組合為多個(gè)zones。比如, ZONE_DMA包含設(shè)備用于DMA的內(nèi)存,ZONE_HIGHMEM包含未永久映射到內(nèi)核地址空間的內(nèi)存,ZONE_NORMAL包含正常尋址內(nèi)存頁(yè)。
內(nèi)存zones的實(shí)際層次架構(gòu)取決于硬件,因?yàn)椴⒎撬屑軜?gòu)都定義了所有的zones,不同平臺(tái)對(duì)DMA的要求也不同。

Nodes

多處理器機(jī)器很多基于NUMA (Non-Uniform Memory Access system - 非統(tǒng)一內(nèi)存訪問(wèn)系統(tǒng))架構(gòu)。 在這樣的系統(tǒng)中,根據(jù)與處理器的“距離”,內(nèi)存被安排成具有不同訪問(wèn)延遲的banks。每個(gè)bank被稱為一個(gè)node,Linux為每個(gè)node構(gòu)造一個(gè)獨(dú)立的內(nèi)存管理子系統(tǒng)。Node有自己的zones集合、free&used頁(yè)面列表,以及各種統(tǒng)計(jì)計(jì)數(shù)器。
What is NUMA?
NUMA Memory Policy

Page cache

物理內(nèi)存易失,將數(shù)據(jù)放入內(nèi)存的常見(jiàn)情況是讀取文件。讀取文件時(shí),數(shù)據(jù)會(huì)放入頁(yè)面緩存(page cache),可以在再次讀取時(shí)避免耗時(shí)的磁盤(pán)訪問(wèn)。同樣,寫(xiě)文件時(shí),數(shù)據(jù)也會(huì)被放入頁(yè)面緩存,并最終進(jìn)入存儲(chǔ)設(shè)備。被寫(xiě)入的頁(yè)被標(biāo)記為臟頁(yè)(dirty page),當(dāng)Linux決定將其重用時(shí),它會(huì)將更新的數(shù)據(jù)同步到設(shè)備上的文件。

Anonymous Memory

匿名內(nèi)存 anonymous memory匿名映射 anonymous mappings表示沒(méi)有后置文件系統(tǒng)的內(nèi)存。這些映射是為程序的stack和heap隱式創(chuàng)建的,或調(diào)用mmap(2)顯式創(chuàng)建的。通常,匿名映射只定義允許程序訪問(wèn)的虛擬內(nèi)存區(qū)域。讀,會(huì)創(chuàng)建一個(gè)頁(yè)表?xiàng)l目,該條目引用一個(gè)填充有零的特殊物理頁(yè)。寫(xiě),則分配一個(gè)常規(guī)物理頁(yè)來(lái)保存寫(xiě)入數(shù)據(jù)。該頁(yè)將被標(biāo)記為臟頁(yè),如果內(nèi)核決定重用該頁(yè),則臟頁(yè)將被交換出去swapped out。

Reclaim

縱貫整個(gè)系統(tǒng)生命周期,物理頁(yè)可用于存儲(chǔ)不同類型的數(shù)據(jù)。它可以是內(nèi)核內(nèi)部數(shù)據(jù)結(jié)構(gòu)、設(shè)備驅(qū)動(dòng)DMA緩沖區(qū)、讀取自文件系統(tǒng)的數(shù)據(jù)、用戶空間進(jìn)程分配的內(nèi)存等。
根據(jù)內(nèi)存頁(yè)使用情況,Linux內(nèi)存管理會(huì)區(qū)別處理??梢噪S時(shí)釋放的頁(yè)面稱為可回收(reclaimable)頁(yè)面,因?yàn)樗鼈儼褦?shù)據(jù)緩存到了其他地方(比如,硬盤(pán)),或者被swap out到硬盤(pán)上。
可回收頁(yè)最值得注意的是頁(yè)面緩存匿名頁(yè)面。

在大多數(shù)情況下,存放內(nèi)部?jī)?nèi)核數(shù)據(jù)的頁(yè),和用作DMA緩沖區(qū)的頁(yè)無(wú)法重用,它們將保持現(xiàn)狀直到用戶釋放。這樣的被稱為不可回收頁(yè)(unreclaimable)
然而,在特定情況下,即便是內(nèi)核數(shù)據(jù)結(jié)構(gòu)占用的頁(yè)面也會(huì)被回收。
例如,文件系統(tǒng)元數(shù)據(jù)的緩存(in-memory)可以從存儲(chǔ)設(shè)備中重新讀取,因此,當(dāng)系統(tǒng)存在內(nèi)存壓力時(shí),可以從主內(nèi)存中丟棄它們。

釋放可回收物理內(nèi)存頁(yè)并重新調(diào)整其用途的過(guò)程稱為(surprise!) reclaim。
Linux支持異步或同步回收頁(yè),取決于系統(tǒng)的狀態(tài)。
當(dāng)系統(tǒng)負(fù)載不高時(shí),大部分內(nèi)存是空閑的,可以立即從空閑頁(yè)得到分配。
當(dāng)系統(tǒng)負(fù)載提升后,空閑頁(yè)減少,當(dāng)達(dá)到某個(gè)閾值(low watermark)時(shí),內(nèi)存分配請(qǐng)求將喚醒kswapd守護(hù)進(jìn)程。它將以異步的方式掃描內(nèi)存頁(yè)。如果內(nèi)存頁(yè)中的數(shù)據(jù)在其他地方也有,則釋放這些內(nèi)存頁(yè);或者退出內(nèi)存到后置存儲(chǔ)設(shè)備(關(guān)聯(lián) 臟頁(yè))。

隨著內(nèi)存使用量進(jìn)一步增加,并達(dá)到另一個(gè)閾值-min watermark-將觸發(fā)回收。這種情況下,分配將暫停,直到回收到足夠的內(nèi)存頁(yè)。

Compaction

當(dāng)系統(tǒng)運(yùn)行時(shí),任務(wù)分配并釋放內(nèi)存,內(nèi)存變得碎片化。
雖然使用虛擬內(nèi)存可以將分散的物理頁(yè)表示為虛擬連續(xù)范圍,但有時(shí)需要分配大的連續(xù)的物理內(nèi)存。這種需求可能會(huì)提升。例如,當(dāng)設(shè)備驅(qū)動(dòng)需要一個(gè)大的DMA緩沖區(qū)時(shí),或當(dāng)THP分配一個(gè)大頁(yè)時(shí)。
內(nèi)存地址壓縮(compaction )解決了碎片問(wèn)題。
該機(jī)制將占用的頁(yè)從內(nèi)存zone的下部移動(dòng)到上部的空閑頁(yè)。壓縮掃描完成后,zone開(kāi)始處的空閑頁(yè)就并在一起了,分配較大的連續(xù)物理內(nèi)存就可行了。

reclaim類似,compaction 可以在kcompactd守護(hù)進(jìn)程中異步進(jìn)行,也可以作為內(nèi)存分配請(qǐng)求的結(jié)果同步進(jìn)行。

OOM killer

在存在負(fù)載的機(jī)器上,內(nèi)存可能會(huì)耗盡,內(nèi)核無(wú)法回收到足夠的內(nèi)存以繼續(xù)運(yùn)行。
為了保障系統(tǒng)的其余部分,引入了OOM killer

OOM killer 選擇犧牲一個(gè)任務(wù)來(lái)保障系統(tǒng)的總體健康。選定的任務(wù)被killed,以期望在它退出后釋放足夠的內(nèi)存以繼續(xù)正常的操作。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容