一、內(nèi)存簡介
內(nèi)存是計算機中重要的部件之一,它是與CPU進(jìn)行溝通的橋梁。其作用是用于暫時存放CPU中的運算數(shù)據(jù),以及與硬盤等外部存儲器交換的數(shù)據(jù)。只要計算機在運行中,CPU就會把需要運算的數(shù)據(jù)調(diào)到內(nèi)存中進(jìn)行運算,當(dāng)運算完成后CPU再將結(jié)果傳送出來。計算機中所有程序的運行都是在內(nèi)存中進(jìn)行的。
二、內(nèi)存管理演變史
在早期的計算機中,程序是直接運行在物理內(nèi)存上的。直接操作物理內(nèi)存有幾個問題:
地址空間不隔離: 程序操作相同地址空間會造成互相影響甚至奔潰,而且安全性也得不到保證。
使用效率低:沒有特別好的策略保證多個進(jìn)程對超過物理內(nèi)存大小的內(nèi)存需求的滿足。
程序運行地址不能確定: 程序每次需要運行時,都需要在內(nèi)存中分配一塊足夠大的空閑區(qū)域,而問題是這個空閑的位置是不能確定的,這會帶來一些重定位的問題。
內(nèi)存管理無非就是想辦法解決上面三個問題。
這里引用計算機界一句無從考證的名言:“計算機系統(tǒng)里的任何問題都可以靠引入一個中間層來解決?!?/p>
那么辦法就是在程序和物理內(nèi)存之間引入了虛擬內(nèi)存這個概念。虛擬內(nèi)存位于程序和物理內(nèi)存之間,程序只能看見虛擬內(nèi)存,再也不能直接訪問物理內(nèi)存。每個程序都有自己獨立的進(jìn)程地址空間,這樣就做到了進(jìn)程隔離。這里的進(jìn)程地址空間是指虛擬地址。
三、虛擬內(nèi)存
物理內(nèi)存:系統(tǒng)中由RAM芯片控制的可用內(nèi)存。
虛擬內(nèi)存:虛擬內(nèi)存是計算機系統(tǒng)內(nèi)存管理的一種技術(shù)。它使得應(yīng)用程序認(rèn)為它擁有連續(xù)的可用的內(nèi)存(一個連續(xù)完整的地址空間),而實際上,它通常是被分隔成多個物理內(nèi)存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進(jìn)行數(shù)據(jù)交換。

四、虛擬內(nèi)存的劃分
從Linux操作系統(tǒng)層次上,可將Linux虛擬內(nèi)存劃分為用戶空間內(nèi)存和內(nèi)核空間內(nèi)存。以32位操作系統(tǒng)為例,最大尋址范圍是4G,也就是整個虛擬地址空間是4G,Linux簡化了分段機制,使得虛擬地址與線性地址總是一致的。Linux一般把這個4G的地址空間劃分為兩個部分:其中 0~3G為用戶程序地址空間,虛地址0x00000000到0xBFFFFFFF,供各個進(jìn)程使用;3G~4G為內(nèi)核的地址空間,虛擬地址 0xC0000000到0xFFFFFFFF, 供內(nèi)核使用。
這里有幾點:
用戶進(jìn)程通常情況下只能訪問用戶空間( 0~3G)的虛擬地址,不能訪問內(nèi)核空間的虛擬地址。例外情況只有用戶進(jìn)程進(jìn)行系統(tǒng)調(diào)用(代表用戶進(jìn)程在內(nèi)核態(tài)執(zhí)行)等時刻可以訪問到內(nèi)核空間。
每個進(jìn)程的用戶空間(0-3G)完全獨立、互不相干,內(nèi)核空間(3G-4G)則由則由所有進(jìn)程以及內(nèi)核共享。
用戶空間對應(yīng)進(jìn)程,所以每當(dāng)進(jìn)程切換,用戶空間就會跟著變化;而內(nèi)核空間是由內(nèi)核負(fù)責(zé)映射,它并不會跟著進(jìn)程變化,是固定的。內(nèi)核空間地址有自己對應(yīng)的頁表,用戶進(jìn)程各自有不同的頁表。

五、虛擬地址與物理地址的映射方案
既然我們在程序和物理地址之間增加了虛擬地址,那么就要解決怎么從虛擬地址映射到物理地址,因為程序最終肯定是運行在物理內(nèi)存中的,主要有分段和分頁兩種技術(shù)。
分段(Segmentation):將程序所需要的內(nèi)存地址空間大小的虛擬空間映射到某個物理地址空間。

我們將兩塊大小相同的虛擬地址空間和實際物理地址空間一一映射,即虛擬地址空間中的每個字節(jié)對應(yīng)于實際地址空間中的每個字節(jié),這個映射過程由軟件來設(shè)置映射的機制,實際的轉(zhuǎn)換由硬件來完成。
分析:這種做法解決了地址隔離和地址重定位問題,但是并沒有解決效率問題。因為這種內(nèi)存映射機制仍然是以程序為單位,當(dāng)內(nèi)存不足時仍然需要將整個程序交換到磁盤,這樣內(nèi)存使用的效率仍然很低。
分頁(Page) :分頁機制就是把內(nèi)存地址空間分為若干個很小的固定大小的頁。
當(dāng)然這個粒度不能太大也不能太小。太大浪費,太小又影響分配效率。Linux中一般頁的大小是4KB。然后我們把進(jìn)程的地址空間按頁分割,把常用的數(shù)據(jù)和代碼頁裝載到內(nèi)存中,不常用的代碼和數(shù)據(jù)保存在磁盤中。

我們可以看到進(jìn)程1和進(jìn)程2的虛擬地址空間都被映射到了不連續(xù)的物理地址空間內(nèi)(這個意義很大,如果有一天我們的連續(xù)物理地址空間不夠,但是不連續(xù)的地址空間很多,如果沒有這種技術(shù),我們的程序就沒有辦法運行),甚至他們共用了一部分物理地址空間,這就是共享內(nèi)存。進(jìn)程1的虛擬頁VP2和VP3被交換到了磁盤中,在程序需要這兩頁的時候,Linux內(nèi)核會產(chǎn)生一個缺頁異常,然后異常管理程序會將其讀到內(nèi)存中。
分析:分頁的內(nèi)存劃分與分段不同,分段是程序需要多少,分配多大。分頁是我固定把整個地址空間按4K大小等分,程序需要多少,就給你拼幾塊。所以它同樣也解決了地址隔離和地址重定位問題。另外,它把常用的數(shù)據(jù)和代碼頁裝載到內(nèi)存中,不常用的代碼和數(shù)據(jù)保存在磁盤中,這樣提升了效率。
另外,分頁機制的實現(xiàn)需要硬件的實現(xiàn),這個硬件名字叫做MMU(Memory Management Unit),他就是專門負(fù)責(zé)從虛擬地址到物理地址轉(zhuǎn)換的,也就是從虛擬頁找到物理頁。
六、地址類型
物理地址:CPU通過地址總線的尋址,找到的真實的物理內(nèi)存對應(yīng)地址。
邏輯地址:機器語言指令中,用來指定一個操作數(shù)或者是一條指令的地址。即程序代碼經(jīng)過編譯后出現(xiàn)在 匯編程序中地址。
虛擬地址(線性地址):是CPU所能尋址的空間范圍。每個進(jìn)程所能使用的虛擬地址大小取決于CPU的尋址能力,處理器是多少位的,就有多少位地址總線,以32位系統(tǒng)為例,那么32位總線就一次讀寫能力是32bit(4Byte).最大尋址范圍為2^32 - 1(4G).
轉(zhuǎn)換關(guān)系:

七、思考
經(jīng)過上面的總結(jié),我們知道了所謂的Linux內(nèi)存管理,實際上是虛擬內(nèi)存的管理,而虛擬內(nèi)存又分為內(nèi)核空間和用戶空間。 對應(yīng)到Android上,也就是Kernel層內(nèi)存管理 和 用戶層 Native + Dalvik 內(nèi)存管理。
那么我理解的內(nèi)存管理最終的目的是:將系統(tǒng)內(nèi)有限的物理內(nèi)存如何及時有效的分配給多個程序。細(xì)分為兩點:最小化管理內(nèi)存所需的時間。最大化程序的可用內(nèi)存。
參考:
https://blog.csdn.net/h674174380/article/details/75453750
https://blog.csdn.net/acs713/article/details/42836335