現代作業系統的記憶體管理機制有兩種:段式管理和頁式管理。
段式記憶體管理,就是將記憶體分成段,每個段的起始地址就是段基地址。地址映射的時候,由邏輯地址加上段基地址而得到物理地址。純粹的段式記憶體管理的缺點很明顯,就是靈活性和效率比較差。首先是段的長度是可變的,這給記憶體的換入換出帶來諸多不便,如何選擇一個段的長度是一個棘手的問題;其次進程在運行過程中,可能會擴充地址空間,這就要增加段,從而造成進程的地址空間由很多小段構成,在進程運行過程中,訪問不同的段時,就需要頻繁切換段基地址;再一點,段式記憶體管理如果有太多的小段,在釋放段的時候,會造成外部片段。
頁式記憶體管理,記憶體分成固定長度的一個個頁片。地址映射的時候,需要先建立頁表,頁表中的每一項都記錄了這個頁的基地址。通過頁表,由邏輯地址的高位部分先找到邏輯地址對應的頁基地址,再由頁基地址位移一定長度就得到最後的物理地址,位移的長度由邏輯地址的低位部分決定。一般情況下,這個過程都可以由硬體完成,所以效率還是比較高的。頁式記憶體管理的優點就是比較靈活,記憶體管理以較小的頁為單位,方便記憶體換入換出和擴充地址空間。
嚴格說Linux採用段頁式記憶體管理,也就是既分段,又分頁。地址映射的時候,先確定對應的段,確定段基地址;段內分頁,再找到對應的頁表項,確定頁基地址;再由邏輯地址低位確定的頁位移量,就能找到最終的物理地址。但是,實際上Linux採用的是頁式記憶體管理。原因是Linux中的段基地址都是0,相當於所有的段都是相同的。這樣做的原因是某些體繫結構的硬體限制,比如Intel的i386。作為軟體的作業系統,必須要符合硬體體系。雖然所有段基地址都是0,但是段的概念在Linux核心中是確實存在的。比如常見的核心程式碼片段、核心資料區段、使用者態程式碼片段、使用者態資料區段等。除了符合硬體要求外,段也是有實際意義的。
電腦記憶體定址
電腦管理記憶體的基本方式有兩種:段式管理和頁式管理。而在使用80x86微處理器時,記憶體位址分為三個不同的地址:邏輯地址,線性地址,物理地址。他們之間有什麼關係,記憶體是如何定址,本文主要介紹的就是記憶體定址。
1、基本概念:
cpu段式管理:段式管理的基本原理是指把一個程式分成若干個段(segment)進行儲存,每個段都是一個邏輯實體(logical entity)。一個使用者作業或進程所包含的段對應一個二維線形虛擬空間,程式通過分段(segmentation)劃分為多個模組,故可以對程式的各個模組分別編寫和編譯。段式管理程式以段為單位分配記憶體,然後通過地址影射機構把段式虛擬位址轉換為虛擬位址。
cpu頁式管理: 頁式管理的基本原理將各進程的虛擬空間劃分成若干個長度相等的頁(一般為4K),頁式管理把記憶體空間按頁的大小劃分成片或者頁面(page frame),然後把頁式虛擬位址與記憶體位址建立一一對應頁表,並用相應的硬體地址變換機構,來解決離散地址變換問題。
邏輯地址:包含在機器語言指令中用來指定一個運算元或一條指令的地址,每個邏輯地址都由一個段和位移量組成,表示為[段標識符:段內位移量]。例如,在C/C++程式中我們使用指標對變數地址操作,該地址就是邏輯地址(準確的應該說是邏輯地址的段內位移量)。對應上述段式管理,邏輯地址是段式管理轉換前的程式地址。
線性地址:也稱為虛擬位址,它是一個32位不帶正負號的整數,故可以用來表達高達4GB的地址。線性地址同邏輯地址一樣也是不真實的地址。對應上述頁式管理,線性地址是頁式管理轉換前的地址。
物理地址:用於記憶體晶片級記憶體單元定址,與處理器和CPU串連的地址匯流排相對應。一般情況下,我們說的電腦記憶體條中的記憶體就是它(雖然不準確)。
有了上述的基本概念後,很顯然,CPU將一個虛擬記憶體空間中的地址轉換為物理地址,需要進行兩步:首先將給定一個邏輯地址,CPU要利用其段式記憶體管理單元,先將每個邏輯地址轉換成一個線程地址,再利用其頁式記憶體管理單元,轉換為最終物理地址。這就是我們所知道的段頁式管理,這樣兩次轉換的好處可以克服段式管理和頁式管理的缺點。
2、CPU段式記憶體管理:邏輯地址轉換為線性地址
邏輯地址由兩部分組成:一個段標識符和一個指定段內相對位址的位移量(簡稱位移量),[段標識符: 段內位移量]。
段標識符是由一個16位長的欄位組成,稱為段選擇符,由處理器提供段寄存器來存放段標識符,段寄存器有6種:
(1)cs 程式碼片段寄存器,指向包含程式指令的段;
(2)ss 棧寄存器,指向包含當前程式的段;
(3)ds 資料區段寄存器,指向包含待用資料或者全域資料段;
(4)其他三個寄存器es, fs, gs稱為附加段寄存器,作一般用途,可以指向任意的資料區段。
位移量指明了從段開始的地方到實際地址之間的距離,位移量為32位。
如上圖為段標識符(段選擇符)格式,其中最關鍵的部分為索引號。
段標識符,按照其字面意思便可理解它的作用是用來標識一個段的,而段是如何表示的呢。
這就引申到了另外一個概念:段描述符,每個段由一個8位元組的段描述符表示,它描述了段的特徵(段描述符就是段)。段標識符通過索引號(13位)就可以找到其對應的段(段描述符),段的格式如下圖所示:
段描述符中我們需要關注的欄位為:Base,它描述了一個段的開始位置的線性地址。
段描述符放在通用描述元表(GDT,存放於gdtr寄存器中)或局部描述符表(LDT,存放於ldtr寄存器中)中,通常只定義一個GDT,而每個進程除了存放在GDT中的段之外如果還學要建立附加的段,就可以有自己的LDT。。
好了,通過上述的講解,我們就可以關注一個邏輯地址是怎樣轉化成相應的線性地址的,具體步驟如下:
(1)先檢查邏輯地址的段選擇符的TI欄位,以決定段描述符儲存在哪一個描述符表中。(TI=0表明在GDT中,TI=1表明存在LDT中)
(2)根據段選擇符的索引號,計算尋找段描述符的地址,方法:索引號*8 + gdtr或者ldtr寄存器中的內容 = Base。
(3)把邏輯地址的位移量與步驟(2)中得到的Base欄位值相加就可以得到其對應的線性地址。
邏輯地址的轉換圖如下圖所示:
3、CPU頁式記憶體管理:線性地址轉換為物理地址
首先,我們得知道一些線性地址相關的東西。
(1)線性地址被分成固定長度為單位的組,稱為頁。頁內部連續的線性地址被映射到連續的物理地址中。
(2)分頁段元把所有的物理地址分成固定長度的頁框,稱為物理頁。
(3)線性地址映射到物理地址的資料結構稱為頁表。
(4)32位的線性地址,被分成3個域:目錄(Directory)高10位,頁表(Table)中間10位,位移量(Offset)低12位,由位移量的12bit可知,每頁含有4096位元組的資料。
線性地址的轉換分兩步完成,每一步都基於一種都基於一種轉換表,第一種轉換表稱為頁目錄錶轉換,第二種轉換稱為頁錶轉換。使用這種二級模式的目的在於減少每個進程頁表所需的RAM的數量。就像我們看書有個書目錄一樣,方便快捷。具體轉換如下圖所示:
轉換步驟:
(1)從cr3中取出進程的頁目錄位址(作業系統負責在調度進程的時候,把這個地址裝入對應寄存器);
(2)根據線性地址前十位,在數組中,找到對應的索引項目,因為引入了二級管理員模式,頁目錄中的項,不再是頁的地址,而是一個頁表的地址。(又引入了一個數組),頁的地址被放到頁表中去了。
(3)根據線性地址的中間十位,在頁表(也是數組)中找到頁的起始地址;
(4)將頁的起始地址與線性地址中最後12位相加,得到最終我們想要的其對應的物理地址。
題外:
在記憶體管理中,內部片段是已經被分配出去的的記憶體空間大於請求所需的記憶體空間。
外部片段是指還沒有分配出去,但是由於大小太小而無法分配給申請空間的新進程的記憶體空間空閑塊。
固定分區存在內部片段,可變式分區分配會存在外部片段;
頁式虛擬儲存系統存在內部片段;段式虛擬儲存系統,存在外部片段