Linux 作業系統和驅動程式運行在核心空間,應用程式運行在使用者空間,兩者不能簡單地使用指標傳遞資料,因為Linux使用的虛擬記憶體機制,使用者空間的資料可能被換出,當核心空間使用使用者空間指標時,對應的資料可能不在記憶體中。
Linux核心地址空間劃分
通常32位Linux核心地址空間劃分0~3G為使用者空間,3~4G為核心空間。注意這裡是32位核心地址空間劃分,64位核心地址空間劃分是不同的。
1、x86的物理地址空間布局:
物理地址空間的頂部以下一段空間,被PCI裝置的I/O記憶體映射佔據,它們的大小和布局由PCI規範所決定。640K~1M這段地址空間被BIOS和VGA適配器所佔據。
Linux系統在初始化時,會根據實際的實體記憶體的大小,為每個物理頁面建立一個page對象,所有的page對象構成一個mem_map數組。
進一步,針對不同的用途,Linux核心將所有的物理頁面劃分到3類記憶體管理區中,如圖,分別為ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。
ZONE_DMA的範圍是0~16M,該地區的物理頁面專門供I/O裝置的DMA使用。之所以需要單獨管理DMA的物理頁面,是因為DMA使用物理地址訪問記憶體,不經過MMU,並且需要連續的緩衝區,所以為了能夠提供物理上連續的緩衝區,必須從物理地址空間專門劃分一段地區用於DMA。
ZONE_NORMAL的範圍是16M~896M,該地區的物理頁面是核心能夠直接使用的。
ZONE_HIGHMEM的範圍是896M~結束,該地區即為高端記憶體,核心不能直接使用。
2、linux虛擬位址核心空間分布
在kernel image下面有16M的核心空間用於DMA操作。位於核心空間高端的128M地址主要由3部分組成,分別為vmalloc area,持久化核心映射區,臨時核心映射區。
由於ZONE_NORMAL和核心線性空間存在直接映射關係,所以核心會將頻繁使用的資料如kernel代碼、GDT、IDT、PGD、mem_map數組等放在ZONE_NORMAL裡。而將使用者資料、頁表(PT)等不常用資料放在ZONE_ HIGHMEM裡,只在要訪問這些資料時才建立映射關係(kmap())。比如,當核心要訪問I/O裝置儲存空間時,就使用ioremap()將位於物理地址高端的mmio區記憶體映射到核心空間的vmalloc area中,在使用完之後便斷開映射關係。
3、linux虛擬位址使用者空間分布
使用者進程的代碼區一般從虛擬位址空間的0x08048000開始,這是為了便於檢查null 指標。代碼區之上便是資料區,未初始化資料區,堆區,棧區,以及參數、全域環境變數。
4、linux虛擬位址與物理地址映射的關係
Linux將4G的線性地址空間分為2部分,0~3G為user space,3G~4G為kernel space。
由於開啟了分頁機制,核心想要訪問物理地址空間的話,必須先建立映射關係,然後通過虛擬位址來訪問。為了能夠訪問所有的物理地址空間,就要將全部物理地址空間映射到1G的核心線性空間中,這顯然不可能。於是,核心將0~896M的物理地址空間一對一映射到自己的線性地址空間中,這樣它便可以隨時訪問ZONE_DMA和ZONE_NORMAL裡的物理頁面;此時核心剩下的128M線性地址空間不足以完全映射所有的ZONE_HIGHMEM,Linux採取了動態映射的方法,即按需的將ZONE_HIGHMEM裡的物理頁面映射到kernel space的最後128M線性地址空間裡,使用完之後釋放映射關係,以供其它物理頁面映射。雖然這樣存在效率的問題,但是核心畢竟可以正常的訪問所有的物理地址空間了。
5、buddyinfo的理解
cat /proc/buddyinfo 顯示如下:
Node 0, zone DMA 0 4 5 4 4 3 ...
Node 0, zone Normal 1 0 0 1 101 8 ...
Node 0, zone HighMem 2 0 0 1 1 0 ...
其中,Node表示在NUMA環境下的節點號,這裡只有一個節點0;zone表示每一個節點下的地區,一般有DMA、Normal和HignMem三個地區;後面的列表示,夥伴系統中每一個order對應的空閑頁面塊。例如,對於zone DMA的第二列(從0開始算起),空閑頁面數為5*2^4,可用記憶體為5*2^4*PAGE_SIZE。
計算方法就是:
當前列的數字*2^列數*PAGE_SIZE 其中列數是從0開始計算的,即第一列是 當前列的數字*2^0*PAGE_SIZE
常見問題:
1、使用者空間(進程)是否有高端記憶體概念。
使用者進程沒有高端記憶體概念。只有在核心空間才存在高端記憶體。使用者進程最多隻可以訪問3G實體記憶體,而核心進程可以訪問所有實體記憶體。
2、64位核心中有高端記憶體嗎。
目前現實中,64位Linux核心不存在高端記憶體,因為64位核心可以支援超過512GB記憶體。若機器安裝的實體記憶體超過核心地址空間範圍,就會存在高端記憶體。
3、使用者進程能訪問多少實體記憶體。核心代碼能訪問多少實體記憶體。
32位系統使用者進程最大可以訪問3GB,核心代碼可以訪問所有實體記憶體。
64位系統使用者進程最大可以訪問超過512GB,核心代碼可以訪問所有實體記憶體。
4、高端記憶體和物理地址、邏輯地址、線性地址的關係。
高端記憶體只和邏輯地址有關係,和邏輯地址、物理地址沒有直接關係。
5、為什麼不把所有的地址空間都分配給核心。
若把所有地址空間都給記憶體,那麼使用者進程怎麼使用記憶體。怎麼保證核心使用記憶體和使用者進程不起衝突。