Linux的內(nèi)存管理
Linux的內(nèi)存管理是一個非常復(fù)雜的過程,主要分成兩個大的部分:內(nèi)核的內(nèi)存管理和進(jìn)程虛擬內(nèi)存。內(nèi)核的內(nèi)存管理是Linux內(nèi)存管理的核心,所以我們先對內(nèi)核的內(nèi)存管理進(jìn)行簡介。
一、物理內(nèi)存模型
?
物理內(nèi)存模型主要分為兩種:UMA(Uniform Memory Access)和NUMA(Non-Uniform Memory Access)。
UMA模型是指物理內(nèi)存是連續(xù)的,SMP系統(tǒng)中的每個處理器訪問各個內(nèi)存區(qū)都是同樣快的;而NUMA模型則是指SMP中的每個CPU都有自己的物理內(nèi)存區(qū),雖然CPU可以訪問其他CPU的內(nèi)存區(qū),但是要比方位自己的內(nèi)存區(qū)慢得多。我們一般使用的物理模型都是UMA模型。為了NUMA模型,Linux提供了三種可能的內(nèi)存布局配置:Flat Memory, Sparse Memory, Discontiguous Memory。
?
Flat Memory就是簡單的線性組織物理內(nèi)存,一般沒有內(nèi)存空洞的UMA架構(gòu)都采用這種配置。對于NUMA模型,一般只能采用后兩者,而后兩者的區(qū)別主要在于:Sparse Memory配置一般認(rèn)為是試驗(yàn)性的,不是那么穩(wěn)定,但是有一些新的功能和性能優(yōu)化,而Discontiguous Memory配置一般認(rèn)為是穩(wěn)定的,但不具有內(nèi)存熱插拔之類的新特性。
二、物理內(nèi)存組織
- 物理內(nèi)存的組織主要分為兩個部分:節(jié)點(diǎn)(node)和內(nèi)存與內(nèi)存域(zone)。
- node主要針對NUMA設(shè)計(jì),在NUMA的SMP系統(tǒng)中,每個處理器都有一個自己的node,而在UMA模型中則只有一個node。對于每個node中的內(nèi)存,Linux分成了若干內(nèi)存域,定義在mmzone.h的zone_type中,常用的有ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM和ZONE_MOVABLE。其中ZONE_NORMAL是最為常用的,表示內(nèi)核能夠直接映射的一般內(nèi)存區(qū)域;ZONE_DMA表示DMA內(nèi)存區(qū);ZONE_DMA32表示64位系統(tǒng)中對于32位DMA設(shè)備使用的內(nèi)存;ZONE_HIGHMEM表示在32位系統(tǒng)中,高地址內(nèi)存的區(qū)域;ZONE_MOVABLE與伙伴系統(tǒng)的內(nèi)存碎片消除有關(guān)。后文會詳細(xì)介紹相關(guān)部分。
- 在物理內(nèi)存管理過程中有一些名詞:
- Page Frame(頁幀,或稱頁框):是系統(tǒng)內(nèi)存管理的最小單位,系統(tǒng)中每個頁框都是struct page的一個實(shí)例。IA-32系統(tǒng)的頁框大小是4KB。
- Hot-n-Code Pages(冷熱頁):是指內(nèi)存管理中對頁框的分類,訪問較多的或者近期訪問的為熱頁,否則為冷頁。該標(biāo)記主要與內(nèi)存換出(memory swap)相關(guān)。
- Page Table(頁表):是內(nèi)存尋址過程中的輔助數(shù)據(jù)結(jié)構(gòu)。層次化的頁表對于大地之空間的快速、高效管理很有意義。Linux一般支持四級頁表:PGD(Page Global Directory)、PUD(Page Upper Directory)、PMD(Page Middle Directory)和PTE(Page Table Entry)。IA-32體系中默認(rèn)只是用了兩級分頁系統(tǒng),即只有PGD和PTE。
三、X86架構(gòu)下的內(nèi)存布局
內(nèi)核在內(nèi)存中的布局
Linux的內(nèi)核在初始化的時候會被加載到內(nèi)存區(qū)的固定位置(在此我們不討論可重定位內(nèi)核的情況),而內(nèi)核所占用的內(nèi)存區(qū)域的布局是固定的,如圖:
?
內(nèi)存第一個頁框不實(shí)用,主要被BIOS用來初始化;之后的連續(xù)640KB內(nèi)存也不被內(nèi)核使用,主要用來映射各種ROM(通常是BIOS和顯卡ROM);再之后的空間是閑置的,原因是內(nèi)核要被放在連續(xù)的內(nèi)存空間。在0x100000開始為內(nèi)核部分,分別是代碼段、數(shù)據(jù)段和附加段。
IA-32架構(gòu)的布局
IA-32架構(gòu)可以訪問4GB的地址空間(不考慮PAE),常規(guī)情況下會將4GB線性空間劃分成3:1的兩部分:低地址的3/4部分為用戶空間,而高地址的1GB是內(nèi)核空間,即內(nèi)核地址空間從偏移量0xC0000000開始,每個虛擬地址x都對應(yīng)于物理地址x-0xC0000000。這樣的設(shè)計(jì)加快了內(nèi)核空間尋址的速度(簡單的減法操作)。在進(jìn)程切換的過程中,只有用戶空間的低3GB內(nèi)存對應(yīng)的頁表會被切換,高地址空間會公用內(nèi)核頁表。
IA-32架構(gòu)的這種設(shè)計(jì)存在著一個問題:既然內(nèi)核只能處理1GB的空間(事實(shí)上,內(nèi)核處理的空間還不足1GB,后面會詳細(xì)說明),那么如果物理內(nèi)存大于1GB,剩下的內(nèi)存將如何處理?這種情況下,內(nèi)核將無法直接映射全部物理內(nèi)存,這樣就用到了上面所說的高地址內(nèi)存域(ZONE_HIGHMEM)。具體的內(nèi)存分配如下圖:
?
能夠看到內(nèi)核區(qū)域的映射從__PAGE_OFFSET(0xC00000)開始,即3GiB位置開始映射到4GiB,開始的一段用來直接映射,而后面有128MB的VMALLOC空間(這部分空間的使用后文將講到),再之后有永久映射和固定映射的空間(從PKMAP_BASE開始)。所以事實(shí)上物理內(nèi)存能夠直接映射的空間為1GB-VMALLOC-固定映射-永久映射,所以真正大約只有850MB多一點(diǎn),也就是說,物理內(nèi)存中只有前850多MB是可以直接映射到內(nèi)核空間的,對于超過的部分來說,將作為高地址空間(HIGHMEM)。高地址空間可以在VMALLOC、永久映射和固定映射部分使用到。
到這里可能會有這樣一個疑問:如果內(nèi)核只能處理896MB的空間,那么如果內(nèi)存很大(比如3GB),剩下的空間的利用率和利用效率豈不是很低?對于這個問題我們需要注意:這里我們所講述的:1、這里的內(nèi)存都是內(nèi)核在內(nèi)核區(qū)的1GB空間里對物理內(nèi)存的訪問,用戶對物理內(nèi)存的訪問不是通過直接映射來訪問的,還有另外一套機(jī)制;2、這里的內(nèi)存僅僅是通過直接映射得到的內(nèi)存,內(nèi)核還可以通過其他的方式訪問到較高地址的內(nèi)存。
還有一個普遍的疑問就是:內(nèi)核直接映射占用了800多MB的空間,那么如果我們又3GB的物理內(nèi)存,是不是只有2GB多一點(diǎn)的實(shí)際可用內(nèi)存呢?這個說法是錯誤的,上圖所描述的只是內(nèi)核在線性地址空間的分布情況,其中的任何區(qū)域如果沒有真正的物理內(nèi)存與之映射的話是不會真正占用物理內(nèi)存的,而物理內(nèi)存在分配的過程中(用戶申請內(nèi)存、VMALLOC部分等),更傾向于先分配高地址內(nèi)存,在高地址內(nèi)存耗盡的情況下才會使用低850MB內(nèi)存。
AMD64架構(gòu)的布局
AMD64架構(gòu)采用了與IA-32完全不同的布局模式。由于64位的尋址空間的64位長,而在真正的實(shí)現(xiàn)過程中64位長尋址會造成較大的開銷,所以Linux目前僅適用了48位長的地址空間,但是為了向后兼容仍然適用64位地址空間表示。在布局方面考慮,如果單純采用48位類似IA-32的布局方式的話,則很難保證向后兼容性。所以AMD64架構(gòu)下的內(nèi)存布局Linux采用了一種特殊的方式,如圖:
?
Linux將內(nèi)存分成了高地址部分和低地址部分兩部分,即下半部空間0~0x0000 7FFF FFFF FFFF和上半部空間0xFFFF 8000 0000 0000~0xFFFF FFFF FFFF FFFF。可以看到虛擬地址的低47位,即[0,46]為有效位,[47,63]的值總是相同的:或者全為0或者全為1。除此之外的值都是無效的。這樣在虛擬內(nèi)存空間中就將內(nèi)存分成了兩個部分:內(nèi)存空間的下半部和上半部。下半部為用戶空間,上半部為內(nèi)核空間。我們考慮內(nèi)核空間部分,下半部的前MAXMEM大?。?4TB)為直接映射地址,之后有一個空洞,主要目的是處理內(nèi)存訪問越界;再之后是大小為32TB的VMALLOC空間,在之后是VMMEMMAP空間、KERNEL TEXT段空間以及Modules空間。
在這里我們不仔細(xì)講述AMD64架構(gòu)的布局,以后的部分則主要關(guān)注于IA-32架構(gòu)。
四、啟動過程期間的內(nèi)存管理
在啟動過程中,盡管內(nèi)存管理尚未初始化,但內(nèi)核仍然需要分配內(nèi)存以創(chuàng)建各種數(shù)據(jù)結(jié)構(gòu)。bootmem分配器用于在啟動階段早期分配內(nèi)存。由于對這部分內(nèi)存分配集中于簡單性方面而不是性能和通用性,因此使用的是最先適配(first-fit)分配器。該分配器使用一個位圖來管理頁,位圖中的1表示頁已使用,0表示未使用。在需要分配內(nèi)存時,分配器掃描位圖,直到找到一個能夠提供足夠連續(xù)頁的為之,即最先最佳(first-best)或最先適配位置。
在這個分配過程中,需要處理一些不可分配的頁面,如IA-32系統(tǒng)中的0頁。另外對于IA-32系統(tǒng),bootmem僅僅使用了低地址部分,對于高地址部分的操作過于麻煩,所以在這里被放棄了。
在這個部分有一個很有意思的事情。我們在編寫內(nèi)核模塊的時候,對于模塊的初始化函數(shù)會使用__init標(biāo)記或者_(dá)_init_data標(biāo)記。對于被這兩個關(guān)鍵字標(biāo)記的函數(shù)和數(shù)據(jù),是只有在初始化階段才用到的,在bootmem退出的時候會全部被回收。而這部分代碼和數(shù)據(jù)再內(nèi)核鏈接的過程中將會被放在.init.text段和.init.data段,并統(tǒng)一放在內(nèi)核的尾部,在啟動結(jié)束后便于回收。
五、物理內(nèi)存的管理
1、伙伴系統(tǒng)
物理內(nèi)存管理中伙伴系統(tǒng)是最為重要的一個系統(tǒng),伙伴系統(tǒng)也基于一種相對簡單然而令人吃驚的強(qiáng)大算法,到目前已經(jīng)使用了幾乎40年?;锇橄到y(tǒng)在這里不再贅述,簡單谷歌一下就可以查到該算法的描述(實(shí)在是很簡單)。在這里主要講一下Linux Kernel的伙伴系統(tǒng)以及在2.6.24之后版本的系統(tǒng)中對伙伴系統(tǒng)的改良。
在上文中已經(jīng)說到,物理內(nèi)存的慣例分為若干個node,每個node中又有若干個zone。對于每個zone,都會有對應(yīng)的伙伴系統(tǒng),如下圖所示:
?
上圖中的Fallback list指的是:在多個node的系統(tǒng)中,如果某個node的內(nèi)存空間不夠,則會在Fallback List中指定的node中分配內(nèi)存。
我們可以執(zhí)行cat /proc/buddyinfo,能夠看到大約如下所示的信息:
/proc/buddyinfo:
wolfgang@meitner> cat /proc/buddyinfo
Node 0, zone DMA 3 5 7 4 6 3 3 3 1 1 1
Node 0, zone DMA32 130 546 695 271 107 38 2 2 1 4 479
Node 0, zone Normal 23 6 6 8 1 4 3 0 0 0 0
顯示的三個域則是我們使用到的內(nèi)存域。
伙伴系統(tǒng)會出現(xiàn)一個很常見的問題:在系統(tǒng)使用較長時間之后,內(nèi)存中經(jīng)常出現(xiàn)較多碎片。對于這種情況,內(nèi)核將內(nèi)存頁面分成五種類型:
MIGRATE_UNMOVABLE
MIGRATE_RECLAIMABLE
MIGRATE_RESERVE
MIGRATE_MOVABLE
MIGRATE_ISOLATE
其中MIGRATE_RESERVE所表示的內(nèi)存是被系統(tǒng)保留以備急用的;MIGRATE_UNMOVABLE是不可移動的,如BIOS信息頁;MIGRATE_RECLAIMABLE在swap系統(tǒng)中使用;MIGRATE_ISOLATE表示不能從這里分配的內(nèi)存;MIGRATE_MOVABLE表示可以移動的內(nèi)存。對于內(nèi)核來說,MIGRATE_MOVABLE部分的內(nèi)存可以采用某種算法來進(jìn)行移動,使得內(nèi)存中的碎片減少。另外內(nèi)核還維護(hù)了一個fallback list,來表示如果在某個類型中分配頁面未成功,會在哪些類型的頁面中來分配。
具體的信息可以在/proc/pagetypeinfo中看到
2、伙伴系統(tǒng)的內(nèi)存分配API
基本上從如下兩張圖就能夠看出來:
?
?
對于其中的函數(shù)命名基本都是自明的,主要的差別在于:對于雙下劃線開頭的函數(shù)(__get_free_page, __free_page等)返回值或者參數(shù)為struct page *,而其他的函數(shù)返回值為unsigned long,即線性地址地址。
3、內(nèi)核中不連續(xù)頁的分配
根據(jù)上文的講述,我們知道物理上連續(xù)的映射對內(nèi)核是最好的,但并不是總能成功的使用。所以內(nèi)核提供了類似用戶空間訪問內(nèi)存一樣的機(jī)制(vmalloc)來進(jìn)行對內(nèi)核中不連續(xù)頁的分配。這一部分就是上文中所說的vmalloc區(qū)域。這部分主要是一個vmalloc函數(shù):
void *vmalloc(unsigned long size);
在該函數(shù)的實(shí)現(xiàn)過程中,需要先申請一部分虛擬內(nèi)存空間vm_area,然后將這部分空間映射到vmalloc區(qū)域中。對于映射的物理內(nèi)存,內(nèi)核更傾向于使用高地址空間(ZONE_HIGHMEM),來節(jié)省寶貴的地地址空間。對于不同vmalloc調(diào)用申請的vm_area之間,會有一個hole來隔離,以避免越界訪問。
?
注意vmalloc系統(tǒng)底層也是使用伙伴系統(tǒng)來分配內(nèi)存,所以申請內(nèi)存的大小只能是整頁的(頁大小對齊)。
在這部分有一個有意思的事情:vmalloc區(qū)域在IA-32中預(yù)設(shè)的大小是128MB,這部分內(nèi)存一般會被內(nèi)核模塊使用。vmalloc區(qū)域的大小是可以定制的,在新版內(nèi)核中可以在內(nèi)核啟動選項(xiàng)中加入vmalloc=xxxMB的方式來修改,或者修改內(nèi)核代碼對應(yīng)的宏:
unsigned int __VMALLOC_RESERVE = 128 << 20;
如果修改了vmalloc區(qū)域的大小,那么內(nèi)核能夠直接映射的區(qū)域?qū)s小,即kmalloc能夠使用的內(nèi)存將會變少(kmalloc使用slab allocator分配,后文將會介紹),但是內(nèi)核真正使用的物理內(nèi)存和vmalloc區(qū)域的大小沒有直接關(guān)系。所以在內(nèi)核模塊的編寫過程中,要根據(jù)需求來使用vmalloc和kmalloc,而了解他們的內(nèi)存分配機(jī)制是很有好處的。
4、內(nèi)核映射
盡管vmalloc函數(shù)族可用于從高端內(nèi)存向內(nèi)核映射頁框,但這并不是這些函數(shù)的實(shí)際用途。內(nèi)核提供了其他函數(shù)用于將ZONE_HIGHMEM頁框顯式的映射到內(nèi)核空間。
如果需要長期的將高端頁框映射到內(nèi)核地址空間中,即持久映射,需要使用kmap函數(shù),映射的空間指向上文圖中所指Persistent Mapings。內(nèi)核使用kunmap接觸映射。持久映射kmap函數(shù)不能用于處理中斷處理程序,因?yàn)閗map過程可能進(jìn)入睡眠狀態(tài)。
為了能夠原子的執(zhí)行映射過程(邏輯上稱為kmap_atomic),內(nèi)核提供了臨時映射機(jī)制,也被稱作固定映射,頁面也會被映射到Fixmaps區(qū)域。映射的API分別是kmap_atomic和kunmap_atomic。固定映射可以用在中斷處理程序中。
對于不支持高端內(nèi)存的體系結(jié)構(gòu)(如64位體系結(jié)構(gòu)),則將以上若干映射函數(shù)通過預(yù)編譯選項(xiàng)指向了對應(yīng)的兼容函數(shù)。事實(shí)上對于這些體系結(jié)構(gòu)的映射,都是簡單的返回對應(yīng)的內(nèi)存地址即可,因?yàn)閮?nèi)核可以在直接映射區(qū)域簡單的找到對應(yīng)的地址。
六、slab分配器
上面所描述的物理內(nèi)存管理機(jī)制中,最小粒度的內(nèi)存管理單元是頁框,大小一般是4KB,而在內(nèi)存中無論何時申請內(nèi)存都分配一個頁面是不合適的方式,所以引入了新的管理機(jī)制,即slab分配器。Slab是Sun公司的一個雇員Jeff Bonwick在Solaris 2.4中設(shè)計(jì)并實(shí)現(xiàn)的。slab分配器將大小相同的內(nèi)核對象放在一起,當(dāng)對象被free了之后并不是直接還給伙伴系統(tǒng),而是將這部分對象的頁面保存下來,在下一次該類對象的內(nèi)存申請時分配給新的對象。這種機(jī)制的優(yōu)勢在于:1、能夠按照CPU緩存的大小來組織分配對象的位置,一般來說,都會將若干個相同的對象放在一個cacheline中,并且對象占用的內(nèi)存不會跨越兩個cacheline。這樣的設(shè)計(jì)能夠保證slab分配器分配的對象能夠較多時間的存在于CPU緩存中。2、采用LIFO方式管理對象。這種做法基于:最近釋放的對象空間是最有可能存在于cache中的。這也能夠有效的利用cache。
各個緩存管理的對象,會合并為較大的組,覆蓋一個或者多個連續(xù)的頁框。這種組稱作slab,每個緩存由幾個這種slab組成。這也是slab分配器命名的由來。
1、slab、slob、slub分配器
Linux內(nèi)核中目前支持三種分配器,其中slab前文已經(jīng)簡單介紹過了,另外兩種分配器是備選分配器,可以在內(nèi)核編譯選項(xiàng)中指定。由于對上層提供的API是固定的,僅僅是底層實(shí)現(xiàn)不同,所以Kernel開發(fā)者不必去考慮底層的分配情況。
slab分配器雖然有很大的優(yōu)勢,但是其存在兩個問題:1、在較小的內(nèi)存系統(tǒng)下,slab分配器過于復(fù)雜。如嵌入式環(huán)境下slab顯得有些過于龐大。2、在內(nèi)存很大的巨型機(jī)上,slab分配器本身的數(shù)據(jù)結(jié)構(gòu)所占用的內(nèi)存空間過大,最大的可高達(dá)2GB以上。
對于前一種情況,設(shè)計(jì)了slob分配器,它圍繞一個simple linked list of block展開(也是slob的由來),在分配內(nèi)存的時候,采用了最先適配算法(first-fit)。
對于后一種情況,設(shè)計(jì)了slub分配器,slub分配器將頁框打包為組,并通過struct page中未使用的字段來管理這些組,試圖最小化內(nèi)存開銷。slub分配器事實(shí)上是基于slab分配器的一種優(yōu)化結(jié)構(gòu)。在大型機(jī)上slub分配器上有著更好的性能。
2、slab分配器的原理
內(nèi)核中一般的內(nèi)存分配和釋放的函數(shù)有kmalloc、kzalloc、kcalloc。這三個函數(shù)的區(qū)別是:kmalloc僅僅申請一片空間,kzalloc在申請一篇空間之后將其置0。kcalloc很少用,即對數(shù)組進(jìn)行空間分配并置0。
所有活動的slab緩存可以通過cat /proc/slabinfo來看到。
slab分配器由一個緊密交織的數(shù)據(jù)和內(nèi)存結(jié)構(gòu)的網(wǎng)絡(luò)組成,主要可以分為如圖的兩部分:保存管理型數(shù)據(jù)的緩存對象和保存被管理對象的各個slab。
?
每個slab緩存只負(fù)責(zé)一種對象類型,或者提供一般性的緩沖區(qū)。下圖中給出了緩存的精細(xì)結(jié)構(gòu):
?
可以看到對于每個slab緩存,都會保存成一個struct kmem_cache,每個結(jié)構(gòu)里面包含一個穿在一起的鏈表,以及三個鏈表頭:free、partial、full,分別表示空閑鏈、部分空閑鏈和滿鏈。含義和字面意思相同。對象在slab中并不是連續(xù)排列的,用戶可以要求對象按硬件緩存對齊,也可以要求按照BYTES_PER_WORD對齊,該值表示void指針?biāo)璧淖止?jié)的數(shù)目。
創(chuàng)建新的slab緩存需要調(diào)用kmem_cache_create函數(shù),返回struct kmem_cache結(jié)構(gòu)。創(chuàng)建緩存的時候需要制定緩存的可讀name(會出現(xiàn)在/proc/slabinfo中),還需要制定被管理對象以字節(jié)計(jì)的長度(size),在對齊數(shù)據(jù)時使用的偏移量(align),以及flags標(biāo)志。另外還需要制定構(gòu)造/析構(gòu)函數(shù)ctor/dtor。
分配對象時調(diào)用kmem_cache_alloc,函數(shù)需要指定創(chuàng)建過的slab緩存,以及flags。內(nèi)核支持的所有GFP_xxx宏都可以用于指定標(biāo)志。
下圖顯示了分配對象的過程:
?
3、通用緩存
如果不涉及對象緩存,而是傳統(tǒng)意義上的分配/釋放內(nèi)存,則需要調(diào)用kmalloc和kfree函數(shù),而這兩個函數(shù)的后端依然是使用slab分配器進(jìn)行分配的。kmalloc的基礎(chǔ)是一個數(shù)組,其中是一些分別用于不同內(nèi)存長度的slab緩存,數(shù)組項(xiàng)是cache_sizes的實(shí)例。該數(shù)據(jù)結(jié)構(gòu)定義如下:
struct cache_sizes {
size_t cs_size;
kmem_cache_t *cs_cachep;
kmem_cache_t *cs_dmacachep;
#ifdef CONFIG_ZONE_DMA
struct kmem_cache *cs_dmacachep;
#endif
}
cs_size指定了該項(xiàng)負(fù)責(zé)的內(nèi)存區(qū)的長度。每個長度對應(yīng)兩個slab緩存,其中之一提供適合DMA訪問的內(nèi)存。通過cat /proc/slabinfo中能夠看到,kmalloc-xxx和dma-kmalloc-xxx就是這一部分提供的。
kmalloc定義在,該函數(shù)首先檢查所需的緩存是否用常數(shù)來指定,如果是這種情況,所需的緩存可以在編譯時靜態(tài)確定,這可以提高速度(內(nèi)核的優(yōu)化真是無所不用其極?。。?。否則,該函數(shù)調(diào)用__kmalloc查找長度匹配的緩存,后者是__do_kmalloc的前端,提供參數(shù)轉(zhuǎn)換功能。
mm/slab.c
void *__do_kmalloc(size_t size, gfp_t flags)
{
kmem_cache_t *cachep;
cachep = __find_general_cachep(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return NULL;
return __cache_alloc(cachep, flags);
}
__find_general_cachep在上文提到的緩存中找到適當(dāng)?shù)囊粋€,之后使用__cache_alloc函數(shù)完成最終的分配。
七、處理器高速緩存和TLB控制
在這里簡單的總結(jié)一些Kernel中和TLB/高速緩存相關(guān)的函數(shù),具體TLB的實(shí)現(xiàn)機(jī)制與體系架構(gòu)相關(guān)性很大,就不詳細(xì)總結(jié)了。
flush_tlb_all和flush_cache_all刷出整個TLB/高速緩存。此操作只在操縱內(nèi)核頁表時需要,因?yàn)榇祟愋薷牟粌H影響所有進(jìn)程,而且影響系統(tǒng)中的所有處理器。
flush_tlb_mm(struct mm_struct *mm)和flush_cache_mm刷出所有屬于地址空間mm的TLB/高速緩存項(xiàng)。
flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)和flush_cache_range(vma, start, end)刷出地址范圍vma->vm_mm中虛擬地址start和end之間的所有TLB/高速緩存項(xiàng)。
flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 和flush_cache_page(vma, page)刷出虛擬地址在[page, page + PAGE_SIZE]范圍內(nèi)所有的TLB/高速緩存項(xiàng)。
update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)在處理頁失效之后調(diào)用。它在處理器的內(nèi)存管理單元MMU中加入信息,是的虛擬地址address由頁表項(xiàng)pte描述。僅當(dāng)存在外部MMU時才需要該函數(shù),通常MMU集成在處理器內(nèi)部。
此外,flush_cache_和flush_tlb_函數(shù)常常成對出現(xiàn),例如,在使用fork進(jìn)程復(fù)制進(jìn)程的地址空間時,則:1、刷出高速緩存,2、操作內(nèi)存,3、刷出TLB。這個順序很重要,因?yàn)?/p>
如果順序相反,那么在TLB刷出之后,正確信息提供之前,多處理器系統(tǒng)中的另一個CPU可能從進(jìn)程的頁表項(xiàng)取得錯誤的信息。
在刷出高速緩存時,某些體系結(jié)構(gòu)需要依賴TLB中的“虛擬->物理”轉(zhuǎn)換規(guī)則。flush_tlb_mm必須在flush_cache_mm之后執(zhí)行以確保這一點(diǎn)。
小結(jié)
這部分東西實(shí)在是太多,簡單的總結(jié)一下就已經(jīng)這么多了。在這里對以上的內(nèi)容進(jìn)行一個簡單的概括。
在內(nèi)核進(jìn)入正常運(yùn)行之后,內(nèi)存管理分為兩個層次:伙伴系統(tǒng)負(fù)責(zé)物理頁框的管理。在伙伴系統(tǒng)之上,所有的內(nèi)存管理都基于此,主要分為:slab分配器處理小塊內(nèi)存;vmalloc模塊為不連續(xù)物理頁框提供映射;永久映射區(qū)域和固定映射區(qū)域提供對高地址物理頁框的訪問。
內(nèi)存管理的初始化很具有挑戰(zhàn)性,內(nèi)核通過引入一個非常簡單的自舉內(nèi)存分配器(bootmem)解決了該問題,該分配器在正式的分配機(jī)制(伙伴系統(tǒng))啟用后停用。
審核編輯:符乾江
-
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209323 -
文件系統(tǒng)
+關(guān)注
關(guān)注
0文章
284瀏覽量
19904 -
內(nèi)存管理
+關(guān)注
關(guān)注
0文章
168瀏覽量
14134 -
虛擬內(nèi)存
+關(guān)注
關(guān)注
0文章
77瀏覽量
8058
發(fā)布評論請先 登錄
相關(guān)推薦
評論