1. Linux內(nèi)核地址空間劃分 通常32位Linux內(nèi)核地址空間劃分0~3G為用戶(hù)空間,,3~4G為內(nèi)核空間。注意這里是32位內(nèi)核地址空間劃分,,64位內(nèi)核地址空間劃分是不同的,。
2. Linux內(nèi)核高端內(nèi)存 a) 由來(lái) 當(dāng)內(nèi)核模塊代碼或線(xiàn)程訪(fǎng)問(wèn)內(nèi)存時(shí),代碼中的內(nèi)存地址都為邏輯地址,,而對(duì)應(yīng)到真正的物理內(nèi)存地址,,需要地址一對(duì)一的映射,如邏輯地址0xc0000003對(duì)應(yīng)的物理地址為0×3,,0xc0000004對(duì)應(yīng)的物理地址為0×4,,… …,邏輯地址與物理地址對(duì)應(yīng)的關(guān)系為 物理地址 = 邏輯地址 –0xC0000000
邏輯地址 | 物理內(nèi)存地址 | 0xc0000000 | 0x0 | 0xc0000001 | 0x1 | 0xc0000002 | 0x2 | 0xc0000003 | 0x3 | … | … | 0xe0000000 | 0x20000000 | … | … | 0xffffffff | 0x40000000?? |
假設(shè)按照上述簡(jiǎn)單的地址映射關(guān)系,,那么內(nèi)核邏輯地址空間訪(fǎng)問(wèn)為0xc0000000 - 0xffffffff,,那么對(duì)應(yīng)的物理內(nèi)存范圍就為0×0 ~0×40000000,,即只能訪(fǎng)問(wèn)1G物理內(nèi)存。若機(jī)器中安裝8G物理內(nèi)存,,那么內(nèi)核就只能訪(fǎng)問(wèn)前1G物理內(nèi)存,,后面7G物理內(nèi)存將會(huì)無(wú)法訪(fǎng)問(wèn),因?yàn)閮?nèi)核的地址空間已經(jīng)全部映射到物理內(nèi)存地址范圍0x0 – 0x40000000,。 顯然不能將內(nèi)核地址空間0xc0000000 ~ 0xfffffff全部用來(lái)簡(jiǎn)單的地址映射,。因此x86架構(gòu)中將內(nèi)核地址空間劃分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM,。ZONE_HIGHMEM即為高端內(nèi)存,,這就是內(nèi)存高端內(nèi)存概念的由來(lái)。 在x86結(jié)構(gòu)中,,三種類(lèi)型的區(qū)域如下: ZONE_DMA 內(nèi)存開(kāi)始的16MB ZONE_NORMAL 16MB~896MB ZONE_HIGHMEM 896MB ~ 結(jié)束
用戶(hù)地址空間沒(méi)有高端內(nèi)存的概念,!就是直接通過(guò)段頁(yè)映射訪(fǎng)問(wèn)物理內(nèi)存!
b) 理解 前 面我們解釋了高端內(nèi)存的由來(lái),。 Linux將內(nèi)核地址空間劃分為三部分ZONE_DMA,、ZONE_NORMAL和ZONE_HIGHMEM,高端內(nèi)存HIGH_MEM地址空間范圍為 0xF8000000 - 0xFFFFFFFF(896MB-1024MB),。那么如內(nèi)核是如何借助128MB高端內(nèi)存地址空間是如何實(shí)現(xiàn)訪(fǎng)問(wèn)可以所有物理內(nèi)存,? 當(dāng)內(nèi)核想訪(fǎng)問(wèn)高于896MB物理地址內(nèi)存時(shí),從0xF8000000 ~ 0xFFFFFFFF地址空間范圍內(nèi)找一段相應(yīng)大小空閑的邏輯地址空間,,借用一會(huì),。借用這段邏輯地址空間,建立映射到想訪(fǎng)問(wèn)的那段物理內(nèi)存(即填充內(nèi)核PTE頁(yè)面表),,臨時(shí)用一會(huì),,用完后歸還。這樣別人也可以借用這段地址空間訪(fǎng)問(wèn)其他物理內(nèi)存,,實(shí)現(xiàn)了使用有限的地址空間,,訪(fǎng)問(wèn)所有所有物理內(nèi)存。如下圖,。
例 如內(nèi)核想訪(fǎng)問(wèn)2G開(kāi)始的一段大小為1MB的物理內(nèi)存,,即物理地址范圍為0x80000000 - 0x800FFFFF,。訪(fǎng)問(wèn)之前先找到一段1MB大小的空閑地址空間,,假設(shè)找到的空閑地址空間為0xF8700000 -0xF87FFFFF,用這1MB的邏輯地址空間映射到物理地址空間0x80000000 - 0x800FFFFF的內(nèi)存,。映射關(guān)系如下:
邏輯地址 | 物理內(nèi)存地址 | 0xF8700000 | 0x80000000 | 0xF8700001 | 0x80000001 | 0xF8700002 | 0x80000002 | … | … | 0xF87FFFFF | 0x800FFFFF |
當(dāng)內(nèi)核訪(fǎng)問(wèn)完0×80000000~ 0x800FFFFF物理內(nèi)存后,,就將0xF8700000 - 0xF87FFFFF內(nèi)核線(xiàn)性空間釋放。這樣其他進(jìn)程或代碼也可以使用0xF8700000 - 0xF87FFFFF這段地址訪(fǎng)問(wèn)其他物理內(nèi)存,。 從上面的描述,,我們可以知道高端內(nèi)存的最基本思想:借一段地址空間,,建立臨時(shí)地址映射,用完后釋放,,達(dá)到這段地址空間可以循環(huán)使用,,訪(fǎng)問(wèn)所有物理內(nèi)存。 看到這里,,不禁有人會(huì)問(wèn):萬(wàn)一有內(nèi)核進(jìn)程或模塊一直占用某段邏輯地址空間不釋放,,怎么辦?若真的出現(xiàn)的這種情況,,則內(nèi)核的高端內(nèi)存地址空間越來(lái)越緊張,,若都被占用不釋放,則沒(méi)有建立映射到物理內(nèi)存都無(wú)法訪(fǎng)問(wèn)了,。
c) 劃分 內(nèi)核將高端內(nèi)存劃分為3部分:VMALLOC_START~VMALLOC_END,、KMAP_BASE~FIXADDR_START和FIXADDR_START~4G。 對(duì) 于高端內(nèi)存,,可以通過(guò) alloc_page() 或者其它函數(shù)獲得對(duì)應(yīng)的 page,,但是要想訪(fǎng)問(wèn)實(shí)際物理內(nèi)存,還得把 page 轉(zhuǎn)為線(xiàn)性地址才行(為什么,?想想 MMU 是如何訪(fǎng)問(wèn)物理內(nèi)存的),,也就是說(shuō),我們需要為高端內(nèi)存對(duì)應(yīng)的 page 找一個(gè)線(xiàn)性空間,,這個(gè)過(guò)程稱(chēng)為高端內(nèi)存映射,。
對(duì)應(yīng)高端內(nèi)存的3部分,高端內(nèi)存映射有三種方式: 映射到”內(nèi)核動(dòng)態(tài)映射空間”(noncontiguousmemory allocation) 這種方式很簡(jiǎn)單,,因?yàn)橥ㄟ^(guò) vmalloc() ,,在”內(nèi)核動(dòng)態(tài)映射空間”申請(qǐng)內(nèi)存的時(shí)候,就可能從高端內(nèi)存獲得頁(yè)面(參看 vmalloc 的實(shí)現(xiàn)),,因此說(shuō)高端內(nèi)存有可能映射到”內(nèi)核動(dòng)態(tài)映射空間”中,。 持久內(nèi)核映射(permanent kernel mapping) 如果是通過(guò) alloc_page() 獲得了高端內(nèi)存對(duì)應(yīng)的 page,如何給它找個(gè)線(xiàn)性空間,? 內(nèi)核專(zhuān)門(mén)為此留出一塊線(xiàn)性空間,,從 PKMAP_BASE 到FIXADDR_START ,用于映射高端內(nèi)存,。在 2.6內(nèi)核上,,這個(gè)地址范圍是 4G-8M 到 4G-4M 之間。這個(gè)空間起叫”內(nèi)核永久映射空間”或者”永久內(nèi)核映射空間”,。這個(gè)空間和其它空間使用同樣的頁(yè)目錄表,,對(duì)于內(nèi)核來(lái)說(shuō),就是swapper_pg_dir,對(duì)普通進(jìn)程來(lái)說(shuō),,通過(guò) CR3 寄存器指向,。通常情況下,這個(gè)空間是 4M 大小,,因此僅僅需要一個(gè)頁(yè)表即可,,內(nèi)核通過(guò)來(lái) pkmap_page_table 尋找這個(gè)頁(yè)表。通過(guò) kmap(),,可以把一個(gè) page 映射到這個(gè)空間來(lái),。由于這個(gè)空間是 4M 大小,最多能同時(shí)映射 1024 個(gè) page,。因此,,對(duì)于不使用的的 page,及應(yīng)該時(shí)從這個(gè)空間釋放掉(也就是解除映射關(guān)系),,通過(guò) kunmap() ,,可以把一個(gè) page 對(duì)應(yīng)的線(xiàn)性地址從這個(gè)空間釋放出來(lái)。 臨時(shí)映射(temporary kernel mapping) 內(nèi)核在 FIXADDR_START 到 FIXADDR_TOP 之間保留了一些線(xiàn)性空間用于特殊需求,。這個(gè)空間稱(chēng)為”固定映射空間”在這個(gè)空間中,,有一部分用于高端內(nèi)存的臨時(shí)映射。 這塊空間具有如下特點(diǎn): (1)每個(gè) CPU 占用一塊空間 (2)在每個(gè) CPU 占用的那塊空間中,,又分為多個(gè)小空間,,每個(gè)小空間大小是 1 個(gè) page,每個(gè)小空間用于一個(gè)目的,,這些目的定義在 kmap_types.h 中的 km_type 中,。 當(dāng)要進(jìn)行一次臨時(shí)映射的時(shí)候,需要指定映射的目的,,根據(jù)映射目的,,可以找到對(duì)應(yīng)的小空間,然后把這個(gè)空間的地址作為映射地址,。這意味著一次臨時(shí)映射會(huì)導(dǎo)致以前的映射被覆蓋,。通過(guò)kmap_atomic() 可實(shí)現(xiàn)臨時(shí)映射。
3. 用戶(hù)虛擬地址 用戶(hù)空間程序所能看到的常規(guī)地址,。每個(gè)進(jìn)程都有自己的虛擬地址空間,。
4. 內(nèi)核邏輯地址 內(nèi)核邏輯地址組成了內(nèi)核的常規(guī)地址空間。邏輯地址和與其關(guān)聯(lián)的物理地址的不同,,僅僅是在它們之間存在一個(gè)固定的偏移量,。kmalloc返回的內(nèi)存就是內(nèi)核邏輯地址.
5. 內(nèi)核虛擬地址 內(nèi)核虛擬地址與邏輯地址相同之處在于,它們都將內(nèi)核空間的地址映射到物理地址上,。內(nèi)核虛擬地址與物理地址的映射不必是線(xiàn)性的和一對(duì)一的,。所有的邏輯地址都是內(nèi)核虛擬地址,但許多內(nèi)核虛擬地址不是邏輯地址,。vmalloc分配的內(nèi)存具有虛擬地址,。kmap同樣返回虛擬地址。
6. 關(guān)系圖
|