歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Windows/Linux內核地址空間管理的異同

Windows/Linux內核地址空間管理的異同

日期:2017/2/28 14:38:33   编辑:Linux內核

相信很多人都知道Windows頁表自映射一說,也曉得Linux內核的一一線性映射。然而很多人也僅僅就是知道而已,記住一個結論比理解一個原因要簡單得多。

上周末,有人極具挑釁態度的問我能否分別用一句話描述它們,我承認我不是布道者,也難以說出”道可道,非常道“的玄語,但我十分贊同老子的觀點,能說出來的道就不是大道,雖難以說出,但卻可以解釋,說到解釋,那就是越詳細越好了,冗長並不總是貶義詞。在這樣的心理慰藉下,才會有以下的文字,雖已深更,卻不無聊...

本文基於32位Intel體系結構討論!怕打字跟不上思維,有些地方只好缺失嚴謹性,比如在應該寫下”1G內存或者2G內存(後者處在2G/2G模式下)“的時候,我會直接寫”1G內存“,但是並不總是這樣。

1.虛擬地址空間概述

現代操作系統上,物理內存不再對程序可見。也就是說,程序指令本身以及其訪問的任何數據都處在虛擬地址空間,機器通過一個叫做MMU的機構將其映射為真實的物理內存頁面。

程序直接訪問的地址為虛擬地址,訪問地址(也包含取指等)會觸發MMU工作,MMU自動將訪問的地址映射到真實的物理地址,如果沒有分配物理頁面,將會觸發缺頁異常,系統捕獲該異常,之後默默地分配一個頁面,重新發起由於沒有分配頁面而失敗的訪問,所有這一切都是自動且默默地發生的,對應用程序是完全透明的。頁面調度這個機制完美地迎合了程序訪問的局部性原則。

虛擬地址填充整個32位地址空間,為了管理的高效性,很多的系統將這32位的地址空間拆分成了兩個部分,即用戶空間和內核空間。但是記住,這個拆分並不是必須的!所謂的內核空間和用戶空間在Intel體系上表現為特權環0和特權環3。根本上的意義是,一個任務有一個滿32位的地址空間,如果某個系統將進程32位的地址空間拆成了兩個部分,那麼則說明該任務進程本身包含內核特權環0的部分,如果沒有拆分,那麼就說明該任務進程沒有內核部分。Remenber,一個滿32位的地址空間使用一套MMU頁表!

如果一個進程沒有內核部分,當系統中斷,系統異常,或者該進程本身調用系統調用的時候,怎麼辦呢?不要被現有的Linux,Windows的實現迷惑了,再次聲明,拆分地址空間並不是必須的!如果在沒有拆分地址空間的情況下出現上述情況,很簡單,切換MMU頁表即可,也就是說,系統單獨維護一個滿32位的內核地址空間為所有的滿32位地址空間的進程服務!

說了這麼多,該來點實例了,我們熟知的Linux,Windows系統,可以支持3G/1G模式,意即滿32位的進程地址空間中,用戶態占3G,內核態占1G;可以是2G/2G模式,解釋同上,這些都是拆分地址空間的情況,這些情況在進入內核態的時候叫做陷入內核,因為即使進入了內核態,還處在同一個地址空間中,並不切換CR3寄存器。還有一種模式是4G/4G模式,即不拆分地址空間的情況,內核單獨占有一個4G的地址空間,所有的用戶進程獨享自己的4G地址空間,這種模式下,在進入內核態的時候,叫做切換到內核,因為需要切換CR3寄存器(切換MMU頁表),所以進入了不同的地址空間!

說到這裡,應該知道為何文檔上說4G/4G模式雖然解放了內核地址空間,使其可以容納更多的管理機構,然而會付出一點小的代價了吧,所謂的代價就是切換CR3以及所有因此而引發的副作用!

2.Windows地址空間

一直我都以為,一個好的開始會帶來令人愉快的結果,一個不好的開始會讓人很累!確實是這樣。很多人想理解Windows頁表自映射,然後去google,去百度,得到的結果幾乎都是在解釋以下這個宏定義:

#define MiGetVirtualAddressMappedByPte(PTE) ((PVOID)((ULONG)(PTE) << 10))

於是很多人都在糾結於那個魔術字10,畫了N個圖,但是基本都是在抄襲Dave Probert很久前寫的一篇文章《Windows Kernel Internals II Processes, Threads, VirtualMemory》。關鍵是最終還是沒有講明白。本來是一個很簡單的事情,被卻無端復雜化了。我覺得就是沒有找到一個好的開始。什麼是好的開始呢?

我認為我找到了,那就是WIndows進程虛擬地址空間的布局!如果這個布局設計的原則你搞明白了,那些宏你自己也能寫出來了!不管怎麼說,在看圖之前,還是要先說一下Windows地址空間設計的原則,那就是:每個進程擁有自己單獨的滿32位地址空間!不管是3G/1G模式,還是2G/2G模式,還是4G/4G模式,每個進程都是獨立的虛擬地址空間,這也是現代操作系統的設計原則,並非Windows獨創。在這些單獨的地址空間中,所有進程擁有相同的映射規則,比如虛擬地址XX不管在進程A還是在進程B,映射的都是自己的進程控制塊PCB...如下圖所示:

其實知道了這個,悟性好的同學可能已經知道自映射的設計細節了,但是我還是繼續下去吧,以免讓人家說我虎頭蛇尾。

頁表自映射,一個神奇的映射方式,為什麼呢?它可不僅僅是為了節省4K的內存空間,雖然它確實可以節省4K的內存空間。最要緊的是,頁表自映射機制提供了一套內核空間直接訪問任意頁面的高效方式。在講頁表自映射前,我先說一下WIndows的線性映射機制,即“頁表項虛擬地址和進程地址空間虛地址頁面的線性映射關系”,簡稱頁表的線性映射。(注意和下一節中要講的Linux的內核虛擬地址和物理地址的線性映射相區分)

Windows頁表的線性映射理解起來很簡單。Windows的所有頁表處在地址空間的固定部分且按照虛擬地址連續分布,那麼所有的頁表項的虛擬地址也是連續分布,從最開始處,連續的頁表項負責連續的虛擬地址的映射,如下圖所示:

注意,直到現在,我都沒有提到頁目錄,因為頁目錄純粹是為了多級頁表而引入的,Windows只是借助了頁目錄的概念,無形中用將頁表映射到虛擬地址空間而取消了頁目錄帶來的4K開銷。Windows只是在地址空間的固定位置開始連續映射所有的頁表,這些頁表當中存在一個頁表的頁表,即頁目錄,頁目錄就這樣湮沒在頁表中了。且往下看!

有了這個基礎做依托,後面的自映射以及神奇的宏就是一個自然而然的結果了。為何這麼說呢?分別來說。

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2013-11/92833p2.htm

Linux Kernel 的詳細介紹:請點這裡
Linux Kernel 的下載地址:請點這裡

推薦閱讀:

Linux 3.12代號Suicidal Squirrel http://www.linuxidc.com/Linux/2013-09/90023.htm

怎樣在 Ubuntu 上安裝 Linux 3.11 內核 http://www.linuxidc.com/Linux/2013-09/89674.htm

Ubuntu 13.10 (Saucy Salamander) 內核已升級至 Linux Kernel 3.10 RC5 http://www.linuxidc.com/Linux/2013-06/86110.htm

Linux Kernel 3.4.62 LTS 現已經提供下載 http://www.linuxidc.com/Linux/2013-09/90368.htm

Copyright © Linux教程網 All Rights Reserved