虛擬內存管理簡要分析


1. 為啥要有虛擬內存管理

當前的處理器都多用戶多任務的,同時運行着很多進程。

  •  如果每個進程都直接訪問物理內存,這樣就要求程序員增加管理物理內存,以避免多個進程訪問同一塊物理內存,同時程序員直接訪問物理內存,這樣會造成可以隨意修改別人的東西,編碼困難,安全完全無法得到保證。
  •  多用戶的情況,經常會出現一個程序的多個實例,這種情況怎么解決呢? 就完全沒有辦法了
  • l同時注意當前我們的程序都非常之大,占用內存都非常多,一台機器跑的進程又太多

針對上述問題,肯定無法通過直接有限的物理內存來實現。

2.  虛擬內存

2.1 什么是虛擬內存?

  系統側幫我們解決了,系統幫我們動態分配和釋放物理內存。程序運行時,給每個進程虛出一整塊虛擬內存,而且可能比實際物理內存還大,讓我們認為每個進程自己獨占所有的物理內存,我們只管寫代碼,申請和使用內存,而不用管內存是否足夠(當然也不能這么任性)。讓程序員認為所有內存都是自己的。

  今天我們以32位系統為例,系統為每個進程虛擬出4G內存空間,其中3G為用戶空間,1G內核工具大家共享。

  

                    於是這樣就解決了我們之前提出來的前兩個問題。

2.2 虛擬內存的結構

每個進程都有如下圖一樣的虛擬內存空間。

從下往上分別為代碼段、bss段、數據段、堆、棧……

注意不是從0地址開始存儲的,是因為如果所有的進程都從0地址開始存儲,那是不是非常容易攻擊。所以系統讓起始地址取隨機。

                                        

2.3  虛擬內存的實現

    進程要運行在物理內存中,現在每個進程都有4G內存,實際上不可能有那么多內存。

      注意:我們一直說的都是虛擬內存,也就是說騙你的,你根本沒有那么多內存,當然也不能讓你知道是騙你的。

        怎么騙你的:操作系統中每個進程都有一個內存描述符結構體(mm_struct)里面記着你的內存結構情況,實際上就是欠你的內存,——賬本。只有你真的要用的時候才會真正給你分配物理內存,不用時就用磁盤空間保存,比如一些文件資源,只給你記賬了,實際上還沒分配呢。比如malloc比較大塊的內存時,當不使用時,很可能是沒有分配真正物理內存的,真正對他讀寫時才會真正分配物理內存。同時會出現這種情況,多個進程使用相同的只讀動態庫,也操作系統讓大家使用同一段物理內存,於是這不就節省了內存空間嗎?於是這樣就解決了我們之前提出來的第三個問題

3.  虛擬內存的映射

    最終運行的時候還是要在物理地址上的,系統通過段機制和頁機制來實現虛擬地址到物理地址的映射的。因為linux簡化了段機制,本文就不講段機制了,只簡述下頁機制。

 

3.1  頁機制

3.1.1  幾個概念

  頁:系統將虛擬內存分成4K大小的塊。每塊就是一頁,作為內存管理的最小單位。每個頁都會有一個編號,從小到大。

  頁框(塊):系統將物理內存也和虛擬內存一樣分成4K大小的塊,每塊就是一個頁框,與頁一一對應。

  地址結構:分頁存儲管理的邏輯地址結構如圖前一部分為頁號P,后一部分為頁內偏移量W。地址長度為32 位,其中0~11位為頁內地址,即每頁大小為4KB

    

  頁表:為了便於在內存中找到進程的每個頁面所對應的物理塊,系統為每個進程建立一張頁表,記錄頁在內存中對應的頁框號,頁表一般存放在內存中。在配置了頁表后,進程執行時,通過查找該表,即可找到每頁在內存中的物理塊號。可見,頁表的作用是實現從頁號到頁框號的地址映射。

3.1.2  虛擬地址到物理地址映射

  如下圖,虛擬地址的各個頁通過頁表找到具體的物理頁框,也就找到了真正的物理內存。同時可以看到在虛擬地址空間中連續的空間,實際上在物理上不連續的。這樣就使碎片化內存得到利用,使內存的利用率變得更高。

 

3.1.3  二級頁表

  分頁管理,進程在執行時不需要將所有頁調入內存頁框中,而只要將保存有映射關系的頁表調入內存中即可。但是我們仍然需要考慮頁表的大小。以32 位邏輯地址空間、頁面大小4KB、頁表項大小4B為例,若要實現進程對全部邏輯地址空間的映射,則每個進程需要2^201M個表項。也就是說,每個進程僅頁表這一項就需要4MB主存空間,這顯然是不切實際的。而即便不考慮對全部邏輯地址空間進行映射的情況,一個邏輯地址空間稍大的進程,其頁表大小也可能是過大的。以一個40MB的進程為例,頁表項共40KB,如果將所有頁表項內容保存在內存中,那么需要10個內存頁框來保存整個頁表。整個進程大小約為1萬個頁面,而實際執行時只需要幾十個頁面進入內存頁框就可以運行,但如果要求10個頁面大小的頁表必須全部進入內存,這相對實際執行時的幾十個進程頁面的大小來說,肯定是降低了內存利用率的;從另一方面來說,這10頁的頁表項也並不需要同時保存在內存中,因為大多數情況下,映射所需要的頁表項都在頁表的同一個頁面中。

  將頁表映射的思想進一步延伸,就可以得到二級分頁:將頁表的10頁空間也進行地址映射,建立上一級頁表,用於存儲頁表的映射關系。這里對頁表的10個頁面進行映射只需要10個頁表項,所以上一級頁表只需要1頁就足夠(可以存儲2^10=1024個頁表項)。在進程執行時,只需要將這1頁的上一級頁表調入內存即可,進程的頁表和進程本身的頁面,可以在后面的執行中再調入內存。

  我們以Intel處理器80x86系列的硬件分頁的地址轉換過程為例。在32位系統中,全部32位邏輯地址空間可以分為220(4GB/4KB)個頁面。這些頁面可以再進一步建立頂級頁表,需要210個頂級頁表項進行索引,這正好是一頁的大小4K,所以建立二級頁表即可,所以內存實際要保存頁表所占的空間就極少了。

 

 

        例如邏輯地址: 0x20021406 (0010 0000 0000 0010 0001 0100 0000 0110 B)

              頂級頁表字段:0x80 (00 1000 0000 B)

 

            二級頁表字段:0x21 (00 0010 0001B)

            頁內偏移量字段:0x406  (0100 0000 0110 B)

  頂級頁表字段的0x80用於選擇頂級頁表的第0x80表項,此表項指向和該進程的頁相關的二級頁表;二級頁表字段0x21用於選擇二級頁表的第0x21表項,此表項指向包含所需頁的頁框;最后的頁內偏移量字段0x406用於在目標頁框中讀取偏移量為0x406中的字節。

3.1.4  頁表結構

  請求分頁系統,請求分頁系統在一個作業運行之前不要求全部一次性調入內存,因此在作業的運行過程中,必然會出現要訪問的頁面不在內存的情況,如何發現和處理這種情況是請求分頁系統必須解決的兩個基本問題。為此,在請求頁表項中增加了四個字段

  增加的四個字段說明如下:

  狀態位P:用於指示該頁是否已調入內存,供程序訪問時參考。

  訪問字段A:用於記錄本頁在一段時間內被訪問的次數,或記錄本頁最近己有多長時間未被訪問,供置換算法換出頁面時參考。

  修改位M:標識該頁在調入內存后是否被修改過。

  外存地址:用於指出該頁在外存上的地址,通常是物理塊號,供調入該頁時參考。

    

3.1.5  頁檢索

   快表檢索:若找到要訪問的頁,便修改頁表項中的訪問位(寫指令則還須重置修改位),然后利用頁表項中給出的物理塊號和頁內地址形成物理地址。若未找到該頁的頁表項,應到內存中去查找頁表,再對比頁表項中的狀態位P,看該頁是否已調入內存,未調入則產生缺頁中斷,請求從外存把該頁調入內存。

 

3.2 磁盤到虛擬內存的映射

  前面講了比較多的虛擬內測到物理內存的映射。下面我們簡要描述下磁盤(交換區)到虛擬內存的映射。

   每個進程都有一個內存描述符結構體mm_struct,此結構體保存整個進程的用戶地址空間情況,包括各個段的開始結束地址等。每個段內存區詳細信息卻由其成員VMA描述,比如通常用到的代碼段、數據段等,同時我們也可手動通過mmap()函數將文件映射到虛擬內存中,使文件操作像直接操作內存一樣方便,映射都都會在程序的虛擬地址空間多一個VMA區段,如下圖。當進程切換到相應的位置后,就會利用我們前面講的頁機制映射到相應的物理內存中。

 


免責聲明!

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



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