現代作業系統普遍採用虛擬記憶體管理(Virtual Memory Management)機制,這需要處理器中的MMU(Memory Management Unit,記憶體管理單元)提供支援,本節簡要介紹MMU的作用。
首先引入兩個概念,虛擬位址和物理地址。如果處理器沒有MMU,或者有MMU但沒有啟用,CPU執行單元發出的記憶體位址將直接傳到晶片引腳上,被記憶體晶片(以下稱為實體記憶體,以便與虛擬記憶體區分)接收,這稱為物理地址(Physical Address,以下簡稱PA),如所示。
圖 17.5. 物理地址
如果處理器啟用了MMU,CPU執行單元發出的記憶體位址將被MMU截獲,從CPU到MMU的地址稱為虛擬位址(Virtual Address,以下簡稱VA),而MMU將這個地址翻譯成另一個地址發到CPU晶片的外部地址引腳上,也就是將VA映射成PA,如所示。
圖 17.6. 虛擬位址
如果是32位處理器,則內地址匯流排是32位的,與CPU執行單元相連(圖中只是示意性地畫了4條地址線),而經過MMU轉換之後的外地址匯流排則不一定是32位的。也就是說,虛擬位址空間和物理地址空間是獨立的,32位處理器的虛擬位址空間是4GB,而物理地址空間既可以大於也可以小於4GB。
MMU將VA映射到PA是以頁(Page)為單位的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個映射項將VA的一頁0xb7001000~0xb7001fff映射到PA的一頁0x2000~0x2fff,如果CPU執行單元要訪問虛擬位址0xb7001008,則實際訪問到的物理地址是0x2008。實體記憶體中的頁稱為物理頁面或者頁幀(Page Frame)。虛擬記憶體的哪個頁面映射到實體記憶體的哪個頁幀是通過頁表(Page Table)來描述的,頁表儲存在實體記憶體中,MMU會尋找頁表來確定一個VA應該映射到什麼PA。
作業系統和MMU是這樣配合的:
作業系統在初始化或分配、釋放記憶體時會執行一些指令在實體記憶體中填寫頁表,然後用指令設定MMU,告訴MMU頁表在實體記憶體中的什麼位置。
設定好之後,CPU每次執行訪問記憶體的指令都會自動引發MMU做查表和地址轉換操作,地址轉換操作由硬體自動完成,不需要用指令控制MMU去做。
我們在程式中使用的變數和函數都有各自的地址,程式被編譯後,這些地址就成了指令中的地址,指令中的地址被CPU解釋執行,就成了CPU執行單元發出的記憶體位址,所以在啟用MMU的情況下,程式中使用的地址都是虛擬位址,都會引發MMU做查表和地址轉換操作。那為什麼要設計這麼複雜的記憶體管理機制呢?多了一層VA到PA的轉換到底換來了什麼好處?All problems in computer science can be solved by another level of indirection.還記得這句話嗎?多了一層間接必然是為瞭解決什麼問題的,等講完了必要的預備知識之後,將在第 5 節 “虛擬記憶體管理”討論虛擬記憶體管理機制的作用。
MMU除了做地址轉換之外,還提供記憶體保護機制。各種體繫結構都有使用者模式(User Mode)和特權模式(Privileged Mode)之分,作業系統可以在頁表中設定每個記憶體頁面的存取權限,有些頁面不允許訪問,有些頁面只有在CPU處於特權模式時才允許訪問,有些頁面在使用者模式和特權模式都可以訪問,存取權限又分為可讀、可寫和可執行三種。這樣設定好之後,當CPU要訪問一個VA時,MMU會檢查CPU當前處於使用者模式還是特權模式,訪問記憶體的目的是讀資料、寫資料還是取指令,如果和作業系統設定的頁面許可權相符,就允許訪問,把它轉換成PA,否則不允許訪問,產生一個異常(Exception)。異常的處理過程和中斷類似,不同的是中斷由外部裝置產生而異常由CPU內部產生,中斷產生的原因和CPU當前執行的指令無關,而異常的產生就是由於CPU當前執行的指令出了問題,例如訪問記憶體的指令被MMU檢查出許可權錯誤,除法指令的除數為0等都會產生異常。
圖 17.7. 處理器模式
通常作業系統把虛擬位址空間劃分為使用者空間和核心空間,例如x86平台的Linux系統虛擬位址空間是0x00000000~0xffffffff,前3GB(0x00000000~0xbfffffff)是使用者空間,後1GB(0xc0000000~0xffffffff)是核心空間。使用者程式載入到使用者空間,在使用者模式下執行,不能訪問核心中的資料,也不能跳轉到核心代碼中執行。這樣可以保護核心,如果一個進程訪問了非法地址,頂多這一個進程崩潰,而不會影響到核心和整個系統的穩定性。CPU在產生中斷或異常時不僅會跳轉到中斷或異常服務程式,還會自動切換模式,從使用者模式切換到特權模式,因此從中斷或異常服務程式可以跳轉到核心代碼中執行。事實上,整個核心就是由各種中斷和例外處理常式組成的。總結一下:在正常情況下處理器在使用者模式執行使用者程式,在中斷或異常情況下處理器切換到特權模式執行核心程式,處理完中斷或異常之後再返回使用者模式繼續執行使用者程式。
段錯誤我們已經遇到過很多次了,它是這樣產生的:
使用者程式要訪問的一個VA,經MMU檢查無權訪問。
MMU產生一個異常,CPU從使用者模式切換到特權模式,跳轉到核心代碼中執行異常服務程式。
核心把這個異常解釋為段錯誤,把引發異常的進程終止掉。
http://learn.akae.cn/media/ch17s04.html