首先借用網上一張圖,感覺這個比較清晰:
Linux系統虛擬內存空間一般布局示意圖
1.1 線性空間
線性地址空間:是指Linux系統中從0x00000000到0xFFFFFFFF整個4GB虛擬存儲空間。線性空間又分為用戶空間和內核空間。
1.1.1 用戶空間(進程地址空間)
用戶空間是指從0x00000000到0xBFFFFFFF共3GB的線性地址空間,每個進程都有一個獨立的3GB用戶空間,所以用戶空間由每個進程獨有,但是內核線程沒有用戶空間,因為它不產生用戶空間地址。另外子進程共享(繼承)父進程的用戶空間只是使用與父進程相同的用戶線性地址到物理內存地址的映射關系,而不是共享父進程用戶空間。運行在用戶態和內核態的進程都可以訪問用戶空間。
linux采用虛擬內存管理技術,每一個進程都有一個3G大小的獨立的進程地址空間,這個地址空間就是用戶空間。每個進程的用戶空間都是完全獨立、互不相干的。進程訪問內核空間的方式:系統調用和中斷。
創建進程等進程相關操作都需要分配內存給進程。這時進程申請和獲得的不是物理地址,僅僅是虛擬地址。 實際的物理內存只有當進程真的去訪問新獲取的虛擬地址時,才會由“請頁機制”產生“缺頁”異常,從而進入分配實際頁框的程序。該異常是虛擬內存機制賴以存在的基本保證,它會告訴內核去為進程分配物理頁,並建立對應的頁表,這之後虛擬地址才實實在在的映射到了物理地址上。
1.1.2 內核空間
內核空間表示運行在處理器最高級別的超級用戶模式(supervisor mode)下的代碼或數據,內核空間占用從0xC0000000到0xFFFFFFFF的1GB線性地址空間,內核線性地址空間由所有進程共享,但只有運行在內核態的進程才能訪問,用戶進程可以通過系統調用切換到內核態訪問內核空間,進程運行在內核態時所產生的地址都屬於內核空間。
內核空間又可分為以下幾個線性空間:
1. 內核邏輯地址空間
是指從PAGE_OFFSET(3G)3G+896)之間的線性地址空間,是系統物理內存映射區,它映射了全部或部分(如果系統包含高端內存)物理內存。內核邏輯地址空間中的地址與RAM內存物理地址空間中對應的地址只差一個固定偏移量(3G),如果RAM內存物理地址空間從0x00000000地址編址,那麼這個偏移量就是PAGE_OFFSET。
2. 高端線性地址空間:從high_memory(3G+896M)到0xFFFFFFFF之間的線性地址空間屬於高端線性地址空間,其中VMALLOC_START~VMALLOC_END之間線性地址:(1)被vmalloc()函數用來分配物理上不連續但線性地址空間連續的高端物理內存,或者(2)被vmap()函數用來映射高端或低端物理內存,或者(3)由ioremap()函數來重新映射I/O物理空間。其中PKMAP_BASE開始的LAST_PKMAP(一般等於1024)頁線性地址空間:被kmap()函數用來永久映射高端物理內存。FIXADDR_START開始的KM_TYPE_NR*NR_CPUS頁線性地址空間:被kmap_atomic()函數用來臨時映射高端物理內存,其他未用高端線性地址空間可以用來在系統初始化期間永久映射I/O地址空間。
1.2 物理空間 1.2.1 低端內存(物理內存)
內核邏輯地址空間所映射物理內存就是低端內存(實際物理內存的大小,但是小於896),低端內存在Linux線性地址空間中始終有永久的一一對應的內核邏輯地址,系統初始化過程中將低端內存永久映射到了內核邏輯地址空間,為低端內存建立了虛擬映射頁表。低端內存內物理內存的物理地址與線性地址之間的轉換可以通過__pa(x)和__va(x)兩個宏來進行,#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) __pa(x)將內核邏輯地址空間的地址x轉換成對應的物理地址,相當於__virt_to_phys((unsigned long)(x)),__va(x)則相反,把低端物理內存空間的地址轉換成對應的內核邏輯地址,相當於((void *)__phys_to_virt((unsigned long)(x)))。
1.2.2 高端內存(物理內存)
低端內存地址之上的物理內存是高端內存(物理內存896之上),高端內存在Linux線性地址空間中沒有沒有固定的一一對應的內核邏輯地址,系統初始化過程中不會為這些內存建立映射頁表將其固定映射到Linux線性地址空間,而是需要使用高端內存的時候才為分配的高端物理內存建立映射頁表,使其能夠被內核使用,否則不能被使用。高端內存的物理地址於線性地址之間的轉換不能使用上面的__pa(x)和__va(x)宏。
高端內存概念的由來:如上所述,Linux將4GB的線性地址空間劃分成兩部分,從0x00000000到0xBFFFFFFF共3GB空間作為用戶空間由用戶進程獨占,這部分線性地址空間並沒有固定映射到物理內存空間上;從0xC0000000到0xFFFFFFFF的第4GB線性地址空間作為內核空間,在嵌入式系統中,這部分線性地址空間除了映射物理內存空間之外還要映射處理器內部外設寄存器空間等I/O空間。0xC0000000~high_memory之間的內核邏輯地址空間專用來固定映射系統中的物理內存,也就是說0xC0000000~high_memory之間空間大小與系統的物理內存空間大小是相同的(當然在配置了CONFIG_DISCONTIGMEMD選項的非連續內存系統中,內核邏輯地址空間和物理內存空間一樣可能存在內存孔洞),如果系統中的物理內存容量遠小於1GB,那麼內核線性地址空間中內核邏輯地址空間之上的high_memory~0xFFFFFFFF之間還有足夠的空間來固定映射一些I/O空間。可是,如果系統中的物理內存容量(包括內存孔洞)大於1GB,那麼就沒有足夠的內核線性地址空間來固定映射系統全部物理內存以及一些I/O空間了,為了解決這個問題,在x86處理器平台設置了一個經驗值:896MB,就是說,如果系統中的物理內存(包括內存孔洞)大於896MB,那麼將前896MB物理內存固定映射到內核邏輯地址空間0xC0000000~0xC0000000+896MB(=high_memory)上,而896MB之後的物理內存則不建立到內核線性地址空間的固定映射,這部分內存就叫高端物理內存。此時內核線性地址空間high_memory~0xFFFFFFFF之間的128MB空間就稱為高端內存線性地址空間,用來映射高端物理內存和I/O空間。896MB是x86處理器平台的經驗值,留了128MB線性地址空間來映射高端內存以及I/O地址空間,在嵌入式系統中可以根據具體情況修改這個阈值,比如,MIPS中將這個值設置為0x20000000B(512MB),那麼只有當系統中的物理內存空間容量大於0x20000000B時,內核才需要配置CONFIG_HIGHMEM選項,使能內核對高端內存的分配和映射功能。什麼情況需要劃分出高端物理內存以及高端物理內存阈值的設置原則見上面的內存頁區(zone)概念說明。