高端內存是Linux中一個(gè)重要的概念,初涉Linux時(shí)曾經(jīng)對這個(gè)概念非常迷惑。實(shí)際上這個(gè)概念比較簡(jiǎn)單,理解這個(gè)概念,需要追溯一下Linux的內存管理。 從前,CPU的地址總線(xiàn)只有32位,再早的就不再追溯了。32的地址總線(xiàn)無(wú)論是從邏輯上還是從物理上都只能描述4G的地址空間,在物理上理論上最多擁有4G內存(除了IO地址空間,實(shí)際內存容量小于4G),邏輯空間也只能描述4G的線(xiàn)性地址空間。為了合理的利用4G空間,Linux采用了3:1的策略,即內核占用1G的線(xiàn)性地址空間,用戶(hù)占用3G的線(xiàn)性地址空間。所以用戶(hù)進(jìn)程的地址范圍從0~3G,內核地址范圍從3G~4G,也就是說(shuō),內核空間只能管理1G的內存。 對于如此緊張的線(xiàn)性地址資源,內核空間與用戶(hù)空間的肆意瓜分,導致了內存管理上的問(wèn)題:當物理內存大于1G時(shí),內核線(xiàn)性地址空間小于實(shí)際的物理內存容量,內核如何實(shí)現對大于1G內存的管理呢?說(shuō)到這里,需要提一下內核空間對內存的管理方法。一方面為了提高內核空間對內存的管理效率;另一方面,為了簡(jiǎn)化內核空間對內存的管理方法,內核采用線(xiàn)性映射的方法實(shí)現對內存的管理,從Linux實(shí)現的方法來(lái)看,物理地址與內核的虛擬地址只差一個(gè)偏移量。所以,當物理內存大于1G時(shí),物理內存無(wú)法全部映射到內核線(xiàn)性地址空間,這就產(chǎn)生了上述問(wèn)題。 從上述描述可以看出,地址空間大于1G的內存區域稱(chēng)之為高端內存,同理,小于1G的內存區域稱(chēng)之為低端內存。高端內存的管理需要進(jìn)行非線(xiàn)性映射,為此,在內核線(xiàn)性地址空間預留了128M的空間,位于線(xiàn)性地址空間的高端。如今,CPU的地址總線(xiàn)都擴大到64位了,線(xiàn)性地址資源非常豐富,所以,可以給內核空間預留足夠的線(xiàn)性地址資源,在最近一段時(shí)間內,內核線(xiàn)性地址資源與物理內存容量之間的矛盾將不再突出,高端內存的概念也就在64位CPU上消失了。 原先一直都對Linux高端內存的管理認識模模糊糊的,可能主要是初次接觸linux kernel 是0.11版吧,當初的內存設計是16M,Linus對擁有32M的內存都是覬覦萬(wàn)分,1G內存恐怕是天方夜譚了。16M內存哪里還顧得上高端內存,腦子就一直沒(méi)有這種概念。先我們還是來(lái)看看為什么要有高端內存? 80386的線(xiàn)性尋址空間是4G,內核空間從3G開(kāi)始,如果全部采用"線(xiàn)性映射"(物理地址和邏輯地址只差一個(gè)常量 PAGE_OFFSET ),最多管理1G物理內存,也就是1G的物理內存挨著(zhù)挨著(zhù)對應的是虛擬地址的3G到4G的位置。你想想如果多于1G的內存,我們用什么線(xiàn)性地址來(lái)裝下這些多出的地址呢? 我朋友的機器有2G,據說(shuō)玩游戲巨爽(AMD64300+).顯然如果線(xiàn)性映射我的朋友就會(huì )浪費1G內存.為了使內核能夠訪(fǎng)問(wèn)這些"高端內存",內核使用HighMem.做法是不將內核1G的虛擬地址空間全部映射成物理內存,而是預留一部分給高端內存做臨時(shí)映射使用. 其實(shí)內核不僅僅預留了highmem的地址空間,還給fixmap,vmalloc預留了虛存空間.實(shí)際上,系統初始化的時(shí)候預留128M虛存,896M用于"直接"映射物理內存。下面我們先貼上一幅圖,摘自《understand linux kernel 》 畢竟意淫是很考大家的空間想象能力的,所以我們還是依照圖片來(lái)看圖說(shuō)話(huà): 高端內存映射有三種方式: 1、映射到“內核動(dòng)態(tài)映射空間” 這種方式很簡(jiǎn)單,因為通過(guò) vmalloc() ,在"內核動(dòng)態(tài)映射空間"(上圖的VMALLOC_START到VMALLOC_END)申請內存的時(shí)候,就可能從高端內存獲得頁(yè)面(參看 vmalloc 的實(shí)現),因此說(shuō)高端內存有可能映射到"內核動(dòng)態(tài)映射空間" 中。 2、永久內核映射 如果是通過(guò) alloc_page() 獲得了高端內存對應的 page,如何給它找個(gè)線(xiàn)性空間? 內核專(zhuān)門(mén)為此留出一塊線(xiàn)性空間,從 PKMAP_BASE 到 FIXADDR_START (上圖的倒數第二塊區域),用于映射高端內存。在 2.4 內核上,這個(gè)地址范圍是 4G-8M 到 4G-4M 之間。這個(gè)空間起叫“內核永久映射空間”或者“永久內核映射空間”。 這個(gè)空間和其它空間使用同樣的頁(yè)目錄表,對于內核來(lái)說(shuō),就是 swapper_pg_dir,對普通進(jìn)程來(lái)說(shuō),通過(guò) CR3 寄存器指向。 通常情況下,這個(gè)空間是 4M 大小,因此僅僅需要一個(gè)頁(yè)表即可,內核通過(guò)來(lái) pkmap_page_table 尋找這個(gè)頁(yè)表。通過(guò) kmap(), 可以把一個(gè) page 映射到這個(gè)空間來(lái)。由于這個(gè)空間是 4M 大小,最多能同時(shí)映射 1024 個(gè) page。因此,對于不使用的的 page,及應該時(shí)從這個(gè)空間釋放掉(也就是解除映射關(guān)系),通過(guò) kunmap() ,可以把一個(gè) page 對應的線(xiàn)性地址從這個(gè)空間釋放出來(lái)。 3、臨時(shí)映射 內核在 FIXADDR_START 到 FIXADDR_TOP 之間保留了一些線(xiàn)性空間用于特殊需求。這個(gè)空間稱(chēng)為“固定映射空間” 在這個(gè)空間中,有一部分用于高端內存的臨時(shí)映射。 這塊空間具有如下特點(diǎn): 1、每個(gè) CPU 占用一塊空間 2、在每個(gè) CPU 占用的那塊空間中,又分為多個(gè)小空間,每個(gè)小空間大小是 1 個(gè) page,每個(gè)小空間用于一個(gè)目的,這些目的定義在 kmap_types.h 中的 km_type 中。 當要進(jìn)行一次臨時(shí)映射的時(shí)候,需要指定映射的目的,根據映射目的,可以找到對應的小空間,然后把這個(gè)空間的地址作為映射地址。這意味著(zhù)一次臨時(shí)映射會(huì )導致以前的映射被覆蓋.通過(guò) kmap_atomic() 可實(shí)現臨時(shí)映射。 想學(xué)習的你和我聯(lián)系預約就可以免費聽(tīng)課了。 以下課程可免費試聽(tīng)C語(yǔ)言、電子、PCB、STM32、Linux、FPGA、JAVA、安卓等。 宋工企鵝號:3524-6590-88 Tel/WX:173--1795--1908 |