在32位的系統上,核心使用第3GB~第4GB的線性地址空間,共1GB大小。核心將其中的前896MB與實體記憶體的0~896MB進行直接對映,即線性對映,將剩餘的128M線性地址空間作為訪問高於896M的記憶體的一個視窗。
引入高階記憶體對映這樣一個概念的主要原因就是我們所安裝的記憶體大於1G時,核心的1G線性地址空間無法建立一個完全的直接對映來觸及整個實體記憶體空間,而對於80x86開啟PAE的情況下,允許的最大實體記憶體可達到64G,因此核心將自己的最後128M的線性地址空間騰出來,用以完成對高階記憶體的暫時性對映。
而在64位的系統上就不存在這樣的問題了,因為可用的線性地址空間遠大於可安裝的記憶體。下圖描述了核心1GB線性地址空間是如何劃分的。
其中PAGE_OFFSET表示核心使用的1GB線性地址的起始處(第3GB),high_memory往右的部分則表示高階記憶體,共 128M
的線性地址。可以用來完成上述對映目的的區域為vmalloc area,Persistent kernel mappings區域和固定對映線性地址空間中的FIX_KMAP區域,這三個區域對應的對映機制分別為 非連續記憶體分配
, 永久核心對映
和 臨時核心對映
。
永久核心對映
在核心初始化頁表管理機制時,專門用pkmap_page_table這個變數儲存了PKMAP_BASE對應的頁表項的地址,由pkmap_page_table來維護永久核心對映區的頁表項的對映,頁表項總數為LAST_PKMAP個。
這裡的永久並不是指呼叫kmap()建立的對映關係會一直持續下去無法解除,而是指在呼叫kunmap()解除對映之間這種對映會一直存在,這是相對於臨時核心對映機制而言的。
需要注意一點的是,當永久核心對映區沒有空閒的頁表項可供對映時,請求對映的程序會被阻塞,因此永久核心對映請求不能發生在中斷和可延遲函式中。
臨時核心對映
臨時核心對映和永久核心對映相比,其最大的特點就是不會阻塞請求對映頁框的程序,因此臨時核心對映請求可以發生在中斷和可延遲函式中。系統中的每個CPU都有自己的13個臨時核心對映視窗,根據不同的需求(用於核心控制路徑),選擇不同的視窗來建立對映。每個CPU的對映視窗集合用 enum km_type
資料結構表示,該資料結構中的每個符號,如 KM_BOUNCE_READ
、 KM_USER0
或 KM_PTE0
,標識了視窗的線性地址,其實是一個下標。當要核心建立一個臨時對映時,通過 cpu_id
和 視窗下標
來確定線性地址。
臨時核心對映的實現也比永久核心對映要簡單,當一個程序申請在某個視窗建立對映,即使這個視窗已經在之前就建立了對映,新的對映也會建立並且覆蓋之前的對映,所以說這種對映機制是臨時的,並且不會阻塞當前程序。
非連續記憶體分配
非連續記憶體分配是指將實體地址不連續的頁框對映到線性地址連續的線性地址空間,主要應用於大容量的記憶體分配。採用這種方式分配記憶體的主要優點是避免了外部碎片,而缺點是必須打亂核心頁表,而且訪問速度較連續分配的物理頁框慢。
非連續記憶體分配的線性地址空間是從 VMALLOC_START
到 VMALLOC_END
,每當核心要用vmalloc類的函式進行非連續記憶體分配,就會申請一個vm_struct結構來描述對應的vmalloc區,兩個vmalloc區之間的間隔至少為一個頁框的大小,即PAGE_SIZE。
總結
由於核心的線性地址空間有限,因此採取上面介紹的三種方式來對映高階記憶體。但是每種對映的本質都是通過頁表來建立線性地址與實體地址之間的聯絡。
永久核心對映和臨時核心對映,都由核心指定了需要進行對映的頁面,也就是說指定了頁描述符(頁描述符和物理頁框之間的關係是固定不可變的)。在永久核心對映中,核心只需要在永久核心對映區找到空閒的,也就是未被對映的線性地址對應的頁表項,然後將其分配給page即可,若找不到則將阻塞申請建立對映的程序;而臨時核心對映更直接,連進行對映的線性地址視窗都是固定的,若是其已經分配給了某個頁框,則直接搶過來用,因此之前的對映就被覆蓋了,體現出了臨時性。
非連續記憶體分配,核心不用指定具體的頁框,只需指定要申請的記憶體大小,核心將在非連續記憶體分配區找到一塊相應大小虛擬地址空間,然後再由夥伴系統分配頁框,還要通過slab分配器為一些資料結構分配記憶體,最後再用同樣的方式(設定PTE表項)來建立對映。