操作系統:內存管理(概念)


  1、物理地址和邏輯地址

  物理地址:加載到內存地址寄存器中的地址,內存單元的真正地址。在前端總線上傳輸的內存地址都是物理內存地址,編號從0開始一直到可用物理內存的最高端。這些數字被北橋(Nortbridge chip)映射到實際的內存條上。物理地址是明確的、最終用在總線上的編號,不必轉換,不必分頁,也沒有特權級檢查(no translation, no paging, no privilege checks)。

  邏輯地址:CPU所生成的地址。邏輯地址是內部和編程使用的、並不唯一。例如,你在進行C語言指針編程中,可以讀取指針變量本身值(&操作),實際上這個值就是邏輯地址,它是相對於你當前進程數據段的地址(偏移地址),不和絕對物理地址相干。

 

  2、連續內存分配方案:

  內存必須容納操作系統和各種用戶進程,因此必須盡可能有效得分配內存,在分配內存過程中,通常需要將多個進程放入內存中,前面提到過,我們需要每個進程的空間相互獨立,而且我們必須保護每個進程的內存空間的獨立性,如果不同的進程間需要通信,可以按照我們前面提到的通信方法進行通信,但是在此時,我們考慮內存空間獨立性的實現。這就涉及到內存分配:

  我們將整個內存區域多個固定大小的分區,每個分區容納一個進程,當一個分區空閑時,可以將內存調入內存,等待執行,這是最簡單的內存分配方案,但是這種方案存在很多問題,我們並不知道每個進程需要多大的空間,如果空間過小,那么我們的進程就存不下,如果進程都很小,但是我們分區很大的話,那么會造成很大程度的浪費,這些在每個分區未被利用的空間,我們稱之為碎片。

  3、分頁內存管理方案

  (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)里,當進程被調度程序選中投入運行時,系統將其頁表指針從進程控制塊中取出並送入用戶寄存器中。隨后可以根據此首地址訪問頁表。

  頁表的存儲方式是TLB(Translation look-aside buffer, 轉換表緩沖區)+內存。TLB實際上是一組硬件緩沖所關聯的快速內存。若沒有TLB,操作系統需要兩次內存訪問來完成邏輯地址到物理地址的轉換,訪問頁表算一次,在頁表中查找算一次。TBL中存儲頁表中的一小部分條目,條目以鍵值對方式存儲。

  (3) 頁表的數據結構

  1)層次化分頁

  現有的筆記本電腦,內存地址空間一般為2^32字節以上。對於具有32位邏輯地址空間的計算機系統,如果系統的頁大小為4KB(2^12B),那么頁表可以擁有2^(32-12)個,也就是一百多萬個條目,假設每個條目占有4B,那每個進程都需要4MB的物理地址空間來存放頁表本身。而且,頁表本身需要分配在連續內存中。

為此,Hierarchical Paging(層次化分頁)被提出,實際上就是將頁號分為兩部分,第一部分作為索引,第二部分作為頁號的偏移。

  以一個4kb頁大小的32位系統為例。一個邏輯地址被分為20位的頁碼和12位的頁偏移。因為要對頁表進行再分頁,所以該頁號可分為10位的頁碼和10位的頁偏移。這樣一個邏輯地址就表示如下形式:

 

地址轉換過程如下:

 

  地址由外向內轉換,因此此方法也被稱為forward-mapped page table(向前映射表)。

  2)Hashed Page Tables 哈希頁表

  處理超過32位地址空間的常用方法是使用hashed page table(哈希頁表),並以虛擬頁碼作為哈希值。哈希頁表的每一條目都包括一個鏈表的元素,這些元素哈希成同一位置。每個元素有三個域:虛擬頁碼,所映射的幀號,指向鏈表中下一個元素的指針。

  個人看來,哈希頁表的地址轉換方式,實際上是Chaining(鏈接)方式,也就是一種哈希函數的溢出處理方式(另一種溢出處理方式叫做Open Addressing,開放尋址),具體過程如下:

  邏輯地址需要大於32bit的地址空間來表示,但是操作系統仍只有32bit來表示地址。此時人們便想到虛擬頁地址,虛擬地址可以在32bit表示范圍之內,然后利用哈希函數完成邏輯地址到虛擬地址的映射,由於虛擬地址更少,哈希函數會出現溢出,這里使用Chaining來解決溢出。

  邏輯地址中的頁號(下圖中的p)經過哈希函數的計算,算出虛擬地址中的頁號,根據虛擬頁號可以在哈希表中查找,用p與鏈表中的每一個元素的第一個域相比較。如果匹配,那么相應的幀號就用來形成物理地址。如果不匹配,就對鏈表中的下一個節點進行比較,以尋找一個匹配的頁號。為什么要存在下一個元素的指針呢??就是因為哈希函數用開放地址法處理碰撞。

  3)反向頁表

  在分頁系統中為每個進程配置一張頁表,進程邏輯地址空間中的每一頁,在頁表中都對應有一個頁表項。在現代計算機系統中通常允許一個進程的邏輯地址空間非常大,因此就有很多頁表項,從而占用很多的內存空間。為了減少頁表占用的內存空間而引入了反向頁表(Inverted Page Table)。一般頁表的表項是按頁號進行排序,頁表項中的內容是物理塊號。而反向頁表是為每一個物理塊設置一個頁表項並將按物理塊號排序,其中的內容則是頁號及其隸屬進程的標志符。

 

  在利用反向頁表進行地址變換時,是用進程標志符和頁號去檢索反向頁表;若檢索完整個頁表都未找到與之匹配的頁表項,表明此頁此時尚未調入內存,對於具有請求調頁功能的存儲器系統應產生請求調頁中斷,若無此功能則表示地址出錯;如果檢索到與之匹配的表項,則該表項的序號i便是該頁所在的物理塊號,將該塊號與頁內地址一起構成物理地址。

  雖然反向頁表可以有效地減少頁表占用的內存,然而該表中卻只包含已經調入內存的頁面,並未包含那些未調入內存的各個進程的頁面,因而必須為每個進程建立一個外部頁表(External Page Table),該頁表與傳統頁表一樣,當所訪問的頁面在內存時並不訪問這些頁表,只是當不在主存時才使用這些頁表。該頁表中包含了頁面在外存的物理位置,通過該頁表可將所需要的頁面調入內存。

 

  4、分段內存管理方案

  采用分頁內存管理有一個不可避免的問題:用戶視角的內存和實際內存的分離。設想一段main函數代碼,里面包含Sqrt函數的調用。按照編寫者的理解,這段代碼運行時,操作系統應該分配內存給:符號表(編譯時使用),棧(存放局部變量與函數參數值),Sqrt代碼段,主函數代碼段等。這樣,編寫者就可以方便地指出:"函數sqrt內存模塊的第五條指令",來定位一個元素。而實際上,由於采用分頁的管理方式,所有的一切都只是散落在物理內存中的各個幀上,並不是以編寫者的理解來划分模塊。

  分段的內存管理方式可以支持這種思路。邏輯地址空間由一組段組成。每個段都有名字和長度。地址指定了段名稱和段內偏移。因此用戶通過兩個量來指定地址:段名稱和偏移。段是編號的,通過段號而非段名稱來引用。因此邏輯地址由有序對構成:

 <segment-number,offset>(<段號s, 段內偏移d>)

段偏移d因該在0和段界限之間,如果合法,那么就與基地址相加而得到所需字節在物理內存中的地址。因此段表是一組基地址和界限寄存器對。

 

  例如下圖,有5個段,編號0~4,例如段2為400B開始於位置4300,對段2第53字節的引用映射成位置4300+53=4353。而段0字節1222的引用則會觸發地址錯誤,因為該段的僅為1000B長(界限為1000)。

  本文部分內容參考http://blog.csdn.net/u010953266/article/details/42774117和http://blog.csdn.net/cn_wk/article/details/52736466


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM