原先一直都對Linux高端內存的管理認識模模糊糊的,可能主要是初次接觸Linux kernel 是0.11版吧,當初的內存設計是16M,Linus對擁有32M的內存都是觊觎萬分,1G內存恐怕是天方夜譚了。16M內存哪裡還顧得上高端內存,腦子就一直沒有這種概念。先我們還是來看看為什麼要有高端內存?
80386的線性尋址空間是4G,內核空間從3G開始,如果全部采用"線性映射"(物理地址和邏輯地址只差一個常量 PAGE_OFFSET ),最多管理1G物理內存,也就是1G的物理內存挨著挨著對應的是虛擬地址的3G到4G的位置。你想想如果多於1G的內存,我們用什麼線性地址來裝下這些多出的地址呢? 我朋友的機器有2G,據說玩游戲巨爽(AMD64300+).顯然如果線性映射我的朋友就會浪費1G內存.為了使內核能夠訪問這些"高端內存",內核使用HighMem.做法是不將內核1G的虛擬地址空間全部映射成物理內存,而是預留一部分給高端內存做臨時映射使用.
其實內核不僅僅預留了highmem的地址空間,還給fixmap,vmalloc預留了虛存空間.實際上,系統初始化的時候預留128M虛存,896M用於"直接"映射物理內存。下面我們先貼上一幅圖,摘自《understand linux kernel 》
畢竟意淫是很考大家的空間想象能力的,所以我們還是依照圖片來看圖說話:
高端內存映射有三種方式:
1、映射到“內核動態映射空間”
這種方式很簡單,因為通過 vmalloc() ,在"內核動態映射空間"(上圖的VMALLOC_START到VMALLOC_END)申請內存的時候,就可能從高端內存獲得頁面(參看 vmalloc 的實現),因此說高端內存有可能映射到"內核動態映射空間" 中。
2、永久內核映射
如果是通過 alloc_page() 獲得了高端內存對應的 page,如何給它找個線性空間?
內核專門為此留出一塊線性空間,從 PKMAP_BASE 到 FIXADDR_START (上圖的倒數第二塊區域),用於映射高端內存。在 2.4 內核上,這個地址范圍是 4G-8M 到 4G-4M 之間。這個空間起叫“內核永久映射空間”或者“永久內核映射空間”。
這個空間和其它空間使用同樣的頁目錄表,對於內核來說,就是 swapper_pg_dir,對普通進程來說,通過 CR3 寄存器指向。
通常情況下,這個空間是 4M 大小,因此僅僅需要一個頁表即可,內核通過來 pkmap_page_table 尋找這個頁表。通過 kmap(), 可以把一個 page 映射到這個空間來。由於這個空間是 4M 大小,最多能同時映射 1024 個 page。因此,對於不使用的的 page,及應該時從這個空間釋放掉(也就是解除映射關系),通過 kunmap() ,可以把一個 page 對應的線性地址從這個空間釋放出來。
3、臨時映射
內核在 FIXADDR_START 到 FIXADDR_TOP 之間保留了一些線性空間用於特殊需求。這個空間稱為“固定映射空間”
在這個空間中,有一部分用於高端內存的臨時映射。
這塊空間具有如下特點:
1、每個 CPU 占用一塊空間
2、在每個 CPU 占用的那塊空間中,又分為多個小空間,每個小空間大小是 1 個 page,每個小空間用於一個目的,這些目的定義在 kmap_types.h 中的 km_type 中。
當要進行一次臨時映射的時候,需要指定映射的目的,根據映射目的,可以找到對應的小空間,然後把這個空間的地址作為映射地址。這意味著一次臨時映射會導致以前的映射被覆蓋。
通過 kmap_atomic() 可實現臨時映射。