Linux內核地址空間劃分
通常32位Linux內核虛擬地址空間劃分0~3G為用戶空間,3~4G為內核空間(注意,內核可以使用的線性地址只有1G)。注意這里是32位內核地址空間劃分,64位內核地址空間劃分是不同的。
通常32位Linux內核虛擬地址空間劃分0~3G為用戶空間,3~4G為內核空間(注意,內核可以使用的線性地址只有1G)。注意這里是32位內核地址空間劃分,64位內核地址空間劃分是不同的。
Linux內核高端內存的由來
當內核模塊代碼或線程訪問內存時,代碼中的內存地址都為邏輯地址,而對應到真正的物理內存地址,需要地址一對一的映射,如邏輯地址0xc0000003對應的物理地址為0×3,0xc0000004對應的物理地址為0×4,… …,邏輯地址與物理地址對應的關系為
物理地址 = 邏輯地址 – 0xC0000000:這是內核地址空間的地址轉換關系,注意內核的虛擬地址在“高端”,但是ta映射的物理內存地址在低端。
實際上,“內核直接映射空間”也達不到 1G, 還得留點線性空間給“內核動態(tài)映射空間” 呢。
因此,Linux 規(guī)定“內核直接映射空間” 最多映射 896M 物理內存。
對于高端內存,可以通過 alloc_page() 或者其它函數獲得對應的 page,但是要想訪問實際物理內存,還得把 page 轉為線性地址才行(為什么?想想 MMU 是如何訪問物理內存的),也就是說,我們需要為高端內存對應的 page 找一個線性空間,這個過程稱為高端內存映射。
假 設按照上述簡單的地址映射關系,那么內核邏輯地址空間訪問為0xc0000000 ~ 0xffffffff,那么對應的物理內存范圍就為0×0 ~ 0×40000000,即只能訪問1G物理內存。若機器中安裝8G物理內存,那么內核就只能訪問前1G物理內存,后面7G物理內存將會無法訪問,因為內核 的地址空間已經全部映射到物理內存地址范圍0×0 ~ 0×40000000。即使安裝了8G物理內存,那么物理地址為0×40000001的內存,內核該怎么去訪問呢?代碼中必須要有內存邏輯地址 的,0xc0000000 ~ 0xffffffff的地址空間已經被用完了,所以無法訪問物理地址0×40000000以后的內存。
顯 然不能將內核地址空間0xc0000000 ~ 0xfffffff全部用來簡單的地址映射。因此x86架構中將內核地址空間劃分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM。ZONE_HIGHMEM即為高端內存,這就是內存高端內存概念的由來。
在x86結構中,三種類型的區(qū)域(從3G開始計算)如下:
ZONE_DMA 內存開始的16MB
ZONE_NORMAL 16MB~896MB
ZONE_HIGHMEM 896MB ~ 結束(1G)
高端內存是指物理地址大于 896M 的內存。對于這樣的內存,無法在“內核直接映射空間”進行映射。
為什么?
因為“內核直接映射空間”最多只能從 3G 到 4G,只能直接映射 1G 物理內存,對于大于 1G 的物理內存,無能為力。
高端內存映射有三種方式:
1、映射到“內核動態(tài)映射空間”
這種方式很簡單,因為通過 vmalloc() ,在“內核動態(tài)映射空間”申請內存的時候,就可能從高端內存獲得頁面(參看 vmalloc 的實現(xiàn)),因此說高端內存有可能映射到“內核動態(tài)映射空間” 中。
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 占用一塊空間