天天看點

Linux記憶體管理--虛拟位址、邏輯位址、線性位址和實體位址的差別(一)【轉】

分析linux記憶體管理機制,離不了上述幾個概念,在介紹上述幾個概念之前,先從《深入了解linux核心》這本書中摘抄幾段關于上述名詞的解釋:

一、《深入了解linux核心》的解釋

邏輯位址(Logical Address) 

       包含在機器語言指令中用來指定一個操作數或一條指令的位址(有點深奧)。這種尋址方式在80x86著名的分段結構中表現得尤為具體,它促使windows程式員把程式分成若幹段。每個邏輯位址都由一個段和偏移量組成,偏移量指明了從段開始的地方到實際位址之間的距離。

線性位址(linear address)(也稱虛拟位址 virtual address)

       是一個32位無符号整數,可以用來表示高達4GB的位址,線性位址通常用十六進制數字表示,值的範圍從0x00000000到0xffffffff。

實體位址(physical address)

       用于記憶體晶片級記憶體單元尋址。它們與從微處理器的位址引腳按發送到記憶體總線上的電信号相對應。實體位址由32位或36位無符号整數表示。(其實這個最好了解,就是實實在在的位址)

       (PS:在下面的解釋就可以看到,有時也将邏輯位址看做虛拟位址,但是《深入了解linux核心》中将線性位址看做虛拟位址)

       首先說一句話:linux關于記憶體尋址可以分為幾個階段,首先由分段機制,然後有分頁機制。

分頁機制在段機制之後進行,以完成線性—實體位址的轉換過程。段機制把邏輯位址轉換為線性址頁機制進一步把該線性位址再轉換為實體位址

         下面是我從網上查找資料了解到的,同時添加了自己的了解

二、第二種解釋

邏輯位址(Logical Address)

       是指由程式産生的與段相關的偏移位址部分。例如,你在進行C語言指針程式設計中,可以讀取指針變量本身值(&操作),實際上這個值就是邏輯位址,它是相對于你目前程序資料段的位址,不和絕對實體位址相幹。隻有在Intel實模式下,邏輯位址才和實體位址相等(因為實模式沒有分段或分頁機制,Cpu不進行自動位址轉換);邏輯也就是在Intel保護模式下程式執行代碼段限長内的偏移位址(假定代碼段、資料段如果完全一樣)。應用程式員僅需與邏輯位址打交道,而分段和分頁機制對您來說是完全透明的,僅由系統程式設計人員涉及。應用程式員雖然自己可以直接操作記憶體,那也隻能在作業系統給你配置設定的記憶體段操作。(也就是說,咱們應用程式中看到的位址都是邏輯位址。)

   如果是程式員,那麼邏輯位址對你來說應該是輕而易舉就可以了解的。我們在寫C代碼的時候經常說我們定義的結構體首位址的偏移量,函數的入口偏移量,數組首位址等等。當我們在考究這些概念的時候,其實是相對于你這個程式而言的。并不是對于整個作業系統而言的。也就是說,邏輯位址是相對于你所編譯運作的具體的程式(或者叫程序吧,事實上在運作時就是當作一個程序來執行的)而言。你的編譯好的程式的入口位址可以看作是首位址,而邏輯位址我們通常可以認為是在這個程式中,編譯器為我們配置設定好的相對于這個首位址的偏移,或者說以這個首位址為起點的一個相對的位址值。(PS:這麼來看,邏輯位址就是一個段内偏移量,但是這麼說違背了邏輯位址的定義,在intel段是管理中,一個邏輯位址,是由一個段辨別符加上一個指定段内相對位址的偏移量,表示為 [段辨別符:段内偏移量])

    當我們輕按兩下一個可執行程式時,就是給作業系統提供了這個程式運作的入口位址。之後shell把可執行檔案的位址傳入核心。進入核心後,會fork一個新的程序出來,新的程序首先配置設定相應的記憶體區域。這裡會碰到一個著名的概念叫做Copy On Write,即寫時複制技術。這裡不詳細講述,總之新的程序在fork出來之後,新的程序也就獲得了整個的PCB結構,繼而會調用exec函數轉而去将磁盤中的代碼加載到記憶體區域中。這時候,程序的PCB就被加入到可執行程序的隊列中,當CPU排程到這個程序的時候就真正的執行了。

   我們大可以把程式運作的入口位址了解為邏輯位址的起始位址,也就是說,一個程式的開始的位址。以及以後用到的程式的相關資料或者代碼相對于這個起始位址的位置(這是由編譯器事先安排好的),就構成了我們所說的邏輯位址。邏輯位址就是相對于一個具體的程式(事實上是一個程序,即程式真正被運作時的相對位址)而言的。這麼了解在細節上有一定的偏差,隻要領會即可。

   總之一句話,邏輯位址是相對于應用程式而言的。

邏輯位址産生的曆史背景:

     追根求源,Intel的8位機8080CPU,資料總線(DB)為8位,位址總線(AB)為16位。那麼這個16位位址資訊也是要通過8位資料總線來傳送,也是要在資料通道中的暫存器,以及在CPU中的寄存器和記憶體中存放的,但由于AB正好是DB的整數倍,故不會産生沖突!

    但當上升到16位機後,Intel8086/8088CPU的設計由于當年IC內建技術和外封裝及引腳技術的限制,不能超過40個引腳。但又感覺到8位機原來的位址尋址能力2^16=64KB太少了,但直接增加到16的整數倍即令AB=32位又是達不到的。故而隻能把AB暫時增加4條成為20條。則

2^20=1MB的尋址能力已經增加了16倍。但此舉卻造成了AB的20位和DB的16位之間的沖突,20位位址資訊既無法在DB上傳送,又無法在16位的CPU寄存器和記憶體單元中存放。于是應運而生就産生了CPU段結構的原理。Intel為了相容,将遠古時代的段式記憶體管理方式保留了下來,也就存在了邏輯位址

線性位址(Linear Address) 

    是邏輯位址到實體位址變換之間的中間層。程式代碼會産生邏輯位址,或者說是段中的偏移位址,加上相應段的基位址就生成了一個線性位址。如果啟用了分頁機制,那麼線性位址可以再經變換以産生一個實體位址。若沒有啟用分頁機制,那麼線性位址直接就是實體位址。Intel

80386的線性位址空間容量為4G(2的32次方即32根位址總線尋址)。

    我們知道每台計算機有一個CPU(我們從單CPU來說吧。多CPU的情況應該是雷同的),最終所有的指令操作或者資料等等的運算都得由這個CPU來進行,而與CPU相關的寄存器就是暫存一些相關資訊的存儲記憶裝置。是以,從CPU的角度出發的話,我們可以将計算機的相關裝置或者部件簡單分為兩類:一是資料或指令存儲記憶裝置(如寄存器,記憶體等等),一種是資料或指令通路(如位址線,資料線等等)。線性位址的本質就是“CPU所看到的位址”。如果我們追根溯源,就會發現線性位址的就是伴随着Intel的X86體系結構的發展而産生的。當32位CPU出現的時候,它的可尋址範圍達到4GB,而相對于記憶體大小來說,這是一個相當巨大的數字,我們也一般不會用到這麼大的記憶體。那麼這個時候CPU可見的4GB空間和記憶體的實際容量産生了差距。而線性位址就是用于描述CPU可見的這4GB空間。我們知道在多程序作業系統中,每個程序擁有獨立的位址空間,擁有獨立的資源。但對于某一個特定的時刻,隻有一個程序運作于CPU之上。此時,CPU看到的就是這個程序所占用的4GB空間,就是這個線性位址。而CPU所做的操作,也是針對這個線性空間而言的。之是以叫線性空間,大概是因為人們覺得這樣一個連續的空間排列成一線更加容易了解吧。其實就是CPU的可尋址範圍。

   對linux而言,CPU将4GB劃分為兩個部分,0-3GB為使用者空間(也可以叫核外空間),3-4GB為核心空間(也可以叫核内空間)。作業系統相關的代碼,即核心部分的代碼資料都會映射到核心空間,而使用者程序則會映射到使用者空間。至于系統是如何将線性位址轉換到實際的實體記憶體上,在下一篇文章講解,無外乎段式管理和頁式管理。

實體位址(Physical Address)

是指出現在CPU外部位址總線上的尋址實體記憶體的位址信号,是位址變換的最終結果位址。如果啟用了分頁機制,那麼線性位址會使用頁目錄和頁表中的項變換成實體位址。如果沒有啟用分頁機制,那麼線性位址就直接成為實體位址了。

三、第三種解釋

虛拟記憶體(Virtual Memory) 

是指計算機呈現出要比實際擁有的記憶體大得多的記憶體量。是以它允許程式員編制并運作比實際系統擁有的記憶體大得多的程式。這使得許多大型項目也能夠在具有有限記憶體資源的系統上實作。一個很恰當的比喻是:你不需要很長的軌道就可以讓一列火車從上海開到北京。你隻需要足夠長的鐵軌(比如說3公裡)就可以完成這個任務。采取的方法是把後面的鐵軌立刻鋪到火車的前面,隻要你的操作足夠快并能滿足要求,列車就能象在一條完整的軌道上運作。這也就是虛拟記憶體管理需要完成的任務。在Linux

0.11核心中,給每個程式(程序)都劃分了總容量為64MB的虛拟記憶體空間。是以程式的邏輯位址範圍是0x0000000到0x4000000。

有時我們也把邏輯位址稱為虛拟位址。因為與虛拟記憶體空間的概念類似,邏輯位址也是與實際實體記憶體容量無關的。(這一點和上面的解釋有一點差別,往下的解釋就按照這個繼續)

    邏輯位址與實體位址的“差距”是0xC0000000,是由于虛拟位址->線性位址->實體位址映射正好差這個值。這個值是由作業系統指定的。

   虛拟位址到實體位址的轉化方法是與體系結構相關的。一般來說有分段、分頁兩種方式。以現在的x86 cpu為例,分段分頁都是支援的。MemoryMangement Unit負責從邏輯位址到實體位址的轉化。邏輯位址是段辨別+段内偏移量的形式,MMU通過查詢段表,可以把邏輯位址轉化為線性位址。如果cpu沒有開啟分頁功能,那麼線性位址就是實體位址;如果cpu開啟了分頁功能,MMU還需要查詢頁表來将線性位址轉化為實體位址:

邏輯位址 ----(段表)---> 線性位址 — (頁表)—> 實體位址

不同的邏輯位址可以映射到同一個線性位址上;不同的線性位址也可以映射到同一個實體位址上;是以是多對一的關系。另外,同一個線性位址,在發生換頁以後,也可能被重新裝載到另外一個實體位址上。是以這種多對一的映射關系也會随時間發生變化。

四、第四種解釋

  1. 程式(程序)的虛拟位址和邏輯位址

邏輯位址(logicaladdress)指程式産生的段内偏移位址。應用程式隻與邏輯位址打交道,分段分頁對應用程式來說是透明的。也就是說C語言中的&,彙編語言中的符号位址,C中嵌入式彙編的”m”對應的都是邏輯位址。

A配置設定的0x08111111這個位址就是邏輯位址。不過不好意思,這樣說,好像又違背了Intel中段式管理中,對邏輯位址要求,“一個邏輯位址,是由一個段辨別符加上一個指定段内相對位址的偏移量,表示為[段辨別符:段内偏移量],也就是說,上例中那個0x08111111,應該表示為[A的代碼段辨別符: 0x08111111],這樣,才完整一些”

線性位址(linear address)或也叫虛拟位址(virtual address):跟邏輯位址類似,它也是一個不真實的位址,如果邏輯位址是對應的硬體平台段式管理轉換前位址的話,那麼線性位址則對應了硬體頁式記憶體的轉換前位址。 

  1. 實際實體記憶體位址

實體位址(physicaladdress)是CPU外部位址總線上的尋址信号,是位址變換的最終結果,一個實體位址始終對應實際記憶體中的一個存儲單元。對80386保護模式來說,如果開啟分頁機制,線性位址經過頁變換産生實體位址。如果沒有開啟分頁機制,線性位址直接對應實體位址。頁目錄表項、頁表項對應都是實體位址。

是指出現在CPU外部位址總線上的尋址實體記憶體的位址信号,是位址變換的最終結果位址。如果啟用了分頁機制,那麼線性位址會使用頁目錄和頁表中的項變換成實體位址。如果沒有啟用分頁機制,那麼線性位址就直接成為實體位址了。

CPU連接配接的位址總線相對應。這個概念應該是這幾個概念中最好了解的一個,但是值得一提的是,雖然可以直接把實體位址了解成插在機器上那根記憶體本身,把記憶體看成一個從0位元組一直到最大空量逐位元組的編号的大數組,然後把這個數組叫做實體位址,但是事實上,這隻是一個硬體提供給軟體的抽像,記憶體的尋址方式并不是這樣。是以,說它是“與位址總線相對應”,是更貼切一些,不過抛開對實體記憶體尋址方式的考慮,直接把實體位址與實體的記憶體一一對應,也是可以接受的。也許錯誤的了解更利于形而上的抽像。

Linux0.11的核心資料段,核心代碼段基位址都是0,是以對核心來說,邏輯位址就是線性位址。又因為1個頁目錄表和4個頁表完全映射16M實體記憶體,是以線性位址也就是實體位址。故對linux0.11核心來說,邏輯位址,線性位址,實體位址重合。

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

       虛拟位址是對整個記憶體(不要與機器上插那條對上号)的抽像描述。它是相對于實體記憶體來講的,可以直接了解成“不真實的”,“假的”記憶體,例如,一個0x08000000記憶體位址,它并不對就實體位址上那個大數組中0x08000000 - 1那個位址元素;之是以是這樣,是因為現代作業系統都提供了一種記憶體管理的抽像,即虛拟記憶體(virtual memory)。程序使用虛拟記憶體中的位址,由作業系統協助相關硬體,把它“轉換”成真正的實體位址。這個“轉換”,是所有問題讨論的關鍵。有了這樣的抽像,一個程式,就可以使用比真實實體位址大得多的位址空間。(拆東牆,補西牆,銀行也是這樣子做的),甚至多個程序可以使用相同的位址。不奇怪,因為轉換後的實體位址并非相同的。可以把連接配接後的程式反編譯看一下,發現連接配接器已經為程式配置設定了一個位址,例如,要調用某個函數A,代碼不是call A,而是call 0x0811111111 ,也就是說,函數A的位址已經被定下來了。沒有這樣的“轉換”,沒有虛拟位址的概念,這樣做是根本行不通的。打住了,這個問題再說下去,就收不住了。

五、總結

CPU将一個虛拟記憶體空間中的位址轉換為實體位址,需要進行兩步:首先将給定一個邏輯位址(其實是段内偏移量,這個一定要了解!!!),CPU要利用其段式記憶體管理單元,先将為個邏輯位址轉換成一個線程位址,再利用其頁式記憶體管理單元,轉換為最終實體位址。

線性位址:是CPU所能尋址的空間或者範圍。

實體位址:是機器中實際的記憶體位址。換言之,是機器中的記憶體容量範圍。

邏輯位址:是對程式而言的。一般以Seg:Offset來表示。(程式員自己看到的位址)

是以,若要确實比較三者的話,應有以下關系:線性位址大于等于實體位址(PS:但二者的位址空間是一樣的),而邏輯位址大于線性位址。邏輯位址通過段表變換成線性位址,此時如果并未開啟分頁機制的情況下,邏輯位址直接轉換成CPU所能尋址的空間。若已開啟則通過頁表完成線性位址到實體位址的變換。

是以,三者最準确的關系是:邏輯位址通過線性位址完成實體位址的映射,線性位址在三者之中完全是充當"橋"的作用。

繼續閱讀