简单的分页模型
分頁機制是 80x86 內(nèi)存管理機制的第二部分。它在分段機制的基礎(chǔ)上完成虛擬(邏輯)地址到物理地址的轉(zhuǎn)換。為了理解分頁機制,本文介紹一個簡單的分頁模型,雖然簡單,但是對理解真正的分頁模型非常有幫助。咱們開始吧!
在單純的分段模式下,線性地址就是物理地址。
比如下面的匯編語句:
mov edx [0x1008]這是要把內(nèi)存中某個位置的值賦給 EDX,但究竟是內(nèi)存的哪個位置呢?這就要看數(shù)據(jù)段描述符了。
假設(shè)描述符中的段基地址為 0x0020_0000,界限值為 0x2007,段的粒度是字節(jié) ,那么該段的最大長度就是 0x2008(=8200)。
當(dāng)訪問內(nèi)存的時候,用段基地址加上段內(nèi)偏移 0x1008,形成線性地址 0x0020_1008,在沒有開啟分頁的情況下,這就是物理地址。
一旦采用分頁式內(nèi)存管理,就應(yīng)該把物理內(nèi)存分成大小相同的頁。注意,頁在物理內(nèi)存中的位置是有講究的,并不是在內(nèi)存中隨便找個位置,說:“來 ,頁從這里開始!”
事實上,頁的單位一般是 4KB,即 4096 (=0x1000)字節(jié)。而且,頁的起始地址和 4KB 對齊,所以,第 1 個頁的物理地址是 0x0000_0000,第 2 個頁的物理地址是 0x0000_1000,第 3 個頁的物理地址是0x0000_2000,最后一個頁的物理地址是 0xFFFFF000。
這樣,可以將 4GB 的物理內(nèi)存劃分為 1048576 (0x 100000) 個頁。很顯然,頁的物理地址,其低 12 位始終為全零。
問題在于,如何將長度不一的段 ,映射到大小相同的頁面上呢?
內(nèi)存的分配涉及段空間的分配和頁的分配。如下圖所示,左邊是虛擬的 4GB 的內(nèi)存空間,稱為虛擬內(nèi)存;右邊呢,是實實在在的物理內(nèi)存,如果大小是 4GB,就被分成 1048576 個 4KB 頁面;當(dāng)然,也許只有 2 GB,那就只能劃分成 524288 個 4KB 頁面。
當(dāng)一個程序加載時,操作系統(tǒng)既在要左邊的虛擬內(nèi)存屮分配段空間,又要在右邊的物理內(nèi)存中分配相應(yīng)的頁面。因此,第一個步驟是尋找空閑的段空間,該段空間既沒有被其他程序使用,也沒有被同一程序內(nèi)的其他段使用。假設(shè)己經(jīng)成功找到并分配了一個段空間,基地址為 0x00200000,長度為 8200 字節(jié)。
頁的最小尺寸是 4KB ,也就是 4096 字節(jié)。因此,8200 字節(jié)的段,需要占用 3 個頁面,其中最后一個頁面只用了 8 個字節(jié)。
作為一個具體的例子,操作系統(tǒng)為程序分配了一個段,段是在虛擬內(nèi)存中分配的,起始地址為 0x00200000。該段有 8200字節(jié),需要分配 3 個頁面。為此,操作系統(tǒng)在物理內(nèi)存中搜索可用的
空閑頁,還真找到了,這三個頁面的物理地址分別是:
0x0000_0000 起始的頁面,對應(yīng)虛擬內(nèi)存地址 0x0020_0000-0x0020_0FFF
0x0000_1000 起始的頁面,對應(yīng)虛擬內(nèi)存地址 0x0020_1000-0x0020_1FFF
0x0000_3000 起始的頁面,對應(yīng)虛擬內(nèi)存地址 0x0020_2000-0x0020_2007
這里只是示例,線性地址區(qū)間和頁的對應(yīng)關(guān)系可以隨意。
虛擬內(nèi)存空間不可能用來保存任何數(shù)據(jù),因為它是虛擬的。當(dāng)操作系統(tǒng)加載一個程序并創(chuàng)建為任務(wù)時,操作系統(tǒng)在虛擬內(nèi)存空間尋找空閑的段,并映射到空閑的頁。然后,到真正幵始加載程序時,再把原本屬于段的數(shù)據(jù)按頁的尺寸拆開,分別寫入對應(yīng)的頁中。
從段部件輸出的是線性地址,為了根據(jù)線性地址找到頁的物理地址,操作系統(tǒng)必須維護(hù)一張轉(zhuǎn)換表,把線性地址轉(zhuǎn)換成物理地址。
因為虛擬的 4GB 內(nèi)存可以分成 1048576 個頁,所以轉(zhuǎn)換表也有 1048576 項 。每個表項占 4 字節(jié),內(nèi)容為頁的物理地址。這個表格的用法是這樣的:因為頁的尺寸是 4K B , 故,線性地址的低 12 位可用于訪問頁內(nèi)偏移,高 20 位可用于指定一個物理頁。因此,把線性地址的高 20 位當(dāng)成索引,乘以 4 ,作為表內(nèi)偏移量,從表中取出一個雙字(4B),就是該線性地址所對應(yīng)的頁的物理地址。
例如,執(zhí)行指令
mov edx, [0x2002]
首先,要算出線性地址,用段地址 0x0020_0000 加上 0x2002,得到線性地址 0x0020_2002;然后把線性地址轉(zhuǎn)換為物理地址:線性地址的高 20 位是轉(zhuǎn)換表的索引,即 0x202(=514),即表格的第 514 項。如果按照字節(jié)索引,需要把 0x202 乘以 4,得到 0x808,看圖,從該單元可以取出一個雙字 0x0000_3000,這就是頁的起始物理地址。線性地址的低 12 位是頁內(nèi)偏移量,用頁物理地址加上頁內(nèi)偏移量,就是最終的物理地址。0x0000_3000 加上 0x002,得到 0x0000_3002,所以物理地址就是 0x0000_3002。
有人問,表格里面的 0x0000_3000 是怎么來的?
當(dāng)程序加載時,操作系統(tǒng)首先在虛擬內(nèi)存中分配段。然后,根據(jù)段需要分成多少頁,來搜索空閑頁面。當(dāng)段較大時,要按頁的尺寸分成好幾個地址區(qū)段,操作系統(tǒng)用每個區(qū)段的首地址,取高 20 位 ,乘以 4 ,作為偏移最訪問表格,并將分配給該區(qū)段的頁的物理地址寫入該表項。最后,把原本需要寫入每個區(qū)段的程序數(shù)據(jù),寫到對應(yīng)的頁中。
注意了,在頁式內(nèi)存管理中,頁面的管理和分配是獨立的,和分段以及段地址沒有關(guān)系。操作系統(tǒng)所要做的,就是尋找空閑頁面,把它分配給需要的段,并將頁的物理地址填寫到映射表內(nèi)。
一般來說,每個任務(wù)都可以擁有 4GB 的虛擬內(nèi)存空間;同時,每個任務(wù)都有自己的頁映射表,如圖 16-5 所示。
每個任務(wù)都有自己的 4GB 虛擬內(nèi)存空間,這個怎么理解呢?
考慮這樣一種情景 :任務(wù) A 有一個段 , 段基地址為 0x0005_0000,段長度為 3000 字節(jié),操作系統(tǒng)為它分配了一個物理地址為 0x0800_1000 的頁。過了一會兒,另一個任務(wù) B 加載了,它也有一個段,段基地址也為 0x0005_0000,段長度 4000 字節(jié),此時,操作系統(tǒng)則分配另一個不同的、物理地址為 0x0070_0000 的頁。
在這種情況下,任務(wù) A 訪問線性地址 0x0005_0006,訪問的其實是物理地址 0x0800_1006;在任務(wù) B 內(nèi)訪問同樣的線性地址 0x0005_0006,訪問的其實是物理地址 0x0070_0006。
你看,線性地址都是一樣的,但是被映射到了不同的物理地址上。
另一個會被質(zhì)疑的問題是,每個任務(wù)都有 4GB 虛擬內(nèi)存空間,而物理內(nèi)存只有一個,最大也才 4GB , 根本不夠分啊。事實上,的確不夠分。但是,操作系統(tǒng)可以將暫時不用的頁退避到磁盤,調(diào)入馬上就要使用的頁,通過這種手段來實現(xiàn)分頁內(nèi)存管理。
以上就是一個簡單的分頁模型,如果理解了,就會為后面學(xué)習(xí)兩級頁表結(jié)構(gòu)打下基礎(chǔ)。
參考資料
【1】《x86匯編語言:從實模式到保護(hù)模式》(李忠,電子工業(yè)出版社)
【2】《Linux內(nèi)核完全剖析》(趙炯,機械工業(yè)出版社,2006)
總結(jié)
- 上一篇: setup.s 总结——Linux-0.
- 下一篇: 将一个数组a的行和列的元素互换,存到另一