【软件开发底层知识修炼】三 深入浅出处理器之三 内存管理与内存管理单元(MMU)
學(xué)習(xí)交流加
- 個人qq:
1126137994 - 個人微信:
liu1126137994 - 學(xué)習(xí)交流資源分享qq群:
962535112 
上一篇文章學(xué)習(xí)了中斷的概念與意義,以及中斷的應(yīng)用-斷點(diǎn)調(diào)試原理。點(diǎn)擊鏈接復(fù)習(xí)上一篇文章:中斷的概念與意義
本片文章繼續(xù)學(xué)習(xí)處理器相關(guān)的知識-內(nèi)存管理。包括:內(nèi)存管理單元MMU的作用,虛擬內(nèi)存與物理內(nèi)存之間的映射方式,頁表的概念,高速緩存(Cache)的作用,物理內(nèi)存與高速緩存之間的映射關(guān)系等。當(dāng)然,想要深入了解,本文并不適合,本文只是從原理上,講述以上幾者之間的關(guān)系。
文章目錄
- 1、內(nèi)存管理單元MMU
 - 1.1、虛擬內(nèi)存
 - 1.2、 頁式內(nèi)存管理
 - 1.21 頁的概念
 - 1.22 頁表的概念
 - 1.23 頁命中
 - 1.24 缺頁
 - 1.25 分配頁面
 - 1.26 程序的局部性原則
 
- 1.3 虛擬內(nèi)存作為內(nèi)存管理工具
 - 1.4、虛擬內(nèi)存作為內(nèi)存保護(hù)工具
 
- 2、地址翻譯
 - 3、總結(jié)
 
1、內(nèi)存管理單元MMU
這里假設(shè)大家了解虛擬內(nèi)存的由來。參考《深入理解計(jì)算機(jī)系統(tǒng)》講虛擬內(nèi)存的章節(jié)
實(shí)際上我們寫的程序,都是面向虛擬內(nèi)存的。我們在程序中寫的變量的地址,實(shí)際上是虛擬內(nèi)存中的地址,當(dāng)CPU想要訪問該地址的時候,內(nèi)存管理單元MMU會將該虛擬地址翻譯成真實(shí)的物理地址,然后CPU就去真實(shí)的物理地址處取得數(shù)據(jù)。
這里說的虛擬地址,是指虛擬地址空間中地址。這里我們說的虛擬地址空間,實(shí)際上是在磁盤上的一塊空間(常見的是4G的進(jìn)程虛擬地址空間)。具體這4G的虛擬地址空間的來龍去脈,參考《深入理解計(jì)算機(jī)系統(tǒng)》第九章。
MMU:內(nèi)存管理單元。它是一個硬件,不是軟件。它用于將虛擬地址翻譯成實(shí)際的物理內(nèi)存地址。同時它還可以將特定的內(nèi)存塊設(shè)置成不同的讀寫屬性,進(jìn)而實(shí)現(xiàn)內(nèi)存保護(hù)。注意,MMU是硬件管理,不是軟件實(shí)現(xiàn)內(nèi)存管理。
總結(jié)來說,MMU能實(shí)現(xiàn)以下功能:
- 虛擬內(nèi)存。有了虛擬內(nèi)存,可以在處理器上運(yùn)行比實(shí)際物理內(nèi)存大的應(yīng)用程序。為了使用虛擬內(nèi)存,操作系統(tǒng)通常要設(shè)置一個交換區(qū)(通常在硬盤上),通過將內(nèi)存中不活躍的數(shù)據(jù)與指令放到交換區(qū),以騰出物理內(nèi)存來為其他程序服務(wù)。
 - 內(nèi)存保護(hù)。通過這一功能,可以將特定的內(nèi)存塊設(shè)置為讀、寫或者可執(zhí)行的屬性。比如將不可變的數(shù)據(jù)或者代碼設(shè)為只讀的,這樣可以防止被惡意串改。
 
1.1、虛擬內(nèi)存
進(jìn)程的概念大家都知道。
每一個進(jìn)程都獨(dú)立的運(yùn)行在自己的虛擬地址空間。為了理解這一個概念。我們可以看一個而簡單的例子:
看一下下面的代碼:
 main.c
如果我同時運(yùn)行該程序兩次。打印結(jié)果會是一樣么?答案是結(jié)果肯定一樣,運(yùn)行結(jié)果都為:
 
當(dāng)然,這是在我的計(jì)算機(jī)上,在你的計(jì)算機(jī)上g_int地址可能不一樣,但是同時運(yùn)行該程序兩次,結(jié)果肯定是一樣的。其實(shí)這個答案很多人都知道是一樣的,初學(xué)者都知道。但是初學(xué)者說不清楚是為什么。
- 分析
 
這個進(jìn)程運(yùn)行兩份實(shí)例的時候。在物理內(nèi)存中,實(shí)際上是以下分布情況:
進(jìn)程1和進(jìn)程2 位于不同的地址。但是我們程序打印的g_int全局變量的地址值,是一樣的。
這里就引入了虛擬內(nèi)存的概念。我們寫程序,面向的是虛擬地址空間。寫的程序的內(nèi)容,都可以看成是在虛擬地址空間中運(yùn)行(實(shí)際上最終是將虛擬地址空間映射到了物理地址空間)。如下圖:
以上只是簡圖。
我們可以看到。main.o可執(zhí)行程序,運(yùn)行兩份實(shí)例時,相當(dāng)于兩個進(jìn)程。這兩個進(jìn)程都有自己獨(dú)立的虛擬地址空間。然后將虛擬地址空間里的代碼數(shù)據(jù)映射到內(nèi)存中,從而被CPU執(zhí)行與處理。在物理內(nèi)存中,g_int這個全局變量的物理地址確實(shí)不同。但是在虛擬內(nèi)存中,由于進(jìn)程1與進(jìn)程2的虛擬地址空間完全一樣(同一個可執(zhí)行程序代碼),那么g_int地址,實(shí)際上就是一樣的。
CPU在執(zhí)行指令與數(shù)據(jù)時,獲得的是虛擬內(nèi)存的地址。但是CPU只能去物理內(nèi)存尋址。此時,MMU就派上用場了。MMU負(fù)責(zé),將虛擬地址,翻譯成,真正運(yùn)行時的物理地址。
MMU是如何將虛擬地址翻譯成物理地址的,這個后面講。現(xiàn)在先要了解一下交換區(qū)的概念。
- 交換區(qū): 實(shí)際上就是一塊磁盤空間(硬盤空間)。虛擬內(nèi)存與物理內(nèi)存映射的時候,是將虛擬內(nèi)存的代碼放到交換區(qū)中,以后在CPU想要執(zhí)行相關(guān)的指令或者數(shù)據(jù)時,如果內(nèi)存中沒有,先去交換區(qū)將需要的指令與數(shù)據(jù)映射到物理內(nèi)存,然后CPU再執(zhí)行。
 
虛擬內(nèi)存與交換取的這種概念,實(shí)現(xiàn)了大內(nèi)存需求量的(多個)進(jìn)程,能夠(同時)運(yùn)行在較小的物理內(nèi)存中。如下圖所示:
上圖中,說的是進(jìn)程的局部代碼在物理內(nèi)存中運(yùn)行。是因?yàn)槌绦蚓哂芯植啃栽瓌t,所以在某一段很小的時間段內(nèi),只有很少一部分代碼會被CPU執(zhí)行。具體可以參考下一篇文章。
到這里,我們應(yīng)該大致明白了虛擬內(nèi)存的作用與簡單機(jī)制。還剩下MMU如何翻譯虛擬地址為物理地址的,這放到最后講解。現(xiàn)在先總結(jié)一下虛擬內(nèi)存機(jī)制:
- 虛擬內(nèi)存需要重新映射到物理內(nèi)存
 - 虛擬地址映射到物理地址中的實(shí)際地址
 - 每次只有進(jìn)程的少部分代碼會在物理內(nèi)存中運(yùn)行
 - 大部分代碼依然位于磁盤中(存儲器硬盤)
 
1.2、 頁式內(nèi)存管理
上一節(jié)籠統(tǒng)的介紹了虛擬內(nèi)存的概念。接下來學(xué)習(xí)內(nèi)存管理中的一種方式:頁式內(nèi)存管理。
 頁式內(nèi)存管理中我們需要了解:
- 頁的概念
 - 頁表的概念
 - 缺頁的概念與頁命中的概念
 - 分配頁面
 - 程序的局部性原則
 
1.21 頁的概念
由1.1的內(nèi)容,我們知道了交換區(qū)。我們知道交換區(qū)里面存放的是大部分的可執(zhí)行代碼與數(shù)據(jù)。而物理內(nèi)存中,執(zhí)行的是少部分的可執(zhí)行代碼與數(shù)據(jù)。那么當(dāng)物理內(nèi)存中的代碼與數(shù)據(jù)執(zhí)行完需要執(zhí)行接下來的代碼,而剛好接下來的代碼還在交換區(qū)中沒有映射到物理內(nèi)存(這稱為缺頁,后面會講),那么此時就需要從交換區(qū)獲取程序的代碼,將它拿到物理內(nèi)存執(zhí)行。那么一次拿多少代碼過來呢?這是一個問題!
為了CPU的高效執(zhí)行以及方便的內(nèi)存管理(詳細(xì)原因見以后的文章),每次需要拿一個頁的代碼。這個頁,指的是一段連續(xù)的存儲空間(常見的是4Kb),也叫作塊。假設(shè)頁的大小為P。在虛擬內(nèi)存中,叫做虛擬頁(VP)。從虛擬內(nèi)存拿了一個頁的代碼要放到物理內(nèi)存,那么自然物理內(nèi)存也得有一個剛好一般大小的頁才能存放虛擬頁的代碼。物理內(nèi)存中的頁叫做物理頁(PP)
在任何時刻,虛擬頁都是以下三種狀態(tài)中的一種:
- 未分配的:VM系統(tǒng)還未分配的頁(或者未創(chuàng)建)。未分配的頁還沒有任何數(shù)據(jù)與代碼與他們相關(guān)聯(lián),因此也就不占用任何磁盤。
 - 緩存的: 當(dāng)前已緩存在物理內(nèi)存中的已分配頁
 - 未緩存的:未緩存在物理內(nèi)存中的已分配頁
 
下圖展示了一個8個虛擬頁的小虛擬內(nèi)存。其中:虛擬頁0和3還沒有被分配,因此在磁盤上還不存在。虛擬頁1、4、 6被緩存在物理內(nèi)存中。虛擬頁2、 5、 7已經(jīng)被分配,但是還沒有緩存到物理內(nèi)存中去執(zhí)行。
1.22 頁表的概念
1.21節(jié)用到了緩存這個詞。這里假設(shè)大家都理解緩存的概念。
虛擬內(nèi)存中的一些虛擬頁是緩存在物理內(nèi)存中被執(zhí)行的。理所應(yīng)當(dāng),應(yīng)該有一種機(jī)制,來判斷虛擬頁,是否被緩存在了物理內(nèi)存中的某個物理頁上。如果不命中(需要一個頁的代碼,但是這個頁未緩存在物理內(nèi)存中),系統(tǒng)還必須知道這個虛擬頁存放在磁盤上的哪個位置,從而在物理內(nèi)存中選擇一個空閑頁或者替換一個犧牲頁,并將需要的虛擬頁從磁盤復(fù)制到物理內(nèi)存中。
這些功能,是由軟硬件結(jié)合完成的。 包括操作系統(tǒng)軟件,MMU中的地址翻譯硬件,和一個存放在物理內(nèi)存中的頁表的數(shù)據(jù)結(jié)構(gòu)。
上一節(jié)說將虛擬頁映射到物理頁,實(shí)際上就是MMU地址翻譯硬件將一個虛擬地址翻譯成物理地址時,都會去讀取頁表的內(nèi)容。操作系統(tǒng)負(fù)責(zé)維護(hù)頁表的內(nèi)容,以及在磁盤與物理內(nèi)存之間來回傳送頁。
下圖是一個頁表的基本組織結(jié)構(gòu)(實(shí)際上不止那些內(nèi)容):
如上圖所示:
頁表實(shí)際上就是一個數(shù)組。這個數(shù)組存放的是一個稱為頁表?xiàng)l目(PTE)的結(jié)構(gòu)。虛擬地址空間的每一個頁在頁表中,都有一個對應(yīng)的頁表?xiàng)l目(PTE)。虛擬頁地址(首地址)翻譯的時候就是查詢的各個虛擬頁在頁表中的PTE,從而進(jìn)行地址翻譯的。
現(xiàn)在假設(shè)每一個PTE都有一個有效位和一個n位字段的地址。其中
- 有效位:表示對應(yīng)的虛擬頁是否緩存在了物理內(nèi)存中。0表示未緩存。1表示已緩存。
 - n位地址字段:如果未緩存(有效字段為0),n位地址字段不為空的話,這個n位地址字段就表示該虛擬頁在磁盤上的起始的位置。如果這個n位字段為空,那么就說明該虛擬頁未分配。如果已緩存(有效字段為1),n位地址字段肯定不為空,它表示該虛擬頁在物理內(nèi)存中的起始地址。
 
綜上分析,就得知,上圖中:四個虛擬頁VP1 , VP2, VP4 , VP7 是被緩存在物理內(nèi)存中。 兩個虛擬頁VP0, VP5還未被分配。但是剩下的虛擬頁VP3 ,VP6已經(jīng)被分配了,但是還沒有緩存到物理內(nèi)存中去執(zhí)行。
注意:任意的物理頁,都可以緩存任意的虛擬頁。(因?yàn)槲锢韮?nèi)存是全相聯(lián)的)
1.23 頁命中
考慮下圖的情形:
 
假設(shè)現(xiàn)在CPU想讀取VP2頁面中的某一個字節(jié)的內(nèi)容。會發(fā)生什么呢?
當(dāng)CPU得到一個地址vaddr想要訪問它(這個addr就是上面想要訪問的某一個字節(jié)的地址),通過后面會學(xué)習(xí)的MMU地址翻譯硬件,將虛擬地址addr作為索引定位到頁表的PTE條目中的PTE2(這里假設(shè)是PTE2),從內(nèi)存中去讀到PTE2的有效位為1,說明該虛擬頁面已經(jīng)被緩存了,所以CPU使用該P(yáng)TE2條目中的物理內(nèi)存地址(這個物理內(nèi)存地址是PP1中的起始地址)構(gòu)造出vaddr的物理地址paddr(這個地址是PP1頁面起始地址或后面的某一個地址)。然后CPU就會去paddr這個物理內(nèi)存地址去取數(shù)據(jù)。這種情況,就是也命中。
實(shí)際上,上面的VP2的起始地址與paddr地址,很類似于內(nèi)存的分段機(jī)制(X86以前就是分段機(jī)制),CPU訪問內(nèi)存的地址是“段地址:偏移地址”或者叫做“CS:IP”。而我們現(xiàn)在學(xué)習(xí)的是分頁機(jī)制,他們都是一種內(nèi)存管理機(jī)制。
1.24 缺頁
什么是缺頁?
考慮以下圖示情形:
當(dāng)CPU想訪問VP3頁面中的某一個字節(jié)。會發(fā)生什么情況?
由1.23小節(jié)的分析知,當(dāng)?shù)刂贩g硬件MMU找到了PTE3后,發(fā)現(xiàn)有效位為0,則說明VP3并未緩存在物理內(nèi)存中,并且觸發(fā)一個缺頁異常。缺頁異常調(diào)用內(nèi)核中的缺頁異常處理程序,該程序會在物理內(nèi)存中查詢是否有空閑頁面。如果物理內(nèi)存中有空閑頁面,則將VP3頁面的內(nèi)容從磁盤中復(fù)制到(映射)物理內(nèi)存中的空閑頁面。如果物理內(nèi)存中沒有空閑頁面,則缺頁異常處理程序就選擇一個犧牲頁,在此例中就是存放在PP3中的VP4。如果VP4已經(jīng)被修改了,那么內(nèi)核就會將它復(fù)制回磁盤。
然后此時因?yàn)閂P3已經(jīng)在物理內(nèi)存中被緩存了,就需要將頁表更新,也就是更新PTE3。
隨后缺頁異常處理程序返回。它會重新啟動導(dǎo)致缺頁的指令,該指令會重新將剛剛導(dǎo)致缺頁的虛擬地址發(fā)送到MMU硬件翻譯,但是此時,因?yàn)閂P3已經(jīng)被緩存,所以會頁命中。
下圖是在經(jīng)過了缺頁后,我們的示例頁表的狀態(tài):
- 以上有一個過程是替換頁面的過程,其中包含一個頁面調(diào)度算法。這個以后會學(xué)習(xí)。
 
1.25 分配頁面
當(dāng)你在程序中調(diào)用malloc或者new分配內(nèi)存時,發(fā)生了什么?調(diào)用malloc后,會在虛擬內(nèi)存中分配頁面。(注意malloc分配的內(nèi)存時虛擬內(nèi)存,當(dāng)CPU訪問的時候,首先肯定會發(fā)生缺頁,然后再將該頁緩存到物理內(nèi)存中)
如下圖所示:
 本身沒有VP5這個虛擬頁面,現(xiàn)在malloc后,新分配了一個虛擬頁面VP5。
分配好VP5這個虛擬頁面后,還需要更新PTE條目,使得PTE5指向VP5。
1.26 程序的局部性原則
虛擬內(nèi)存這種機(jī)制會有什么問題?經(jīng)常缺頁會不會導(dǎo)致程序的執(zhí)行效率低下?
實(shí)際上,雖然會產(chǎn)生不命中現(xiàn)象,但是虛擬內(nèi)存機(jī)制工作的很好。這主要與程序局部性原則有關(guān)!!!什么是程序的局部性?
盡管在程序整個運(yùn)行的生命周期,引用的不同的頁面總數(shù)可能會超過物理內(nèi)存的大小,但是局部性原則保證了在任意時刻:程序?qū)②呄蛴谠谝粋€較小的活動頁面集合上工作。 這個集合成為工作集或者常駐集合。在最開始,也就是將工作集頁面調(diào)度到物理內(nèi)存中之后,接下來對這個工作集的引用將導(dǎo)致頁命中,而不會產(chǎn)生額外的磁盤流量。
上面看似很完美,但是也有可能會出現(xiàn)這樣一種情況:工作集的大小超過了物理內(nèi)存的大小!! 此時,頁面會不停的換入換出。這種狀態(tài)叫做抖動!!!
當(dāng)然,現(xiàn)在的計(jì)算機(jī)的物理內(nèi)存的大小都非常大,一般不會出現(xiàn)抖動的現(xiàn)象!!!
1.3 虛擬內(nèi)存作為內(nèi)存管理工具
虛擬內(nèi)存為什么說是一種內(nèi)存管理工具?
虛擬內(nèi)存大大地簡化了內(nèi)存管理,并提供了一種自然的保護(hù)內(nèi)存的方法。
到目前為止,我們都假設(shè)有一個單獨(dú)的頁表,將一個虛擬地址空間映射到物理地址空間。實(shí)際上,操作系統(tǒng)為每一個進(jìn)程提供了一個獨(dú)立的頁表,因而也就是一個獨(dú)立的虛擬地址空間。如下圖:
 
注意:多個虛擬頁面,可以映射到同一個共享物理頁面上。
按需頁面調(diào)度和獨(dú)立的虛擬地址空間的結(jié)合,對系統(tǒng)中內(nèi)存的使用和管理產(chǎn)生了深遠(yuǎn)的影響!!!如下:
- 簡化鏈接。
 - 簡化加載
 - 簡化共享
 - 簡化內(nèi)存分配
 
具體參考CSAPP:9.4節(jié)內(nèi)容。
1.4、虛擬內(nèi)存作為內(nèi)存保護(hù)工具
上一節(jié)學(xué)習(xí)了虛擬內(nèi)存作為內(nèi)存管理工具。
其實(shí)虛擬內(nèi)存還可以作為內(nèi)存保護(hù)工具。如何做到?
想一想,CPU在訪問一個虛擬內(nèi)存頁面時,需要讀取頁表?xiàng)l目中的PTE條目。如果在PTE條目中加一些額外的許可位來控制對虛擬內(nèi)存的訪問,當(dāng)CPU讀到相應(yīng)的許可位,就可以知道該虛擬內(nèi)存是否可讀或者可寫,或者可執(zhí)行? 這樣看來我們的頁表就要變化一下,就如下圖所示:
 
上圖中:
- SUP表示進(jìn)程是否必須運(yùn)行在內(nèi)核模式(超級用戶)下才能訪問該頁。
 - READ表示是否可讀
 - WRITE表示是否可寫
 
如果一條指令違反了這些許可條件,那么CPU就會觸發(fā)一個一般保護(hù)故障,將控制傳遞給一個內(nèi)核中的異常處理程序。Linux shell 一般將這種異常報(bào)告為“段錯誤(segmentation fault)”
2、地址翻譯
上面一直在說MMU通過讀取頁表的PTE將虛擬地址翻譯成物理地址。到底是如何翻譯的?
如下圖,展示了MMU是如何翻譯地址的:
 
看到這么復(fù)雜的圖,不要害怕!!! 下面講解很容易懂!
- CPU中有一個控制寄存器,頁表基址寄存器(PTBR)指向當(dāng)前頁表。
 - n位的虛擬地址,包含兩個部分:虛擬頁面偏移VPO(p位)與虛擬頁號VPN(n-p位)
 - MMU利用虛擬內(nèi)存的高n-p位VPN作為索引找到頁表的對應(yīng)的PTE條目,然后獲取PTE條目對應(yīng)的物理頁號PPN
 - 然后將PPN與VPO串聯(lián)連接起來,就得到了實(shí)際的物理地址。(實(shí)際上就是PPN左移p位然后加上VPO,VPO=PPO)
 
到這里實(shí)際上我們已經(jīng)更加的將這種地址串聯(lián)與X86處理器中的分段機(jī)制很像。X86-16位的分段機(jī)制 也是將段地址CS左移4位然后與偏移地址IP相加,得到最終的物理地址。這是不是與上面的分頁機(jī)制的地址翻譯過程很像? 實(shí)際上它們一個是實(shí)模式,一個是保護(hù)模式而已!
MMU的地址翻譯過程是不是很簡單?如果不理解,就反復(fù)看,就理解了!!!
3、總結(jié)
下面來總結(jié)一下,分頁機(jī)制中,CPU獲得一個虛擬地址后,有哪些步驟需要做:
本片文章,我學(xué)會了:
-  
虛擬內(nèi)存的概念與交換區(qū)的概念
 -  
MMU的作用
 -  
虛擬內(nèi)存機(jī)制的意義
- 虛擬內(nèi)存作為內(nèi)存管理工具
 - 虛擬內(nèi)存作為內(nèi)存保護(hù)工具
 
 -  
頁表的概念
 -  
頁命中與缺頁
 -  
程序的局部性在虛擬內(nèi)存中的作用
 -  
MMU的地址翻譯過程
 
本人積累了無數(shù)的技術(shù)電子書籍與各類技術(shù)的視頻教程,可以加好友共同探討學(xué)習(xí)交流。
 學(xué)習(xí)探討加個人:
 qq:1126137994
 微信:liu1126137994
總結(jié)
以上是生活随笔為你收集整理的【软件开发底层知识修炼】三 深入浅出处理器之三 内存管理与内存管理单元(MMU)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: KNX教程
 - 下一篇: C++中cin、cin.get()、ci