1.1 內核地址空間(線性空間)分布
(1) 直接映射區:線性空間中從3G開始最大896M的區間,為直接內存映射區,該區域的線性地址和物理地址存在線性轉換關系:線性地址=3G+物理地址。
(2) 動態內存映射區:該區域由內核函數vmalloc來分配,特點是:線性空間連續,但是對應的物理空間不一定連續。vmalloc分配的線性地址所對應的物理頁可能處於低端內存,也可能處於高端內存。
(3) 永久內存映射區:該區域可訪問高端內存。訪問方法是使用alloc_page(_GFP_HIGHMEM)分配高端內存頁或者使用kmap函數將分配到的高端內存映射到該區域。
(4) 固定映射區:該區域和4G的頂端只有4k的隔離帶,其每個地址項都服務於特定的用途,如ACPI_BASE等。
說明:
注意用戶空間當然可以使用高端內存,而且是正常的使用,內核在分配那些不經常使用的內存時,都用高端內存空間(如果有),所謂不經常使用是相對來說的,比如內核的一些數據結構就屬於經常使用的,而用戶的一些數據就屬於不經常使用的。用戶在啟動一個應用程序時,是需要內存的,而每個應用程序都有3G的線性地址,給這些地址映射頁表時就可以直接使用高端內存。
而且還要糾正一點的是:那128M線性地址不僅僅是用在這些地方的,如果你要加載一個設備,而這個設備需要映射其內存到內核中,它也需要使用這段線性地址空間來完成,否則內核就不能訪問設備上的內存空間了。
總之,內核的高端線性地址是為了訪問內核固定映射以外的內存資源。進程在使用內存時,觸發缺頁異常,具體將哪些物理頁映射給用戶進程是內核考慮的事情。在用戶空間中沒有高端內存這個概念。
1.2 高端內存映射
高端內存映射含義為:將線性地址空間 (范圍從PAGE_OFFSET + 896M 至4G的最後128M)映射到 896M以上的物理頁框。如下圖所示:
高端內存映射有三種方式(都是非直接映射):
1.2.1 映射到“內核動態映射空間”(非連續內存區映射)
這種方式很簡單,因為通過 vmalloc() ,在內核“動態映射空間”申請內存的時候,就可能從高端內存獲得頁面(參看 vmalloc 的實現),因此說高端內存有可能映射到“內核動態映射空間 ”中。
1.2.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 對應的線性地址從這個空間釋放出來。永久內存映射允許建立長期映射。
1.2.3 臨時映射
內核在 FIXADDR_START 到 FIXADDR_TOP 之間保留了一些線性空間用於特殊需求。這個空間稱為“固定映射空間”在這個空間中,有一部分用於高端內存的臨時映射。
這塊空間具有如下特點:
1、 每個 CPU 占用一塊空間
2、 在每個 CPU 占用的那塊空間中,又分為多個小空間,每個小空間大小是 1 個 page,每個小空間用於一個目的,這些目的定義在 kmap_types.h 中的 km_type 中。當要進行一次臨時映射的時候,需要指定映射的目的,根據映射目的,可以找到對應的小空間,然後把這個空間的地址作為映射地址。這意味著一次臨時映射會導致以前的映射被覆蓋。
通過 kmap_atomic() 可實現臨時映射。可以用在中斷處理函數和可延遲函數的內部,從不阻塞。因為臨時內存映射是固定內存映射的一部分,一個地址固定給一個內核成分使用。