本文內容參考自以下博客與書籍:html
http://blog.csdn.net/windowseight/article/details/8279863前端
http://bbs.chinaunix.net/thread-2083672-1-1.html程序員
http://blog.csdn.net/erazy0/article/details/6457626#comments編程
http://blog.csdn.net/drshenlei/article/details/4261909windows
http://duartes.org/gustavo/blog/post/memory-translation-and-segmentation數組
《操做系統概念》第六版中文版,高等教育出版社,第九章,鄭扣根譯。數據結構
《Operating System Concepts》7th Edition, 高等教育出版社。app
文中不少處是根據本身理解所寫,如有錯誤,歡迎指出和探討。ide
1. 物理地址和邏輯地址函數
物理地址:加載到內存地址寄存器中的地址,內存單元的真正地址。在前端總線上傳輸的內存地址都是物理內存地址,編號從0開始一直到可用物理內存的最高端。這些數字被北橋(Nortbridge chip)映射到實際的內存條上。物理地址是明確的、最終用在總線上的編號,沒必要轉換,沒必要分頁,也沒有特權級檢查(no translation, no paging, no privilege checks)。
邏輯地址:CPU所生成的地址。邏輯地址是內部和編程使用的、並不惟一。例如,你在進行C語言指針編程中,能夠讀取指針變量自己值(&操做),實際上這個值就是邏輯地址,它是相對於你當前進程數據段的地址(偏移地址),不和絕對物理地址相干。
爲何會有這兩種地址?
我的覺的緣由在於邏輯地址分配更加靈活,能夠容許不惟一,看起來也較爲直觀,例如,一段代碼中分配數組,邏輯地址上是連續的,然而在物理地址上,這個數組所佔用的頁可能分散開來,物理地址上就是不連續的,這樣對程序的可理解性上有影響。另外,有了邏輯地址這個概念,才能使用虛擬內存技術。
2. Paging,分頁內存管理方案
(1) 分頁的最大做用就在於:使得進程的物理地址空間能夠是非連續的。
物理內存被劃分爲一小塊一小塊,每塊被稱爲幀(Frame)。分配內存時,幀是分配時的最小單位,最少也要給一幀。在邏輯內存中,與幀對應的概念就是頁(Page)。
邏輯地址的表示方式是:前部分是頁碼後部分是頁偏移。
例如,已知邏輯空間地址爲2^m個字節(也就是說邏輯地址的長度是m位),已知頁大小是2^n字節。那麼一共能夠有2^(m-n)個頁。所以頁碼部分會佔m-n位,以後的n位,用來存儲頁偏移。
舉個例子, 頁大小爲4B,而邏輯內存爲32B(8頁),邏輯地址0的頁號爲0,頁號0對應幀5,所以邏輯地址映射爲物理地址5*4+0=20。邏輯地址3映射物理地址5*4+3=23。邏輯地址13(4*3+1,頁號爲3,偏移爲1,所以幀號爲2),映射到物理地址9。
採用分頁技術不會產生外部碎片(內存都被劃分爲幀),但可能產生內部碎片(幀已是最小單元,所以幀內部可能有空間沒有用到)。
按機率計算下來,每一個進程平都可有半個幀大小的內部碎片。
(2) 頁表的硬件實現
上一小節中寫到頁表是邏輯地址轉化到物理地址的關鍵所在。那麼頁表如何存儲?
每一個操做系統都有本身的方法來保存頁表。絕大多數都會爲每一個進程分配一個頁表。如今因爲頁表都比較大,因此放在內存中(以往是放在一組專用寄存器裏),其指針存在進程控制塊(PCB)裏,當進程被調度程序選中投入運行時,系統將其頁表指針從進程控制塊中取出並送入用戶寄存器中。隨後能夠根據此首地址訪問頁表。
頁表的存儲方式是TBL(Translation look-aside buffer, 翻譯後備緩衝器)+內存。TBL其實是一組硬件緩衝所關聯的快速內存。若沒有TBL,操做系統須要兩次內存訪問來完成邏輯地址到物理地址的轉換,訪問頁表算一次,在頁表中查找算一次。TBL中存儲頁表中的一小部分條目,條目以鍵值對方式存儲。
(3) 頁表的數據結構
a.
今年是2013年,現有的筆記本電腦,內存地址空間通常爲2^32字節以上。對於具備32位邏輯地址空間的計算機系統,若是系統的頁大小爲4KB(2^12B),那麼頁表能夠擁有2^(32-12)個,也就是一百多萬個條目,假設每一個條目佔有4B,那每一個進程都須要4MB的物理地址空間來存放頁表自己。並且,頁表自己須要分配在連續內存中。
爲此,Hierarchical Paging(層次化分頁)被提出,實際上就是將頁號分爲兩部分,第一部分做爲索引,第二部分做爲頁號的偏移。
以一個4kb頁大小的32位系統爲例。一個邏輯地址被分爲20位的頁碼和12位的頁偏移。由於要對頁表進行再分頁,因此該頁號可分爲10位的頁碼和10位的頁偏移。這樣一個邏輯地址就表示以下形式:
地址轉換過程以下:
地址由外向內轉換,所以此方法也被稱爲forward-mapped page table(向前映射表)。
b. Hashed Page Tables 哈希頁表
處理超過32位地址空間的經常使用方法是使用hashed page table(哈希頁表),並以虛擬頁碼做爲哈希值。哈希頁表的每一條目都包括一個鏈表的元素,這些元素哈希成同一位置。每一個元素有三個域:虛擬頁碼,所映射的幀號,指向鏈表中下一個元素的指針。
我的看來,哈希頁表的地址轉換方式,其實是Chaining(連接)方式,也就是一種哈希函數的溢出處理方式(另外一種溢出處理方式叫作Open Addressing,開放尋址),具體過程以下:
邏輯地址須要大於32bit的地址空間來表示,可是操做系統仍只有32bit來表示地址。此時人們便想到虛擬頁地址,虛擬地址能夠在32bit表示範圍以內,而後利用哈希函數完成邏輯地址到虛擬地址的映射,因爲虛擬地址更少,哈希函數會出現溢出,這裏使用Chaining來解決溢出。
邏輯地址中的頁號(下圖中的p)通過哈希函數的計算,算出虛擬地址中的頁號,根據虛擬頁號能夠在哈希表中以O(1)方式尋址,用p與鏈表中的每個元素的第一個域相比較。若是匹配,那麼相應的幀號就用來造成物理地址。若是不匹配,就對鏈表中的下一個節點進行比較,以尋找一個匹配的頁號。
c. Inverted page table 反向頁表
時間關係,這段暫時略過。
3. Segmentation,分段內存管理方案
採用分頁內存管理有一個不可避免的問題:用戶視角的內存和實際內存的分離。設想一段main函數代碼,裏面包含Sqrt函數的調用。按照編寫者的理解,這段代碼運行時,操做系統應該分配內存給:符號表(編譯時使用),棧(存放局部變量與函數參數值),Sqrt代碼段,主函數代碼段等。這樣,編寫者就能夠方便地指出:"函數sqrt內存模塊的第五條指令",來定位一個元素。而實際上,因爲採用Paging的管理方式,全部的一切都只是散落在物理內存中的各個幀上,並非以編寫者的理解來劃分模塊。
Segmentation的內存管理方式能夠支持這種思路。邏輯地址空間由一組段組成。每一個段都有名字和長度。地址指定了段名稱和段內偏移。所以用戶經過兩個量來指定地址:段名稱和偏移。段是編號的,經過段號而非段名稱來引用。所以邏輯地址由有序對構成:
<segment-number,offset>(<段號s, 段內偏移d>)
段偏移d因該在0和段界限之間,若是合法,那麼就與基地址相加而獲得所需字節在物理內存中的地址。所以段表是一組基地址和界限寄存器對。
例以下圖,有5個段,編號0~4,例如段2爲400B開始於位置4300,對段2第53字節的引用映射成位置4300+53=4353。而段0字節1222的引用則會觸發地址錯誤,由於該段的僅爲1000B長(界限爲1000)。
4. 合併分段和分頁的管理方案
在現有的Intel兼容計算機(x86)上,採用的內存管理方案是分段和分頁合併的管理方案。
在這個方案中,邏輯地址,如前一節中所說,是由一個段標識符加上一個指定段內相對地址的偏移量,表示爲 [段標識符:段內偏移量]。
這樣的邏輯地址轉換的過程是怎樣呢?以下圖所示:
當CPU要執行一條引用了內存地址的指令時,轉換過程就開始了。第一步是把邏輯地址轉換成線性地址。可是,爲何不跳過這一步,而讓軟件直接使用線性地址(或物理地址呢?)緣由主要是由於:
(1) Intel的更新是漸進式而非革命式,新的處理器須要兼容和保留過往的設置。具體的緣由,博文Memory Translation and Segmentation (http://blog.csdn.net/drshenlei/article/details/4261909) 中講的較爲清楚。
(2) 如上節所說,採用段內存管理,能夠跟方便地進行地址保護(同一類型的地址邏輯地址在一塊兒)。
下面講邏輯地址到線性地址的部分。
在IBM OS/2 32位版本的操做系統,和Intel 386的環境下。操做系統採用的內存分配方式就是分段和分頁合併的方式。
邏輯地址的其實是一對<選擇符,偏移>。
選擇符的內容以下:
從左開始,13位是索引(或者稱爲段號),經過這個索引,能夠定位到段描述符(segment descriptor),而段描述符是能夠真正記載了有關一個段的位置和大小信息, 以及訪問控制的狀態信息。段描述符通常由8個字節組成。因爲8B較大,而Intel爲了保持向後兼容,將段寄存器仍然規定爲16-bit(儘管每一個段寄存器事實上有一個64-bit長的不可見部分,但對於程序員來講,段寄存器就是16-bit的),那麼很明顯,咱們沒法經過16-bit長度的段寄存器來直接引用64-bit的段描述符。所以在邏輯地址中,只用13bit記錄其索引。而真正的段描述符,被放於數組之中。
這個內存中的數組就叫作GDT(Global Descriptor Table,全局描述表),Intel的設計者門提供了一個寄存器GDTR用來存放GDT的入口地址。程序員將GDT設定在內存中某個位置以後,能夠經過LGDT指令將GDT的入口地址裝入此寄存器,今後之後,CPU就根據此寄存器中的內容做爲GDT的入口來訪問GDT了。
除了GDT以外,還有LDT(Local Descriptor Table,本地描述表),但與GDT不一樣的是,LDT在系統中能夠存在多個,每一個進程能夠擁有本身的LDT。LDT的內存地址在LDTR寄存器中。
在以前圖中的TI位,就是用來表示此索引所指向的段描述符是存於全局描述表中,仍是本地描述表中。=0,表示用GDT,=1表示用LDT。
RPL位,佔2bit,是保護信息位,尚未仔細瞭解過這一塊,暫時先不寫。
找到,段描述符後,加上偏移量,即是線性地址。轉換過程以下:
在Intel 386的環境下,線性地址轉換爲物理地址的過程,和第二節分頁式內存管理中,層次分頁中,邏輯地址轉換爲物理地址的方法相似。以下圖。
Intel 80386的地址轉換全過程以下圖:
內存管理部分是操做系統的核心功能之一,此次將理論部分整理出來,一是爲了複習,二也是爲了提綱挈領地爲深刻學習操做系統作準備。
文中的圖片均非本人原創,主要來自文章開頭所引用的博文,以及參考書籍中的圖片。如有侵權行爲,請指出,博主將盡快移除。