逻辑地址、线性地址、物理地址和虚拟地址初步认识
早期的內(nèi)存分配機(jī)制
在早期的計(jì)算機(jī)中,要運(yùn)行一個(gè)程序,會(huì)把這些程序全都裝入內(nèi)存,程序都是直接運(yùn)行在內(nèi)存上的,也就是說(shuō)程序中訪(fǎng)問(wèn)的內(nèi)存地址都是實(shí)際的物理內(nèi)存地址。當(dāng)計(jì)算機(jī)同時(shí)運(yùn)行多個(gè)程序時(shí),必須保證這些程序用到的內(nèi)存總量要小于計(jì)算機(jī)實(shí)際物理內(nèi)存的大小。
那當(dāng)程序同時(shí)運(yùn)行多個(gè)程序時(shí),操作系統(tǒng)是如何為這些程序分配內(nèi)存的呢?下面通過(guò)實(shí)例來(lái)說(shuō)明當(dāng)時(shí)的內(nèi)存分配方法:
某臺(tái)計(jì)算機(jī)總的內(nèi)存大小是128M,現(xiàn)在同時(shí)運(yùn)行兩個(gè)程序A和B,A需占用內(nèi)存10M,B需占用內(nèi)存110.計(jì)算機(jī)在給程序分配內(nèi)存時(shí)會(huì)采取這樣的方法:先將內(nèi)存中的前10M分配給程序A,接著再?gòu)膬?nèi)存中剩余的118M中劃分出110M分配給程序B。這種分配方法可以保證程序A和程序B都能運(yùn)行,但是這種簡(jiǎn)單的內(nèi)存分配策略問(wèn)題很多。
早期的內(nèi)存分配方法:
問(wèn)題1 :進(jìn)程地址空間不隔離。由于程序都是直接訪(fǎng)問(wèn)物理內(nèi)存,所以惡意程序可以隨意修改別的進(jìn)程的內(nèi)存數(shù)據(jù),以達(dá)到破壞的目的。有些非惡意的,但是有bug的程序也可能不小心修改了其它程序的內(nèi)存數(shù)據(jù),就會(huì)導(dǎo)致其它程序的運(yùn)行出現(xiàn)異常。這種情況對(duì)用戶(hù)來(lái)說(shuō)是無(wú)法容忍的,因?yàn)橛脩?hù)希望使用計(jì)算機(jī)的時(shí)候,其中一個(gè)任務(wù)失敗了,至少不能影響其它的任務(wù)。
問(wèn)題2 :內(nèi)存使用效率低。在A和B都運(yùn)行的情況下,如果用戶(hù)又運(yùn)行了程序C,而程序C需要20M大小的內(nèi)存才能運(yùn)行,而此時(shí)系統(tǒng)只剩下8M的空間可供使用,所以此時(shí)系統(tǒng)必須在已運(yùn)行的程序中選擇一個(gè)將該程序的數(shù)據(jù)暫時(shí)拷貝到硬盤(pán)上,釋放出部分空間來(lái)供程序C使用,然后再將程序C的數(shù)據(jù)全部裝入內(nèi)存中運(yùn)行。想象得到,在這個(gè)過(guò)程中,有大量的數(shù)據(jù)在裝入裝出,導(dǎo)致效率十分低下。
問(wèn)題3 :程序運(yùn)行的地址不確定。當(dāng)內(nèi)存中的剩余空間可以滿(mǎn)足程序C的要求后,操作系統(tǒng)會(huì)在剩余空間中隨機(jī)分配一段連續(xù)的20M大小的空間給程序C使用,因?yàn)槭请S機(jī)分配的,所以程序運(yùn)行的地址是不確定的。
分段
為 了解決上述問(wèn)題,人們想到了一種變通的方法,就是增加一個(gè)中間層,利用一種間接的地址訪(fǎng)問(wèn)方法訪(fǎng)問(wèn)物理內(nèi)存。按照這種方法,程序中訪(fǎng)問(wèn)的內(nèi)存地址不再是實(shí)際的物理內(nèi)存地址,而是一個(gè)虛擬地址,然后由操作系統(tǒng)將這個(gè)虛擬地址映射到適當(dāng)?shù)奈锢韮?nèi)存地址上。這樣,只要操作系統(tǒng)處理好虛擬地址到物理內(nèi)存地址的映射,就可以保證不同的程序最終訪(fǎng)問(wèn)的內(nèi)存地址位于不同的區(qū)域,彼此沒(méi)有重疊,就可以達(dá)到內(nèi)存地址空間隔離的效果。
當(dāng)創(chuàng)建一個(gè)進(jìn)程時(shí),操作系統(tǒng)會(huì)為該進(jìn)程分配一個(gè) 4GB 大小的虛擬進(jìn)程地址空間。之所以是 4GB ,是因?yàn)樵?32 位的操作系統(tǒng)中,一個(gè)指針長(zhǎng)度是 4 字節(jié),而 4 字節(jié)指針的尋址能力是從 0x00000000~0xFFFFFFFF,最大值 0xFFFFFFFF 表示的即為 4GB 大小的容量。與虛擬地址空間相對(duì)的,還有一個(gè)物理地址空間,這個(gè)地址空間對(duì)應(yīng)的是真實(shí)的物理內(nèi)存。如果你的計(jì)算機(jī)上安裝了 512M 大小的內(nèi)存,那么這個(gè)物理地址空間表示的范圍是 0x00000000~0x1FFFFFFF 。當(dāng)操作系統(tǒng)做虛擬地址到物理地址映射時(shí),只能映射到這一范圍,操作系統(tǒng)也只會(huì)映射到這一范圍。當(dāng)進(jìn)程創(chuàng)建時(shí),每個(gè)進(jìn)程都會(huì)有一個(gè)自己的 4GB 虛擬地址空間。要注意的是這個(gè) 4GB 的地址空間是“虛擬”的,并不是真實(shí)存在的,而且每個(gè)進(jìn)程只能訪(fǎng)問(wèn)自己虛擬地址空間中的數(shù)據(jù),無(wú)法訪(fǎng)問(wèn)別的進(jìn)程中的數(shù)據(jù),通過(guò)這種方法實(shí)現(xiàn)了進(jìn)程間的地址隔離。那是不是這 4GB 的虛擬地址空間應(yīng)用程序可以隨意使用呢?很遺憾,在 Windows 系統(tǒng)下,這個(gè)虛擬地址空間被分成了 4 部分: NULL 指針區(qū)、用戶(hù)區(qū)、 64KB 禁入?yún)^(qū)、內(nèi)核區(qū)。
1)NULL指針區(qū) (0x00000000~0x0000FFFF): 如果進(jìn)程中的一個(gè)線(xiàn)程試圖操作這個(gè)分區(qū)中的數(shù)據(jù),CPU就會(huì)引發(fā)非法訪(fǎng)問(wèn)。他的作用是,調(diào)用 malloc 等內(nèi)存分配函數(shù)時(shí),如果無(wú)法找到足夠的內(nèi)存空間,它將返回 NULL。而不進(jìn)行安全性檢查。它只是假設(shè)地址分配成功,并開(kāi)始訪(fǎng)問(wèn)內(nèi)存地址 0x00000000(NULL)。由于禁止訪(fǎng)問(wèn)內(nèi)存的這個(gè)分區(qū),因此會(huì)發(fā)生非法訪(fǎng)問(wèn)現(xiàn)象,并終止這個(gè)進(jìn)程的運(yùn)行。
2)用戶(hù)模式分區(qū) ( 0x00010000~0xBFFEFFFF):這個(gè)分區(qū)中存放進(jìn)程的私有地址空間。一個(gè)進(jìn)程無(wú)法以任何方式訪(fǎng)問(wèn)另外一個(gè)進(jìn)程駐留在這個(gè)分區(qū)中的數(shù)據(jù) (相同 exe,通過(guò) copy-on-write 來(lái)完成地址隔離)。(在windows中,所有 .exe 和動(dòng)態(tài)鏈接庫(kù)都載入到這一區(qū)域。系統(tǒng)同時(shí)會(huì)把該進(jìn)程可以訪(fǎng)問(wèn)的所有內(nèi)存映射文件映射到這一分區(qū))。
2)隔離區(qū) (0xBFFF0000~0xBFFFFFFF):這個(gè)分區(qū)禁止進(jìn)入。任何試圖訪(fǎng)問(wèn)這個(gè)內(nèi)存分區(qū)的操作都是違規(guī)的。微軟保留這塊分區(qū)的目的是為了簡(jiǎn)化操作系統(tǒng)的現(xiàn)實(shí)。
3)內(nèi)核區(qū) (0xC0000000~0xFFFFFFFF):這個(gè)分區(qū)存放操作系統(tǒng)駐留的代碼。線(xiàn)程調(diào)度、內(nèi)存管理、文件系統(tǒng)支持、網(wǎng)絡(luò)支持和所有設(shè)備驅(qū)動(dòng)程序代碼都在這個(gè)分區(qū)加載。這個(gè)分區(qū)被所有進(jìn)程共享。
應(yīng)用程序能使用的只是用戶(hù)區(qū)而已,大約 2GB 左右 ( 最大可以調(diào)整到 3GB) 。內(nèi)核區(qū)為 2GB ,內(nèi)核區(qū)保存的是系統(tǒng)線(xiàn)程調(diào)度、內(nèi)存管理、設(shè)備驅(qū)動(dòng)等數(shù)據(jù),這部分?jǐn)?shù)據(jù)供所有的進(jìn)程共享,但應(yīng)用程序是不能直接訪(fǎng)問(wèn)的。
人們之所以要?jiǎng)?chuàng)建一個(gè)虛擬地址空間,目的是為了解決進(jìn)程地址空間隔離的問(wèn)題。但程序要想執(zhí)行,必須運(yùn)行在真實(shí)的內(nèi)存上,所以,必須在虛擬地址與物理地址間建立一種映射關(guān)系。這樣,通過(guò)映射機(jī)制,當(dāng)程序訪(fǎng)問(wèn)虛擬地址空間上的某個(gè)地址值時(shí),就相當(dāng)于訪(fǎng)問(wèn)了物理地址空間中的另一個(gè)值。人們想到了一種分段(Sagmentation) 的方法,它的思想是在虛擬地址空間和物理地址空間之間做一一映射。比如說(shuō)虛擬地址空間中某個(gè) 10M 大小的空間映射到物理地址空間中某個(gè) 10M 大小的空間。這種思想理解起來(lái)并不難,操作系統(tǒng)保證不同進(jìn)程的地址空間被映射到物理地址空間中不同的區(qū)域上,這樣每個(gè)進(jìn)程最終訪(fǎng)問(wèn)到的。
物理地址空間都是彼此分開(kāi)的。通過(guò)這種方式,就實(shí)現(xiàn)了進(jìn)程間的地址隔離。還是以實(shí)例說(shuō)明,假設(shè)有兩個(gè)進(jìn)程 A 和 B ,進(jìn)程 A 所需內(nèi)存大小為 10M ,其虛擬地址空間分布在 0x00000000 到 0x00A00000 ,進(jìn)程 B 所需內(nèi)存為 100M ,其虛擬地址空間分布為 0x00000000 到 0x06400000 。那么按照分段的映射方法,進(jìn)程 A 在物理內(nèi)存上映射區(qū)域?yàn)?0x00100000 到 0x00B00000 ,,進(jìn)程 B 在物理內(nèi)存上映射區(qū)域?yàn)?x00C00000 到 0x07000000 。于是進(jìn)程 A 和進(jìn)程 B 分別被映射到了不同的內(nèi)存區(qū)間,彼此互不重疊,實(shí)現(xiàn)了地址隔離。從應(yīng)用程序的角度看來(lái),進(jìn)程 A 的地址空間就是分布在 0x00000000 到 0x00A00000 ,在做開(kāi)發(fā)時(shí),開(kāi)發(fā)人員只需訪(fǎng)問(wèn)這段區(qū)間上的地址即可。應(yīng)用程序并不關(guān)心進(jìn)程 A 究竟被映射到物理內(nèi)存的那塊區(qū)域上了,所以程序的運(yùn)行地址也就是相當(dāng)于說(shuō)是確定的了。 下圖顯示的是分段方式的內(nèi)存映射方法:
分段方式的內(nèi)存映射方法
這種分段的映射方法雖然解決了上述中的問(wèn)題一和問(wèn)題三,但并沒(méi)能解決問(wèn)題二,即內(nèi)存的使用效率問(wèn)題。在分段的映射方法中,每次換入換出內(nèi)存的都是整個(gè)程序, 這樣會(huì)造成大量的磁盤(pán)訪(fǎng)問(wèn)操作,導(dǎo)致效率低下。所以這種映射方法還是稍顯粗糙,粒度比較大。實(shí)際上,程序的運(yùn)行有局部性特點(diǎn),在某個(gè)時(shí)間段內(nèi),程序只是訪(fǎng)問(wèn)程序的一小部分?jǐn)?shù)據(jù),也就是說(shuō),程序的大部分?jǐn)?shù)據(jù)在一個(gè)時(shí)間段內(nèi)都不會(huì)被用到。基于這種情況,人們想到了粒度更小的內(nèi)存分割和映射方法,這種方法就是分頁(yè) (Paging) 。
分頁(yè)
分頁(yè)的基本方法是,將地址空間分成許多的頁(yè)。每頁(yè)的大小由 CPU 決定,然后由操作系統(tǒng)選擇頁(yè)的大小。目前 Inter 系列的 CPU 支持 4KB 或 4MB 的頁(yè)大小,而 PC上目前都選擇使用 4KB 。按這種選擇, 4GB 虛擬地址空間共可以分成 1048576 頁(yè), 512M 的物理內(nèi)存可以分為 131072 個(gè)頁(yè)。顯然虛擬空間的頁(yè)數(shù)要比物理空間的頁(yè)數(shù)多得多。
在分段的方法中,每次程序運(yùn)行時(shí)總是把程序全部裝入內(nèi)存,而分頁(yè)的方法則有所不同。分頁(yè)的思想是程序運(yùn)行時(shí)用到哪頁(yè)就為哪頁(yè)分配內(nèi)存,沒(méi)用到的頁(yè)暫時(shí)保留在硬盤(pán)上。當(dāng)用到這些頁(yè)時(shí)再在物理地址空間中為這些頁(yè)分配內(nèi)存,然后建立虛擬地址空間中的頁(yè)和剛分配的物理內(nèi)存頁(yè)間的映射。
下面通過(guò)介紹一個(gè)可執(zhí)行文件的裝載過(guò)程來(lái)說(shuō)明分頁(yè)機(jī)制的實(shí)現(xiàn)方法。一個(gè)可執(zhí)行文件 (PE 文件 ) 其實(shí)就是一些編譯鏈接好的數(shù)據(jù)和指令的集合,它也會(huì)被分成很多頁(yè),在 PE 文件執(zhí)行的過(guò)程中,它往內(nèi)存中裝載的單位就是頁(yè)。當(dāng)一個(gè) PE 文件被執(zhí)行時(shí),操作系統(tǒng)會(huì)先為該程序創(chuàng)建一個(gè) 4GB 的進(jìn)程虛擬地址空間。前面介紹過(guò),虛擬地址空間只是一個(gè)中間層而已,它的功能是利用一種映射機(jī)制將虛擬地址空間映射到物理地址空間,所以,創(chuàng)建 4GB 虛擬地址空間其實(shí)并不是要真的創(chuàng)建空間,只是要?jiǎng)?chuàng)建那種映射機(jī)制所需要的數(shù)據(jù)結(jié)構(gòu)而已,這種數(shù)據(jù)結(jié)構(gòu)就是頁(yè)目和頁(yè)表。
當(dāng)創(chuàng)建完虛擬地址空間所需要的數(shù)據(jù)結(jié)構(gòu)后,進(jìn)程開(kāi)始讀取 PE 文件的第一頁(yè)。在PE 文件的第一頁(yè)包含了 PE 文件頭和段表等信息,進(jìn)程根據(jù)文件頭和段表等信息,將 PE 文件中所有的段一一映射到虛擬地址空間中相應(yīng)的頁(yè) (PE 文件中的段的長(zhǎng)度都是頁(yè)長(zhǎng)的整數(shù)倍 ) 。這時(shí) PE 文件的真正指令和數(shù)據(jù)還沒(méi)有被裝入內(nèi)存中,操作系統(tǒng)只是據(jù) PE 文件的頭部等信息建立了 PE 文件和進(jìn)程虛擬地址空間中頁(yè)的映射關(guān)系而已。當(dāng) CPU 要訪(fǎng)問(wèn)程序中用到的某個(gè)虛擬地址時(shí),當(dāng) CPU 發(fā)現(xiàn)該地址并沒(méi)有相相關(guān)聯(lián)的物理地址時(shí), CPU 認(rèn)為該虛擬地址所在的頁(yè)面是個(gè)空頁(yè)面, CPU 會(huì)認(rèn)為這是個(gè)頁(yè)錯(cuò)誤 (Page Fault) , CPU 也就知道了操作系統(tǒng)還未給該 PE 頁(yè)面分配內(nèi)存,CPU 會(huì)將控制權(quán)交還給操作系統(tǒng)。操作系統(tǒng)于是為該 PE 頁(yè)面在物理空間中分配一個(gè)頁(yè)面,然后再將這個(gè)物理頁(yè)面與虛擬空間中的虛擬頁(yè)面映射起來(lái),然后將控制權(quán)再還給進(jìn)程,進(jìn)程從剛才發(fā)生頁(yè)錯(cuò)誤的位置重新開(kāi)始執(zhí)行。由于此時(shí)已為 PE 文件的那個(gè)頁(yè)面分配了內(nèi)存,所以就不會(huì)發(fā)生頁(yè)錯(cuò)誤了。隨著程序的執(zhí)行,頁(yè)錯(cuò)誤會(huì)不斷地產(chǎn)生,操作系統(tǒng)也會(huì)為進(jìn)程分配相應(yīng)的物理頁(yè)面來(lái)滿(mǎn)足進(jìn)程執(zhí)行的需求。
分頁(yè)方法的核心思想就是當(dāng)可執(zhí)行文件執(zhí)行到第 x 頁(yè)時(shí),就為第 x 頁(yè)分配一個(gè)內(nèi)存頁(yè) y ,然后再將這個(gè)內(nèi)存頁(yè)添加到進(jìn)程虛擬地址空間的映射表中 , 這個(gè)映射表就相當(dāng)于一個(gè) y=f(x) 函數(shù)。應(yīng)用程序通過(guò)這個(gè)映射表就可以訪(fǎng)問(wèn)到 x 頁(yè)關(guān)聯(lián)的 y 頁(yè)了。
邏輯地址、線(xiàn)性地址、物理地址和虛擬地址的區(qū)別
邏輯地址(Logical Address) 是指由程式產(chǎn)生的和段相關(guān)的偏移地址部分。例如,你在進(jìn)行 C 語(yǔ)言指針編程中,能讀取指針變量本身值( &操作 ),實(shí)際上這個(gè)值就是邏輯地址,他是相對(duì)于你當(dāng)前進(jìn)程數(shù)據(jù)段的地址,不和絕對(duì)物理地址相干。只有在 Intel 實(shí)模式下,邏輯地址才和物理地址相等(因?yàn)閷?shí)模式?jīng)]有分段或分頁(yè)機(jī)制,cpu不進(jìn)行自動(dòng)地址轉(zhuǎn)換);邏輯也就是在Intel保護(hù)模式下程式執(zhí)行代碼段限長(zhǎng)內(nèi)的偏移地址(假定代碼段、數(shù)據(jù)段如果完全相同)。應(yīng)用程式員僅需和邏輯地址打交道,而分段和分頁(yè)機(jī)制對(duì)你來(lái)說(shuō)是完全透明的,僅由系統(tǒng)編程人員涉及。應(yīng)用程式員雖然自己能直接操作內(nèi)存,那也只能在操作系統(tǒng)給你分配的內(nèi)存段操作。
線(xiàn)性地址(Linear Address) 是邏輯地址到物理地址變換之間的中間層。程式代碼會(huì)產(chǎn)生邏輯地址,或說(shuō)是段中的偏移地址,加上相應(yīng)段的基地址就生成了一個(gè)線(xiàn)性地址。如果啟用了分頁(yè)機(jī)制,那么線(xiàn)性地址能再經(jīng)變換以產(chǎn)生一個(gè)物理地址。若沒(méi)有啟用分頁(yè)機(jī)制,那么線(xiàn)性地址直接就是物理地址。Intel 80386 的線(xiàn)性地址空間容量為 4G(2的32次方即32根地址總線(xiàn)尋址)。
物理地址(Physical Address) 是指出目前 CPU 外部地址總線(xiàn)上的尋址物理內(nèi)存的地址信號(hào),是地址變換的最終結(jié)果地址。如果啟用了分頁(yè)機(jī)制,那么線(xiàn)性地址會(huì)使用頁(yè)目錄和頁(yè)表中的項(xiàng)變換成物理地址。如果沒(méi)有啟用分頁(yè)機(jī)制,那么線(xiàn)性地址就直接成為物理地址了。
虛擬內(nèi)存(Virtual Memory)是指計(jì)算機(jī)呈現(xiàn)出要比實(shí)際擁有的內(nèi)存大得多的內(nèi)存量。因此他允許程式員編制并運(yùn)行比實(shí)際系統(tǒng)擁有的內(nèi)存大得多的程式。這使得許多大型項(xiàng)目也能夠在具有有限內(nèi)存資源的系統(tǒng)上實(shí)現(xiàn)。一個(gè)非常恰當(dāng)?shù)谋扔魇?#xff1a;你不必非常長(zhǎng)的軌道就能讓一列火車(chē)從上海開(kāi)到北京你只需要足夠長(zhǎng)的鐵軌(比如說(shuō)3公里)就能完成這個(gè)任務(wù)。采取的方法是把后面的鐵軌即時(shí)鋪到火車(chē)的前面,只要你的操作足夠快并能滿(mǎn)足需求,列車(chē)就能象在一條完整的軌道上運(yùn)行。這也就是虛擬內(nèi)存管理需要完成的任務(wù)。在Linux0.11內(nèi)核中,給每個(gè)程式(進(jìn)程)都劃分了總?cè)萘繛?4MB的虛擬內(nèi)存空間。因此程式的邏輯地址范圍是0x0000000到0x4000000。有時(shí)我們也把邏輯地址稱(chēng)為虛擬地址。因?yàn)闋钫Z(yǔ)從句:虛擬內(nèi)存空間的概念類(lèi)似,邏輯地址也是和實(shí)際物理內(nèi)存容量無(wú) 的。邏輯地址和物理地址的“差距”是0xC0000000,是由于虛擬地址 - >線(xiàn)性地址 - >物理地址映射正好差這個(gè)值。這個(gè)值是由操作系統(tǒng)指定的。機(jī)理邏輯地址(或稱(chēng)為虛擬地址)到線(xiàn)性地址是由CPU的段機(jī)制自動(dòng)轉(zhuǎn)換的。如果沒(méi)有開(kāi)啟分頁(yè)管理,則線(xiàn)性地址就是物理地址。如果開(kāi)啟了分頁(yè)管理,那么系統(tǒng)程式需要參和線(xiàn)性地址到物理地址的轉(zhuǎn)換過(guò)程。具體是通過(guò)設(shè)置頁(yè)目錄表和頁(yè)表項(xiàng)進(jìn)行的。
總結(jié)
以上是生活随笔為你收集整理的逻辑地址、线性地址、物理地址和虚拟地址初步认识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: arm Linux 中断管理机制
- 下一篇: cat /proc/meminfo 各字