Linux 記憶體管理 高階記憶體的對映方式

2021-06-18 19:25:28 字數 4549 閱讀 8267

cited from 

解釋一:

高階記憶體是指實體地址大於 896m 的記憶體。

對於這樣的記憶體,無法在「核心直接對映空間」進行對映。

為什麼?

因為「核心直接對映空間」最多只能從 3g 到 4g,只能直接對映 1g 物理記憶體,對於大於 1g 的物理記憶體,無能為力。

實際上,「核心直接對映空間」也達不到 1g, 還得留點線性空間給「核心動態對映空間」 呢。

因此,linux 規定「核心直接對映空間」 最多對映 896m 物理記憶體。

對 於高階記憶體,可以通過 alloc_page() 或者其它函式獲得對應的 page,但是要想訪問實際物理記憶體,還得把 page 轉為線性位址才行(為什麼?想想 mmu 是如何訪問物理記憶體的),也就是說,我們需要為高階記憶體對應的 page 找乙個線性空間,這個過程稱為高階記憶體對映。

高階記憶體對映有三種方式:

1、對映到「核心動態對映空間」

這種方式很簡單,因為通過 vmalloc() ,在」核心動態對映空間「申請記憶體的時候,就可能從高階記憶體獲得頁面(參看 vmalloc 的實現),因此說高階記憶體有可能對映到」核心動態對映空間「 中。

2、永久核心對映

如果是通過 alloc_page() 獲得了高階記憶體對應的 page,如何給它找個線性空間?

核心專門為此留出一塊線性空間,從 pkmap_base 到 fixaddr_start ,用於對映高階記憶體。在 2.4 核心上,這個位址範圍是 4g-8m 到 4g-4m 之間。這個空間起叫「核心永久對映空間」或者「永久核心對映空間」

通常情況下,這個空間是 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() 可實現臨時對映。

下圖簡單簡單表達如何對高階記憶體進行對映

解釋二:

高階記憶體含義為:線性位址空間 page_offset + 896m 至4g的最後128m線性位址 <==對映==> 896m以上的物理頁框,非直接對映。有3種方法:非連續記憶體區對映,永久核心對映,臨時核心對映(固定對映)

從 page_offset開始的線性位址區域為:

page_offset(3g)|物理記憶體對映 --8m-- vmalloc區 --4k-- vmalloc區 --8k-- 永久核心對映--臨時核心對映(固定對映)|4g

1. 非連續區對映

1.1 每個非連續記憶體區都對應乙個型別為 vm_struct的描述符,通過next欄位,這些描述符被插入到乙個vmlist鍊錶中。

1.2 三種非連續區的型別:

vm_alloc   -- 物理記憶體(呼叫alloc_page)和線性位址同時申請,物理記憶體是 __gfp_highmem型別(分配順序是high, normal , dma )(可見vmalloc不僅僅可以對映 __gfp_highmem頁框,它的主要目的是為了將零散的,不連續的頁框拼湊成連續的核心邏輯位址空間... )

vm_map     -- 僅申請線性區,物理記憶體另外申請,是vm_alloc的簡化版

vm_ioremap -- 僅申請線性區,物理記憶體另外申請(這裡的物理記憶體一般都是高階記憶體,大於896m的記憶體)

2. 永久核心對映

2.2 如果last_pkmap個項都用完,則把當前程序置為 task_uninterruptible,並呼叫schedule()

3. 臨時記憶體對映

3.1 可以用在中斷處理函式和可延遲函式的內部,從不阻塞。因為臨時記憶體對映是固定記憶體對映的一部分,乙個位址固定給乙個核心成分使用。

3.2 每個cpu都有自己的乙個13個視窗(乙個線性位址及頁表項)的集合。

enum km_type ;

3.3 注意 fixed_addresses 的位址從上至下是倒著的,fix_hole的位址等於 0xfffff000,是乙個洞

#define __fix_to_virt(x)    (fixaddr_top - ((x) << page_shift))

#define __fixaddr_top    0xfffff000

解釋三:

vmalloc_reserve和896m

linux 核心虛擬位址空間到實體地址空間一般是固定連續影射的。

假定機器記憶體為512m,

從3g開始,到3g + 512m 為連續固定影射區。zone_dma, zone_normal為這個區域的。固定影射的vaddr可以直接使用(get a free page, then use pfn_to_virt()等巨集定義轉換得到vaddr)或用kmalloc等分配. 這樣的vaddr的物理頁是連續的。得到的位址也一定在固定影射區域內。

如果記憶體緊張,連續區域無法滿足,呼叫vmalloc分配是必須的,因為它可以將物理不連續的空間組合後分配,所以更能滿足分配要求。vmalloc可以對映高階頁框,也可以對映底端頁框。vmalloc的作用只是為了提供邏輯上連續的位址。。 。

但vmalloc分配的vaddr一定不能與固定影射區域的vaddr重合。因為vaddr到物理頁的影射同時只能唯一。所以vmalloc得到的 vaddr要在3g + 512m 以上才可以。也就是從vmalloc_start開始分配。 vmalloc_start比連續固定影射區大最大vaddr位址還多8-16m(2*vmalloc_offset)--有個鬼公式在

#define vmalloc_offset   8*1024

#define vmalloc_start   (high_memory - 2*vmalloc_offset) & ~(vmalloc_offset-1)

high_memory 就是固定影射區域最高處。

空開8-16m做什麼? 為了捕獲越界的mm_fault.

同樣,vmalloc每次得到的vaddr空間中間要留乙個page的空(空洞),目的和上面的空開一樣。你vmalloc(100)2次,得到的2個位址中間相距8k。

如果連續分配無空洞,那麼比如

p1=vmalloc(4096);

p2=vmalloc(4096);

如果p1使用越界到p2中了,也不會mm_****t. 那不容易debug.

下面說明vmalloc_reserve和896m的問題。

上面假設機器物理512m的case. 如果機器有1g物理記憶體如何是好?那vmalloc()的vaddr是不是要在3g + 1g + 8m 空洞以上分配?超過定址空間了嗎。

這時,4g 下面保留的vmalloc_reserve 128m 就派上用場了。

也就是說如果物理記憶體超過896m, high_memory也只能在3g + 896地方。可定址空間最高處要保留vmalloc_resreve 128m給vmalloc用。

所以這128m的vaddr空間是為了vmalloc在物理超過了896m時候使用。如果物理僅僅有512m, 一般使用不到。因為vmalloc_start很低了。如果vmalloc太多了才會用到。

high_memory在arch/i386/kernel, mm的初始化中設定。根據物理記憶體大小和vmalloc_reserve得到數值.

所以說那128m的核心線性位址僅僅是為了影射1g以上的物理記憶體的不對的。如果物理記憶體2g,1g以下的vmalloc也用那空間影射。總之,核心的高階線性位址是為了訪問核心固定對映以外的記憶體資源

看vmalloc分配的東西可以用

show_vmalloc()

}使用者空間當然可以使用高階記憶體,而且是正常的使用,核心在分配那些不經常使用的記憶體時,都用高階記憶體空間(如果有),所謂不經常使用是相對來說的,比如核心的一些資料結構就屬於經常使用的,而使用者的一些資料就屬於不經常使用的。

使用者在啟動乙個應用程式時,是需要記憶體的,而每個應用程式都有3g的線性位址,給這些位址對映頁表時就可以直接使用高階記憶體。

而且還要糾正一點的是:那128m線性位址不僅僅是用在這些地方的,如果你要載入乙個裝置,而這個裝置需要對映其記憶體到核心中,它也需要使用這段線性位址空間來完成,否則核心就不能訪問裝置上的記憶體空間了。

總之,核心的高階線性位址是為了訪問核心固定對映以外的記憶體資源

實際上高階記憶體是針對核心一段特殊的線性空間提出的概念,和實際的物理記憶體是兩碼事。程序在使用記憶體時,觸發缺頁異常,具體將哪些物理頁對映給使用者程序是核心考慮的事情。在使用者空間中沒有高階記憶體這個概念。

解釋四(**解釋 是基於linux2.4核心**的)

c語言學習筆記 記憶體管理 linux的記憶體映像

程式在記憶體中的儲存形式 程式儲存在flash中 程式在記憶體中主要分為以下幾段 段,資料段,唯讀資料段,bss段,堆,棧,檔案對映區,核心對映區 作業系統核心 程式一般存放在 段,在linux中又稱為文字段 資料段存放的一般是靜態變數和被初始化為非零值的全域性變數 bss段存放的是被初始化為零的全...

Linux記憶體管理之高階記憶體對映

分析完linux記憶體管理的基本概念與實現之後,就可以接著分析使用者空間與核心空間的互動操作了。brk系統呼叫屬於那種常用但是 可見度 不高的操作,常用於使用者空間堆的管理 請參閱本站的 中的malloc機制分析 一文 brk在使用者空間的介面為int brk void end data segme...

Linux記憶體管理 核心高階記憶體

linux核心位址對映模型 x86 cpu採用了段頁式位址對映模型。程序 中的位址為邏輯位址,經過段頁式位址對映後,才真正訪問物理記憶體。段頁式機制如下圖 linux核心位址空間劃分 通常32位linux核心位址空間劃分0 3g為使用者空間,3 4g為核心空間。注意這裡是32位核心位址空間劃分,64...