邏輯地址(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ì)您來說是完全透明的,僅由系統(tǒng)編程人員涉及。應(yīng)用程序員雖然自己可以直接操作內(nèi)存,那也只能在操作系統(tǒng)給你分配的內(nèi)存段操作。
如果是程序員,那么邏輯地址對(duì)你來說應(yīng)該是輕而易舉就可以理解的。我們?cè)趯慍代碼的時(shí)候經(jīng)常說我們定義的結(jié)構(gòu)體首地址的偏移量,函數(shù)的入口偏移量,數(shù)組首地址等等。當(dāng)我們?cè)诳季窟@些概念的時(shí)候,其實(shí)是相對(duì)于你這個(gè)程序而言的。并不是對(duì)于整個(gè)操作系統(tǒng)而言的。也就是說,邏輯地址是相對(duì)于你所編譯運(yùn)行的具體的程序(或者叫進(jìn)程吧,事實(shí)上在運(yùn)行時(shí)就是當(dāng)作一個(gè)進(jìn)程來執(zhí)行的)而言。你的編譯好的程序的入口地址可以看作是首地址,而邏輯地址我們通??梢哉J(rèn)為是在這個(gè)程序中,編譯器為我們分配好的相對(duì)于這個(gè)首地址的偏移,或者說以這個(gè)首地址為起點(diǎn)的一個(gè)相對(duì)的地址值。
當(dāng)我們雙擊一個(gè)可執(zhí)行程序時(shí),就是給操作系統(tǒng)提供了這個(gè)程序運(yùn)行的入口地址。之后shell把可執(zhí)行文件的地址傳入內(nèi)核。進(jìn)入內(nèi)核后,會(huì)fork一個(gè)新的進(jìn)程出來,新的進(jìn)程首先分配相應(yīng)的內(nèi)存區(qū)域。這里會(huì)碰到一個(gè)著名的概念叫做Copy On Write,即寫時(shí)復(fù)制技術(shù)。這里不詳細(xì)講述,總之新的進(jìn)程在fork出來之后,新的進(jìn)程也就獲得了整個(gè)的PCB結(jié)構(gòu),繼而會(huì)調(diào)用exec函數(shù)轉(zhuǎn)而去將磁盤中的代碼加載到內(nèi)存區(qū)域中。這時(shí)候,進(jìn)程的PCB就被加入到可執(zhí)行進(jìn)程的隊(duì)列中,當(dāng)CPU調(diào)度到這個(gè)進(jìn)程的時(shí)候就真正的執(zhí)行了。
我們大可以把程序運(yùn)行的入口地址理解為邏輯地址的起始地址,也就是說,一個(gè)程序的開始的地址。以及以后用到的程序的相關(guān)數(shù)據(jù)或者代碼相對(duì)于這個(gè)起始地址的位置(這是由編譯器事先安排好的),就構(gòu)成了我們所說的邏輯地址。邏輯地址就是相對(duì)于一個(gè)具體的程序(事實(shí)上是一個(gè)進(jìn)程,即程序真正被運(yùn)行時(shí)的相對(duì)地址)而言的。盡管我們這樣理解可能有一些細(xì)節(jié)上的偏差,但是比起網(wǎng)上一些含糊其辭,讓人不知所云的描述要好得多,實(shí)用得多,等到自己對(duì)這個(gè)地址有更加深刻的理解的時(shí)候,再對(duì)上面的理解進(jìn)行一些補(bǔ)充或者糾正。
總之一句話,邏輯地址是相對(duì)于應(yīng)用程序而言的。
邏輯地址產(chǎn)生的歷史背景:
追根求源,Intel的8位機(jī)8080CPU,數(shù)據(jù)總線(DB)為8位,地址總線(AB)為16位。那么這個(gè)16位地址信息也是要通過8位數(shù)據(jù)總線來傳送,也是要在數(shù)據(jù)通道中的暫存器,以及在CPU中的寄存器和內(nèi)存中存放的,但由于AB正好是 DB的整數(shù)倍,故不會(huì)產(chǎn)生矛盾!
但當(dāng)上升到16位機(jī)后,Intel8086/8088CPU的設(shè)計(jì)由于當(dāng)年IC集成技術(shù)和外封裝及引腳技術(shù)的限制,不能超過40個(gè)引腳。但又感覺到8位機(jī)原來的地址尋址能力2^16=64KB太少了,但直接增加到16的整數(shù)倍即令A(yù)B=32位又是達(dá)不到的。故而只能把AB暫時(shí)增加4條成為20條。則 2^20=1MB的尋址能力已經(jīng)增加了16倍。但此舉卻造成了AB的20位和DB的16位之間的矛盾,20位地址信息既無法在DB上傳送,又無法在16位的CPU寄存器和內(nèi)存單元中存放。于是應(yīng)運(yùn)而生就產(chǎn)生了CPU段結(jié)構(gòu)的原理。
線性地址(Linear Address) 是邏輯地址到物理地址變換之間的中間層。程序代碼會(huì)產(chǎn)生邏輯地址,或者說是段中的偏移地址,加上相應(yīng)段的基地址就生成了一個(gè)線性地址。如果啟用了分頁(yè)機(jī)制,那么線性地址可以再經(jīng)變換以產(chǎn)生一個(gè)物理地址。若沒有啟用分頁(yè)機(jī)制,那么線性地址直接就是物理地址。Intel 80386的線性地址空間容量為4G(2的32次方即32根地址總線尋址)。
線性地址:
我們知道每臺(tái)計(jì)算機(jī)有一個(gè)CPU(我們從單CPU來說吧。多CPU的情況應(yīng)該是雷同的),最終所有的指令操作或者數(shù)據(jù)等等的運(yùn)算都得由這個(gè)CPU來進(jìn)行,而與CPU相關(guān)的寄存器就是暫存一些相關(guān)信息的存儲(chǔ)記憶設(shè)備。因此,從CPU的角度出發(fā)的話,我們可以將計(jì)算機(jī)的相關(guān)設(shè)備或者部件簡(jiǎn)單分為兩類:一是數(shù)據(jù)或指令存儲(chǔ)記憶設(shè)備(如寄存器,內(nèi)存等等),一種是數(shù)據(jù)或指令通路(如地址線,數(shù)據(jù)線等等)。線性地址的本質(zhì)就是“CPU所看到的地址”。如果我們追根溯源,就會(huì)發(fā)現(xiàn)線性地址的就是伴隨著Intel的X86體系結(jié)構(gòu)的發(fā)展而產(chǎn)生的。當(dāng)32位CPU出現(xiàn)的時(shí)候,它的可尋址范圍達(dá)到4GB,而相對(duì)于內(nèi)存大小來說,這是一個(gè)相當(dāng)巨大的數(shù)字,我們也一般不會(huì)用到這么大的內(nèi)存。那么這個(gè)時(shí)候CPU可見的4GB空間和內(nèi)存的實(shí)際容量產(chǎn)生了差距。而線性地址就是用于描述CPU可見的這4GB空間。我們知道在多進(jìn)程操作系統(tǒng)中,每個(gè)進(jìn)程擁有獨(dú)立的地址空間,擁有獨(dú)立的資源。但對(duì)于某一個(gè)特定的時(shí)刻,只有一個(gè)進(jìn)程運(yùn)行于CPU之上。此時(shí),CPU看到的就是這個(gè)進(jìn)程所占用的4GB空間,就是這個(gè)線性地址。而CPU所做的操作,也是針對(duì)這個(gè)線性空間而言的。之所以叫線性空間,大概是因?yàn)槿藗冇X得這樣一個(gè)連續(xù)的空間排列成一線更加容易理解吧。其實(shí)就是CPU的可尋址范圍。
對(duì)linux而言,CPU將4GB劃分為兩個(gè)部分,0-3GB為用戶空間(也可以叫核外空間),3-4GB為內(nèi)核空間(也可以叫核內(nèi)空間)。操作系統(tǒng)相關(guān)的代碼,即內(nèi)核部分的代碼數(shù)據(jù)都會(huì)映射到內(nèi)核空間,而用戶進(jìn)程則會(huì)映射到用戶空間。至于系統(tǒng)是如何將線性地址轉(zhuǎn)換到實(shí)際的物理內(nèi)存上,那是另外的話題了。網(wǎng)上到處可以找到相關(guān)文章,我不在此啰嗦。對(duì)于X86,無外乎段式管理和頁(yè)式管理。
物理地址(Physical Address) 是指出現(xiàn)在CPU外部地址總線上的尋址物理內(nèi)存的地址信號(hào),是地址變換的最終結(jié)果地址。如果啟用了分頁(yè)機(jī)制,那么線性地址會(huì)使用頁(yè)目錄和頁(yè)表中的項(xiàng)變換成物理地址。如果沒有啟用分頁(yè)機(jī)制,那么線性地址就直接成為物理地址了。
虛擬內(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ù)谋扔魇牵耗悴恍枰荛L(zhǎng)的軌道就可以讓一列火車從上海開到北京。你只需要足夠長(zhǎng)的鐵軌(比如說3公里)就可以完成這個(gè)任務(wù)。采取的方法是把后面的鐵軌立刻鋪到火車的前面,只要你的操作足夠快并能滿足要求,列車就能象在一條完整的軌道上運(yùn)行。這也就是虛擬內(nèi)存管理需要完成的任務(wù)。在Linux 0.11內(nèi)核中,給每個(gè)程序(進(jìn)程)都劃分了總?cè)萘繛?4MB的虛擬內(nèi)存空間。因此程序的邏輯地址范圍是0x0000000到0x4000000。
有時(shí)我們也把邏輯地址稱為虛擬地址。因?yàn)榕c虛擬內(nèi)存空間的概念類似,邏輯地址也是與實(shí)際物理內(nèi)存容量無關(guān)的。
邏輯地址與物理地址的“差距”是0xC0000000,是由于虛擬地址->線性地址->物理地址映射正好差這個(gè)值。這個(gè)值是由操作系統(tǒng)指定的。
虛擬地址到物理地址的轉(zhuǎn)化方法是與體系結(jié)構(gòu)相關(guān)的。一般來說有分段、分頁(yè)兩種方式。以現(xiàn)在的x86 cpu為例,分段分頁(yè)都是支持的。Memory Mangement Unit負(fù)責(zé)從虛擬地址到物理地址的轉(zhuǎn)化。邏輯地址是段標(biāo)識(shí)+段內(nèi)偏移量的形式,MMU通過查詢段表,可以把邏輯地址轉(zhuǎn)化為線性地址。如果cpu沒有開啟分頁(yè)功能,那么線性地址就是物理地址;如果cpu開啟了分頁(yè)功能,MMU還需要查詢頁(yè)表來將線性地址轉(zhuǎn)化為物理地址:
邏輯地址 ----(段表)---> 線性地址 — (頁(yè)表)—> 物理地址
不同的邏輯地址可以映射到同一個(gè)線性地址上;不同的線性地址也可以映射到同一個(gè)物理地址上;所以是多對(duì)一的關(guān)系。另外,同一個(gè)線性地址,在發(fā)生換頁(yè)以后,也可能被重新裝載到另外一個(gè)物理地址上。所以這種多對(duì)一的映射關(guān)系也會(huì)隨時(shí)間發(fā)生變化。