JOS | linux 神祕的0xC0000000&&核心邏輯地址&&核心虛擬地址&&直接對映&&高階記憶體
0xC0000000:3GB的起始地址。一個程序分為兩個部分:私有和全域性。私有部分是指程序自己的程式碼,而全域性部分則是指核心程式碼。區域性是程序私有的,而全域性則是所有程序公用的。理解這個只需要理解頁表即可:4GB記憶體,0-3GB用於使用者,3-4GB用於核心,也就是說AB兩個程序,頁表中0-3GB分別對映到了不同的實體記憶體,而3-4GB都是對映到了同一片實體記憶體即核心程式碼段(核心可以通過複製把核心部分頁表項複製到使用者程序的頁表中)。(“《ULK》 P74翻譯版“原話”:主核心全域性目錄的最高目錄項部分作為參考模型,給每個普通程序對應的頁全域性目錄項提供參考模型 。 ”)。
csdn部落格-核心的連線指令碼vmlinux.lds中
. = PAGE_OFFSET + TEXT_OFFSET; 將我們的核心程式碼編譯到0xc0000000+TEXT_OFFSET處。就像我們做的嵌入式實驗一樣,編譯時我們的程式碼預設會載入到0x30000000處執行一樣,而啟動程式碼剛開始是載入到0x0處,編譯好的程式碼需要在地址0x0處執行一段,因為在把我們的程式碼複製到0x30000000之前程式碼中使用的都是相對跳轉和相對偏移,並不涉及存放資料的地址,所以即使我們編譯時設定執行地址是0x30000000而實際在地址0x0處執行並不會出錯。(要想深刻理解偏移量,則可以嘗試寫一個程式,從真實模式跳入保護模式,見《x86組合語言:從真實模式到保護模式》第十一章,當你犯夠了錯誤才能理解了這個偏移量使用不當致錯的原因,一旦理解,那麼許多東西就很容易懂了)。
linux也一樣,我們編譯linux是編譯到PAGE_OFFSET + TEXT_OFFSET處,而系統啟動時並不是一開始就跳到0xC00000000以後的核心空間,而是會跳到固定地址(8086是FFFF:0000)處執行,然後再跳到核心空間。(PAGE_OFFSET:linux-2.6.0\include\asm-i386\page.h,i386中是0xc0000000
TEXT_OFFSET:linux-2.6.0\arch\arm\Makefile,其中定義了TEXTADDR 0xc0008000,可得出TEXT_OFFSET=0x8000)。linux-2.6.0\arch\i386\kernel\head.S中使用了三個變數,pg0,swapper_pg_dir,mmu_cr4_features,linux以後肯定會開啟分頁,所以這些變數的地址都是虛擬地址,而因為此時還未開啟分頁,所以又必須使用實體地址,所以我們會注意到在使用這三個變數時使用的都是 (var_addr - __PAGE_OFFSET)來獲得變數地址,也就是說將虛擬地址直接減去一個固定的值就得到了其真正的實體地址,如《ULK》第三版P74倒數兩行所說(他只是舉了個例子說是8M):“分頁第一個階段的目標是允許在真實模式和保護模式下(筆者注:此時肯定開啟了分頁)都很容易對這8MB定址。因此核心必須建立一個對映,把從0x0~0x007fffff的線性地址和從0xc0000000~0xc07fffff的線性地址對映到從0x0~0x007fffff的實體地址”,所以說linux核心頁表0xc0000000開始的一段線性地址空間必須對映到0x0處。因為32位的話linux核心只佔據3GB到4GB這1GB的線性地址空間,所以需要留出一部分線性地址空間用做高階記憶體,即用做動態對映
核心邏輯地址&&核心虛擬地址個人筆記:
理解了0xc0000000,那麼核心邏輯地址和核心虛擬地址就很好理解了。因為0xc0000000開始的一部分連續線性地址必須對映到實體地址0x0處開始的一段連續記憶體,那麼這部分實體地址空間就可以看做一個段地址為0x0的段。舉個例子,假設有線性地址0xc09110111,採用的是直接對映,於是便可以得到其對應的實體地址為0x00911011(0x00911011=0xc09110111-0xc00000)因為段基址為0,那麼這個0x009110111便可以看做是段內偏移,而X86系列的段式記憶體管理中把這個段內偏移叫做邏輯地址,其實際地址=段地址(0)+段內偏移=段內偏移,也就是說可以把這個採用直接對映的與實體地址一 一對應的線性地址部分看做是以0x0為段基址的段的一個段內偏移(線性地址減去一個固定值便可得到,這個固定值就是0xC0000000),所以給這段線性地址重新取個名字,叫邏輯地址,所以說linux的核心邏輯地址一定是核心虛擬地址,而因為linux中有一部分線性地址(比如高階記憶體)不是採用直接對映的方式,.假設有個地址0xCfff0000不屬於直接對映的線性地址範圍,其對映的實體地址是隨機的不等於段地址(0xC)+(0xCfff0000),也就是不可以把線性地址0xCfff0000看做段內偏移,所以說核心虛擬地址不一定是核心邏輯地址。
高階記憶體個人筆記:
假設PC安裝了2G記憶體
因為linux中一個程序分為3GB使用者空間和1GB核心空間,即線性地址0xc00000000xffffffff,故核心最多隻能訪問0xffffffff-0xc0000000即1GB記憶體。linux將0xC0000000開始的896MB虛擬地址直接對映到實體地址00x896MB處,然後當假設核心需要訪問大於1Gb的實體地址時便將剩下的128M左右的虛擬地址空間對映都你想要訪問的實體地址。舉個例子,核心想要訪問0x40000000(1GB)~0x40000000+X之間X M大小的實體地址空間時,便將0xf7000000~0xffffffff之間的一部分(假設從0xf9000000開始)線性地址即X M線性地址動態的對映到實體地址0x40000000~0x40000000+X,於是核心訪問線性地址0xf9000000+Y便可以訪問到實體地址0x40000000+Y(Y<X),也就是說臨時對映,訪問完了大於1Gb的實體地址後就釋放這個臨時的對映以便可以重複利用,也就是說當其他程序佔據高階記憶體卻沒有釋放時便會導致高階記憶體越來越少,以致當其他程序的核心部分想使用大於1GB的實體地址時無法辦到
回彈緩衝區-百度百科:
DMA:裝置使用的是實體地址,所以需要連續的物理頁,所以高階記憶體除非對映到連續物理頁,否則一般使用直接對映部分的地址空間做緩衝區