1. 程式人生 > >虛擬記憶體與實體記憶體

虛擬記憶體與實體記憶體

第一層理解

        1.每個程序都有自己獨立的4G(32位系統下)記憶體空間,各個程序的記憶體空間具有類似的結構

 

        2.一個新程序建立的時候,將會建立起自己的記憶體空間,此程序的資料,程式碼等從磁碟拷貝到自己的程序空間,哪些資料在哪裡,都由程序控制表中的task_struct記錄,task_struct中記錄中一條連結串列,記錄中記憶體空間的分配情況,哪些地址有資料,哪些地址無資料,哪些可讀,哪些可寫,都可以通過這個連結串列記錄

 

        3.每個程序已經分配的記憶體空間,都與對應的磁碟空間對映

 

問題:

        計算機明明沒有那麼多記憶體(n個程序的話就需要n*4G)記憶體

        建立一個程序,就要把磁碟上的程式檔案拷貝到程序對應的記憶體中去,對於一個程式對應的多個程序這種情況,浪費記憶體!

 

第二層理解

        1.每個程序的4G記憶體空間只是虛擬記憶體空間,每次訪問記憶體空間的某個地址,都需要把地址翻譯為實際實體記憶體地址

        2.所有程序共享同一實體記憶體,每個程序只把自己目前需要的虛擬記憶體空間對映並存儲到實體記憶體上。

        3.程序要知道哪些記憶體地址上的資料在實體記憶體上,哪些不在,還有在實體記憶體上的哪裡,需要用頁表來記錄

        4.頁表的每一個表項分兩部分,第一部分記錄此頁是否在實體記憶體上,第二部分記錄實體記憶體頁的地址(如果在的話)

        5.當程序訪問某個虛擬地址,去看頁表,如果發現對應的資料不在實體記憶體中,則缺頁異常

        6.缺頁異常的處理過程,就是把程序需要的資料從磁碟上拷貝到實體記憶體中,如果記憶體已經滿了,沒有空地方了,那就找一個頁覆蓋,當然如果被覆蓋的頁曾經被修改過,需要將此頁寫回磁碟

 

總結:

優點:

1.既然每個程序的記憶體空間都是一致而且固定的,所以連結器在連結可執行檔案時,可以設定記憶體地址,而不用去管這些資料最終實際的記憶體地址,這是有獨立記憶體空間的好處

2.當不同的程序使用同樣的程式碼時,比如庫檔案中的程式碼,實體記憶體中可以只儲存一份這樣的程式碼,不同的程序只需要把自己的虛擬記憶體對映過去就可以了,節省記憶體

3.在程式需要分配連續的記憶體空間的時候,只需要在虛擬記憶體空間分配連續空間,而不需要實際實體記憶體的連續空間,可以利用碎片。

 

另外,事實上,在每個程序建立載入時,核心只是為程序“建立”了虛擬記憶體的佈局,具體就是初始化程序控制表中記憶體相關的連結串列,實際上並不立即就把虛擬記憶體對應位置的程式資料和程式碼(比如.text .data段)拷貝到實體記憶體中,只是建立好虛擬記憶體和磁碟檔案之間的對映就好(叫做儲存器對映),等到執行到對應的程式時,才會通過缺頁異常,來拷貝資料。還有程序執行過程中,要動態分配記憶體,比如malloc時,也只是分配了虛擬記憶體,即為這塊虛擬記憶體對應的頁表項做相應設定,當程序真正訪問到此資料時,才引發缺頁異常。

 

補充理解:

虛擬儲存器涉及三個概念: 虛擬儲存空間,磁碟空間,記憶體空間

 

可以認為虛擬空間都被對映到了磁碟空間中,(事實上也是按需要對映到磁碟空間上,通過mmap),並且由頁表記錄對映位置,當訪問到某個地址的時候,通過頁表中的有效位,可以得知此資料是否在記憶體中,如果不是,則通過缺頁異常,將磁碟對應的資料拷貝到記憶體中,如果沒有空閒記憶體,則選擇犧牲頁面,替換其他頁面。

 

mmap是用來建立從虛擬空間到磁碟空間的對映的,可以將一個虛擬空間地址對映到一個磁碟檔案上,當不設定這個地址時,則由系統自動設定,函式返回對應的記憶體地址(虛擬地址),當訪問這個地址的時候,就需要把磁碟上的內容拷貝到記憶體了,然後就可以讀或者寫,最後通過manmap可以將記憶體上的資料換回到磁碟,也就是解除虛擬空間和記憶體空間的對映,這也是一種讀寫磁碟檔案的方法,也是一種程序共享資料的方法 共享記憶體

 

 

記憶體:

如果每個程式執行都直接佔用記憶體,那你開一個冰封王座豈不是要佔1G的記憶體?還能不能幹別的了。虛擬地址空間的設計簡直是神來之筆。

給每個程序分配一個4G(對32位系統來說)的虛擬地址空間。程序直接操作虛擬地址空間,讀寫資料時,才給它調撥物理儲存器。

 

實體記憶體和虛擬記憶體關係:實體記憶體和虛擬記憶體對應。除OS外任何程式都不會直接訪問實體記憶體而是訪問虛擬記憶體。可把虛擬記憶體等同於實體記憶體。以後就只說記憶體,不再區分實體記憶體和虛擬記憶體。

頁面檔案和虛擬記憶體關係:可把虛擬記憶體等同於實體記憶體。改變頁面檔案大小可改變虛擬記憶體大小。詳細來說:頁面檔案只是改變了實體記憶體的大小,當然也改變了虛擬記憶體的大小。(猜測:實體記憶體和虛擬記憶體的對映在大小上是1:1的。)可禁用頁面檔案但不能禁用虛擬記憶體。

虛擬地址空間和實體地址空間對應:虛擬地址空間指的是程序的可用地址空間範圍。而實體地址空間指的是實際可用的記憶體空間範圍。

1、使用者編制程式時使用的地址稱為虛地址或邏輯地址,其對應的儲存空間稱為虛存空間或邏輯地址空間;而計算機實體記憶體的訪問地址則稱為實地址或實體地址,其對應的儲存空間稱為物理儲存空間或主存空間。

2、虛擬儲存器的容量限制:主存容量+輔存(硬碟)容量。


3、實體記憶體:在應用中,真實存在的,插在主機板記憶體槽上的記憶體條的容量的大小。從本質上來說,實體記憶體是程式碼和資料在其中執行的視窗。

4、虛擬記憶體:使程式認為它擁有連續的可用的記憶體(一個連續完整的地址空間),而實際上,它通常是被分隔成多個實體記憶體碎片,還有部分暫時儲存在外部磁碟儲存器上,在需要時進行資料交換。
若計算機執行程式或操作所需的隨機儲存器(RAM)不足時,則 Windows 會用虛擬儲存器進行補償,即拿出一部分硬碟空間來充當記憶體使用,這部分空間即稱為虛擬記憶體,虛擬記憶體在硬碟上的存在形式就是 PAGEFILE.SYS這個頁面檔案。它將計算機的RAM和硬碟上的臨時空間組合。將資料移入分頁檔案可釋放RAM,以便完成工作。
若計算機的速率由於RAM可用空間匱乏而減緩,則可嘗試通過增加虛擬記憶體來進行補償。但是,計算機從RAM讀取資料的速率要比從硬碟讀取資料的速率快,因而擴增RAM容量(可加記憶體條)是最佳選擇。
分頁檔案:硬碟上一個或者多個隱藏檔案pagefile.sys,Windows用於儲存未存入記憶體的部分程式和資料檔案。頁面檔案和實體記憶體或隨機存取記憶體(RAM)構成了虛擬記憶體。Windows會根據需要將資料從頁面檔案移至記憶體,或將資料從記憶體移至頁面檔案以便為新資料釋放記憶體。也叫“交換檔案”。

 

5、虛存的訪問過程:
虛存空間的使用者程式按照虛地址程式設計並存放在輔存(硬碟)中。程式執行時,由地址變換機構依據當時分配給該程式的實地址空間把程式的一部分調入實存。每次訪存時,首先判斷該虛地址所對應的部分是否在實存中。如果是,則進行地址轉換並用實地址訪問主存;否則,按照某種演算法將輔存中的部分程式排程進記憶體,再按同樣的方法訪問主存。由此可見,每個程式的虛地址空間可以遠大於實地址空間,也可以遠小於實地址空間。前一種情況以提高儲存容量為目的,後一種情況則以地址變換為目的。後者通常出現在多使用者或多工系統中:實存空間較大,而單個任務並不需要很大的地址空間,較小的虛存空間則可以縮短指令中地址欄位的長度。

6、引入虛擬儲存技術的好處:
可在較小的可用記憶體中執行較大的使用者程式;
可在記憶體中容納更多程式併發執行;
不必影響程式設計時的程式結構(與覆蓋技術比較);
提供給使用者可用的虛擬記憶體空間通常大於實體記憶體。

 

7、虛擬地址:
如果CPU暫存器中的分頁標誌位被設定,那麼執行記憶體操作的機器指令時,CPU會自動根據頁目錄和頁表中的資訊,把虛擬地址轉換成實體地址,完成該指令。比如 mov eax,004227b8h ,這是把地址004227b8h處的值賦給暫存器的彙編程式碼,004227b8這個地址就是虛擬址。CPU在執行這行程式碼時,發現暫存器中的分頁標誌位已經被設定,就自動完成虛擬地址到實體地址的轉換,使用實體地址取出值,完成指令。對於Intel CPU 來說,分頁標誌位是暫存器CR0的第31位,為1表示使用分頁,為0表示不使用分頁。對於初始化之後的 Win2k 我們觀察 CR0 ,發現第31位為1。表明Win2k是使用分頁的。

使用了分頁機制之後,4G的地址空間被分成了固定大小的頁,每一頁或者被對映到實體記憶體,或者被對映到硬碟上的交換檔案中,或者沒有對映任何東西。對於一般程式來說,4G的地址空間,只有一小部分映射了實體記憶體,大片大片的部分是沒有對映任何東西。實體記憶體也被分頁,來對映地址空間。對於32bit的Win2k,頁的大小是4K位元組。CPU用來把虛擬地址轉換成實體地址的資訊存放在叫做頁目錄和頁表的結構裡。

8、實體記憶體分頁:
一個物理頁的大小為4K位元組,第0個物理頁從實體地址 0x00000000 處開始。由於頁的大小為4KB,就是0x1000位元組,所以第1頁從實體地址0x00001000 處開始。第2頁從實體地址 0x00002000 處開始。可以看到由於頁的大小是4KB,所以只需要32bit的地址中高20bit來定址物理頁。
頁表:一個頁表的大小為4K位元組(32bit),放在一個物理頁中。由1024個4位元組的頁表項組成。頁表中的每一項的內容(每項4個位元組,32bit)高20bit用來放一個物理頁的實體地址,低12bit放著一些標誌。
頁目錄:一個頁目錄大小為4K位元組(32bit),放在一個物理頁中。由1024個4位元組的頁目錄項組成。頁目錄中的每一項的內容(每項4個位元組)高20bit用來放一個頁表的實體地址,低12bit放著一些標誌。

9、對於x86系統(32bit),頁目錄的實體地址放在CPU的CR3暫存器中。
一個虛擬地址轉換成實體地址的計算過程就是:處理器通過CR3找到當前頁目錄所在物理頁,取虛擬地址的高10bit,然後把這10bit左移2bit(因為每個頁目錄項4個位元組長,左移2bit相當於乘4)得到在該頁中的地址,取出該地址處PDE(4個位元組),就找到了該虛擬地址對應頁表所在物理頁,取虛擬地址第12位到第21位這10位,然後把這10bit左移2bit(因為每個頁表項4個位元組長,左移2bit相當於乘4)得到在該頁中的地址,取出該地址處的PTE(4個位元組),就找到了該虛擬地址對應物理頁的地址,最後加上12bit的頁內偏移得到了實體地址。

10、32bit的一個指標,可以定址範圍0x00000000-0xFFFFFFFF,4GB大小。也就是說一個32bit的指標可以定址整個4GB地址空間的每一個位元組。一個頁表項負責4K的地址空間和實體記憶體的對映,一個頁表1024項,也就是負責1024*4k=4M的地址空間的對映。一個頁目錄項,對應一個頁表。一個頁目錄有1024項,也就對應著1024個頁表,每個頁表負責4M地址空間的對映。1024個頁表負責1024*4M=4G的地址空間對映。一個程序有一個頁目錄,所以以頁為單位。頁目錄和頁表可以保證4G的地址空間中的每頁和實體記憶體的對映。

11、每個程序都有自己的4G地址空間,從 0x00000000-0xFFFFFFFF 。通過每個程序自己的一套頁目錄和頁表來實現。由於每個程序有自己的頁目錄和頁表,所以每個程序的地址空間對映的實體記憶體是不一樣的。兩個程序的同一個虛擬地址處(如果都有實體記憶體對映)的值一般是不同的,因為他們往往對應不同的物理頁。

====================================================

虛擬記憶體:
1.每個程序都有各自獨立的4G 位元組的虛擬地址空間。4G的程序空間分為兩部分,0~3G-1 為使用者空間,3G~ 4G-1 為核心空間。
2.使用者程式中使用的都是虛擬地址空間中的地址,永遠無法直接訪問實際實體地址。
3.虛擬記憶體到實體記憶體的對映由作業系統動態維護。
4.虛擬記憶體一方面保護了作業系統的安全,另一方面允許應用程式使用比實際實體記憶體更大的地址空間。
5.使用者空間中的程式碼不能直接訪問核心空間中的程式碼和資料,但是可以通過系統呼叫進入核心態,間接地與核心互動。
6.對記憶體的越權訪問,或訪問未建立對映的虛擬記憶體(野指標、不在對映表中),將會導致段錯誤。

7. 使用者空間對應程序,程序一切換,使用者空間隨即變換。
核心空間由作業系統核心使用,不會隨程序切換而變化。
核心空間由核心根據獨立且唯一的頁表init_mm.pgd 進行對映,而使用者空間的頁表則每個程序一份。
8. 每個程序的記憶體空間完全獨立,因此在不同程序之間交換虛擬地址毫無意義。
9.虛擬記憶體到實體記憶體的對映,以頁(4096位元組)為單位。
 

在面試總監的時候,總監問了我這樣的一個問題:你個我說說實體記憶體和虛擬記憶體到底是怎麼一回事?
其實之前我看過這個問題,據我理解的,當時是這麼回答的“程序在執行的時候,作業系統都為其分配一個4GB的地址空間,即所謂的虛擬地址空間,一般情況下,當我們的程式很大的時候,實際的實體記憶體根本不能滿足我們的需求的時候,這個時候作業系統就會藉助磁碟空間來做虛擬的記憶體空間,把當前程序不需要的資料放在磁碟上,等到用到的時候,在利用排程演算法把所需要的資料從磁碟空間上排程到記憶體,虛擬記憶體就是為了擴大記憶體的容量,每當我們要執行一個程式的時候經過編譯以後形成的僅僅是邏輯上的空間,根本不是可以直接執行的記憶體空間,所以它還存在一個地址對映的概念。”當時感覺回答的很是籠統,只見總監在最後說了一句,你下去還是把這一塊的內容在好好看看,所以今天就好好的把這個概念理一理。
首先我從最基本的概念說起,什麼是實體記憶體的概念,虛擬記憶體的概念?
實體記憶體,在應用中,自然是顧名思義,物理上,真實的插在板子上的記憶體是多大就是多大了。而在CPU中的概念,實體記憶體就是CPU的地址線可以直接進行定址的記憶體空間大小。比如8086只有20根地址線,那麼它的定址空間就是1MB,我們就說8086能支援1MB的實體記憶體,及時我們安裝了128M的記憶體條在板子上,我們也只能說8086擁有1MB的實體記憶體空間。同理我們現在大部分使用的是32位的機子,32位的386以上CPU就可以支援最大4GB的實體記憶體空間了。
先說說為什麼會有虛擬記憶體和實體記憶體的區別。正在執行的一個程序,他所需的記憶體是有可能大於記憶體條容量之和的,比如你的記憶體條是256M,你的程式卻要建立一個2G的資料區,那麼不是所有資料都能一起載入到記憶體(實體記憶體)中,勢必有一部分資料要放到其他介質中(比如硬碟),待程序需要訪問那部分資料時,在通過排程進入實體記憶體。所以,虛擬記憶體是程序執行時所有記憶體空間的總和,並且可能有一部分不在實體記憶體中,而實體記憶體就是我們平時所瞭解的記憶體條。有的地方呢,也叫這個虛擬記憶體為記憶體交換區。關鍵的是不要把虛擬記憶體跟真實的插在主機板上的記憶體條相掛鉤,虛擬記憶體它是“虛擬的”不存在,假的啦,它只是記憶體管理的一種抽象!
那麼,什麼是虛擬記憶體地址和實體記憶體地址呢。假設你的計算機是32位,那麼它的地址匯流排是32位的,也就是它可以定址0~0xFFFFFFFF(4G)的地址空間,但如果你的計算機只有256M的實體記憶體0x~0x0FFFFFFF(256M),同時你的程序產生了一個不在這256M地址空間中的地址,那麼計算機該如何處理呢?回答這個問題前,先說明計算機的記憶體分頁機制。
計算機會對虛擬記憶體地址空間(32位為4G)分頁產生頁(page),對實體記憶體地址空間(假設256M)分頁產生頁幀(page frame),這個頁和頁幀的大小是一樣大的,所以呢,在這裡,虛擬記憶體頁的個數勢必要大於實體記憶體頁幀的個數。在計算機上有一個頁表(page table),就是對映虛擬記憶體頁到實體記憶體頁的,更確切的說是頁號到頁幀號的對映,而且是一對一的對映。但是問題來了,虛擬記憶體頁的個數 > 實體記憶體頁幀的個數,豈不是有些虛擬記憶體頁的地址永遠沒有對應的實體記憶體地址空間?不是的,作業系統是這樣處理的。作業系統有個頁面失效(page fault)功能。作業系統找到一個最少使用的頁幀,讓他失效,並把它寫入磁碟,隨後把需要訪問的頁放到頁幀中,並修改頁表中的對映,這樣就保證所有的頁都有被排程的可能了。這就是處理虛擬記憶體地址到實體記憶體的步驟。

現在來回答什麼是虛擬記憶體地址和實體記憶體地址。虛擬記憶體地址由頁號(與頁表中的頁號關聯)和偏移量組成。頁號就不必解釋了,上面已經說了,頁號對應的對映到一個頁幀。那麼,說說偏移量。偏移量就是我上面說的頁(或者頁幀)的大小,即這個頁(或者頁幀)到底能存多少資料。舉個例子,有一個虛擬地址它的頁號是4,偏移量是20,那麼他的定址過程是這樣的:首先到頁表中找到頁號4對應的頁幀號(比如為8),如果頁不在記憶體中,則用失效機制調入頁,否則把頁幀號和偏移量傳給MMU(CPU的記憶體管理單元)組成一個物理上真正存在的地址,接著就是訪問實體記憶體中的資料了。總結起來說,虛擬記憶體地址的大小是與地址匯流排位數相關,實體記憶體地址的大小跟實體記憶體條的容量相關