來自:今日頭條,作者:大話IT 連結:https://www.toutiao.com/a6952760982372385317/
程式經過編譯後,變成了可執行的檔案,可執行檔案主要包括代碼和資料兩部分,代碼是隻讀的,資料則是可讀可寫的。
可執行檔案由作業系統加載到記憶體中,交由CPU去執行,現在問題來了,CPU怎麼去通路代碼和資料?,通路的方式經曆過四個階段:
1.直接通路
2.段基址+段偏移位址
3.段選擇子+段偏移位址
4.虛拟位址
現代作業系統采用的是虛拟位址,這也是本篇文章闡述的重點,但虛拟位址是由1~3階段發展而來的,是以也有必要闡述1~3三種通路方式。
直接通路很好了解,程式經過編譯後,生成了可執行檔案,編譯器給每行資料和代碼配置設定了一個唯一的位址,如下圖

可執行檔案
如上圖可執行檔案中1000~1024之間的位址,加載到記憶體後,記憶體的位址也是1000~1024,在可執行檔案中配置設定的唯一位址就是記憶體中的實體位址,這就叫直接通路,直接定通路幹脆直接,沒有那些彎彎繞。
當時問題也不少,例如同一個可執行檔案不能同時執行,它們的實體位址一樣,沖突了,必須一個接一個,還有就是可執行檔案的實體位址已經固定了,如果想在其它實體位址運作,必須地重新編譯,生成新的實體位址。
可見直接定位是計算機發展早期的産物,早期沒有那麼多的程式要運作,程式都是一個接一個地去執行的,是以早期這種定位比較簡單,直接高效。
随着多任務需求的來臨,現在記憶體中要并發運作多個程式,雖然采用直接定位把每個不同的程式放在不同的記憶體段中,勉強可以滿足,但是一個可執行檔案不能同時運作多個,另外程式必須在固定的實體位址運作,靈活性大大減弱,排程起來也是非常麻煩,是以CPU設計師和作業系統開發人員發明了段基址+段偏移位址。
Inter 8086處理器就是采用這種定位方式,我們知道可執行檔案主要分為資料段和記憶體段,如下圖
由上圖紅色部分可知,0,4,8就是相對于資料段的偏移位址,0,4,8,12是相對于代碼段的偏移位址。
在可執行檔案中,一個段的偏移位址是固定的,無論可執行檔案加載到記憶體的什麼位置,這個偏移位址是固定的。
當可執行檔案加載到記憶體時,先在記憶體中配置設定一個資料段和代碼段,這兩個段理論上可以不挨着,一般情況下,代碼段和資料段是挨着的,代碼段和資料段在記憶體中都有一個起始位址,這個起始位址就叫做段基址,這個段基址是放在段寄存器裡,例如代碼段基址放在CS寄存器,資料段基址放在DS寄存器,當然還有其他的段例如棧段,棧段剛開始大小為0,随着程式的運作入棧,出棧,這個棧段在不斷擴充,當然,咋們主要說的是資料段和代碼段,棧段隻是簡單帶過。
假設可執行檔案被加載到了記憶體中,如下圖
如上圖所示,代碼段被布局到以0x00600000為起始位址的記憶體中,資料段被布局到以0x00601000為起始位址的記憶體中。
當CPU開始執行代碼段的第一條指令時,會将代碼段的起始位址放入到段寄存器中,此時CS代碼段寄存器中存儲的就是0x00600000,然後開始從起始位址處開始執行第一條代碼指令,此時把代碼指令的偏移位址放入到IP寄存器中,IP寄存器存儲的就是0,是以CPU要定位一條代碼指令時通過CS:IP的方式定位的,如下圖所示
定位指令
當CPU執行到0x00600000處的代碼指令時,該指令為MOV AX,[0],該指令的意思是把位址0處的資料存儲到AX寄存器,這個0就是資料段的偏移位址,此時CPU會将資料段的起始位址加入到DS段寄存器中,然後将資料段寄存器的值+偏移位址即0x00601000+0=0x00601000定位到了資料123,然後将123存儲到AX寄存器中。
定位資料
上述過程就是【段基址+段偏移位址】的定位方式,之是以把起始位址加入到寄存器中,也是為了後續再執行指令或者擷取資料時,可以直接從寄存器擷取,加快CPU執行的速度。
【段選擇子+段偏移位址】與【段基址+段偏移位址】有些相似之處,之是以采用【段選擇子+段偏移位址】主要是為了安全,原來的【段基址+段偏移位址】方式,程式員可以直接跳轉到其他代碼段和資料段,沒有任何限制,安全性全依賴于程式員的職業操守和水準,是以CPU設計者就發明了【段選擇子+段偏移位址】。
【段選擇子+段偏移位址】中的段選擇子可以認為是一個索引,這個索引指向了全局段描述符表中的一項,全局段描述表存儲在記憶體中,它的起始位址存儲在全局段描述符寄存器中。
全局段描述符表有很多個段描述符,每個段描述占用8個位元組,這個段描述符裡面就包括了段基址,另外還有一些安全性相關的描述資訊例如段的可讀,可寫,可執行,段的大小等。
段選擇子存儲在了段寄存器中,總共16位,其中高13位就是全局段描述表的索引。
當CPU開始執行代碼段的第一條指令時,會将代碼段的選擇子放入到CS段寄存器中,然後CPU從段寄存器中的擷取段選擇子,然後截取選擇子的高13位擷取索引,然後根據全局描述符表寄存器的位址找到全局描述符表的起始位址,根據起始位址+索引*8找到段描述符,然後根據段描述符擷取段的基址,段的基址加上ip寄存器中的偏移位址就是指令的實體位址,如下圖所示1~6步驟所示
當CPU執行到0x00600000處的代碼指令時,該指令為MOV AX,[0],該指令的意思是把位址0處的資料存儲到AX寄存器,這個0就是資料段的偏移位址,此時CPU會将資料段的選擇子加入到DS段寄存器中,然後CPU擷取段選擇的高13位擷取索引,然後根據全局描述符表寄存器的位址找到全局描述符表的起始位址,根據起始位址+索引*8找到段描述符,然後根據段描述符擷取段的基址,段的基址加上資料段的偏移位址就是資料的實體位址,如下圖1~6步驟所示
上述過程就是【段選擇子+段偏移位址】的定位方式。
現代的作業系統和CPU未打開分頁時采用的是【段選擇子+段偏移位址】通路代碼和資料,而一旦打開分頁時,經過【段選擇子+段偏移位址】得到的位址不再是實體位址了,而是叫做虛拟位址,預設則是打開分頁的。
現代的作業系統和CPU采用的平坦模型,平坦模型就是整個記憶體就一個段,是以段基址就是0,段偏移位址就等于虛拟位址了。
下面将從以下幾個方面來闡述虛拟位址相關的話題。
1.什麼是虛拟位址,實體位址,虛拟位址空間,實體位址空間,虛拟記憶體,實體記憶體?
2.什麼是程序虛拟位址空間?
3.什麼是虛拟頁,實體頁?
4.什麼是頁表?
5.虛拟位址怎麼樣通路實體記憶體?
虛拟位址空間是虛拟位址的集合,假設虛拟位址空間是N位的,它的位址範圍為{0~2的N次方-1}即它有2的N次方個虛拟位址,例如16位的虛拟位址空間,它的位址範圍為{0~65535},這意味着16位的虛拟位址空間有65536個虛拟位址。
實體位址空間是實體位址的集合,假設實體位址空間有M個位元組,它的位址範圍為{0~M-1},M不一定是2的多少次幂,例如M=100,表示實體位址空間大小為100個位元組,它的位址範圍為{0~99},通常情況下實體位址空間是2的幂次方,例如65536,這也是為了計算機友善處理而已,并不是強制要求的。
實體記憶體可以認為是一個的實體位元組數組,每個實體位址指向這個實體位元組數組中的一項。
虛拟記憶體也一樣,它也可以認為是一個實體位元組數組,不過這個位元組數組是存儲在磁盤上。
實體位址空間是實體記憶體的範圍,虛拟位址空間是虛拟記憶體的範圍,實體位址空間中的每個實體位址都是實打實地指向了具體的存儲單元,虛拟位址空間中每個虛拟位址指向哪裡有3種情況:
a.未配置設定,這個虛拟位址僅僅是個數字而已,沒有任何指向。
b.未緩沖,這個虛拟位址指向了磁盤的某個位元組存儲單元,裡面存儲了指令或者資料。
c.已緩沖,這個虛拟位址指向了實體記憶體的某個位元組存儲單元,裡面存儲了指令或者資料。
作業系統加載可執行檔案後,建立了一個程序,這個程序就有了自己的虛拟位址空間,每個程序的虛拟位址空間都一樣,如下圖所示
程序虛拟位址空間
如上圖所示,程序的虛拟位址空間被統一劃分成了多個固定區域,例如代碼區,資料區,堆區,共享區,棧區,核心區。
代碼區和資料區域:來自于可執行檔案,代碼區和資料區挨着,代碼區總是在0x0040000位址以上,0x0040000位址以下另有它用。
運作時堆區域:它初始化大小為0,随着動态配置設定記憶體(malloc),運作時堆不斷往高位址方向擴充,有個指針brk指向了堆的最高位址。
共享庫的記憶體映射區域:這個區域是一些标準的系統庫,這個共享庫在實體記憶體中隻存儲一份,每個程序将這個區域的虛拟位址映射到同一份共享庫實體記憶體上。
使用者棧區域:這個區域緊挨着核心區域,處于高位址處,随着使用者棧的出棧,入棧,動态擴充,入棧向低位址方向擴充,出棧則向高位址方向收縮,棧頂指針存儲在棧寄存器(ESP)中。
核心區域:這個區域是作業系統自己代碼,資料,棧空間,核心在實體記憶體中隻存儲一份,每個程序将這個區域的虛拟位址映射到同一份核心實體記憶體上。
核心和共享庫的映射
現代操作操作和CPU将實體記憶體按照固定的頁大小分成很多份,每一份叫做實體頁(PP),每一份有一個編号叫做實體頁号(PPN),這個實體頁大小通常是4KB,例如一個實體記憶體大小為20KB,這個實體記憶體可以分成5個實體頁,那麼實體頁号(PPN)就是0,1,2,3,4。
虛拟記憶體也一樣,它的頁大小與實體記憶體的頁大小相同,虛拟記憶體也被分成了很多份,每一份叫做虛拟頁(VP),每一份的編号叫做虛拟頁号(VPN),例如假設虛拟頁大小為4KB,一個虛拟記憶體大小為10KB,這個虛拟記憶體可以分成2個虛拟頁(VP),虛拟頁号(VPN)就是0,1
每個實體頁存儲在實體記憶體上,每個虛拟頁存儲在磁盤上,如下圖所示
虛拟記憶體和實體記憶體
上圖的虛拟記憶體有8個虛拟頁,實體記憶體有6個實體記憶體頁,虛拟頁存儲在磁盤上,實體頁則存儲在DRARM上。
每個虛拟頁可以有三種狀态,未配置設定,已緩沖,未緩沖
未配置設定:虛拟頁還沒有配置設定磁盤空間
已緩沖:虛拟頁緩沖或者映射在了實體頁上。
未緩沖:虛拟頁配置設定了磁盤空間,但沒有在實體頁上緩沖。
通常作業系統加載可執行檔案後,建立了一個程序,這個程序就有了虛拟位址空間,這并不意味着可執行檔案已經從磁盤加載到記憶體中了,作業系統隻是為了程序虛拟位址空間的每個區域配置設定了虛拟頁。
代碼和資料區域的虛拟頁被配置設定到了可執行檔案的适當位置,此時虛拟頁狀态為未緩沖,虛拟頁指向了磁盤位址。
作業系統和共享庫的虛拟頁被映射到了實體記憶體,因為作業系統和共享庫已經在實體記憶體了,這些虛拟頁的狀态為已緩沖。
使用者棧,運作時堆的虛拟頁沒有任何配置設定,不占用任何空間,這些虛拟頁的狀态為未配置設定。
那麼程序虛拟位址空間的代碼和資料,使用者棧,運作時堆的實體記憶體什麼時候配置設定呢,答案就是處理器用虛拟位址執行代碼,讀取資料時,這個下一章闡述。
先普及幾個概念:
VPO即虛拟頁偏移量:
虛拟位址由虛拟頁号+虛拟頁偏移量組成,虛拟頁偏移量是相對某個虛拟頁的偏移量。
PPO即實體頁偏移量:
實體位址由實體頁号+實體頁偏移量組成,實體頁偏移量是相對某個實體頁的偏移量,
VPO等于PPO
頁表(Page Table)PT:
頁表是建立虛拟頁号和實體頁号映射關系的表結構,每個頁表項(PTE)包括了有效位,實體頁号,磁盤位址等資訊,如下圖:
頁表與實體記憶體,虛拟記憶體的關系
由上圖可以得知,作業系統可以根據頁表項的有效位和位址資訊判斷出虛拟頁目前所處的狀态即未配置設定,已緩沖,未緩沖
如上圖所示,頁表有8個頁表項,每個頁表項裡包含一個有效位和位址資訊。
當頁表項 PTE n的頁表項有效位為0時,表示虛拟頁 n沒有緩沖在實體記憶體,可能在磁盤或者未配置設定,例如PTE 0頁表項裡存儲的是null,表明虛拟頁0即VP0是未配置設定狀态,PTE 3裡存儲的是磁盤位址,表明虛拟頁 3即VP3在磁盤裡,但沒有緩沖,VP3狀态為未緩沖。
當頁表項PTE n的頁表項的有效位為1時,表示虛拟頁n緩沖在實體記憶體,PTE n存儲了實體頁号,虛拟頁 n的狀态為已緩沖,例如PTE 1的頁表項,有效位為1,則虛拟頁VP1緩沖在了實體頁中。
頁表基址寄存器(PTBR):
每個程序都有自己的頁表,CPU執行某個程序時,會先把該程序的一級頁表起始位址存儲到頁表基址寄存器,這樣CPU查找一級頁表起始位址可以直接從寄存器查找,加快了查找效率。
好了,概念介紹到這裡,先來看看虛拟位址翻譯實體位址的過程,按照一級頁表來示範,如下圖所示:
虛拟位址翻譯實體位址
上圖為虛拟位址翻譯實體位址的示意圖,可以看出VPO等于PPO。
下面看看計算機各個部件是怎麼通過虛拟位址通路實體記憶體的。
處理器根據虛拟位址通路實體記憶體的分為頁表項命中和頁表項未命中兩種況,頁表項命中意味着頁表項的有效位為1,頁表項存儲的是實體頁号,虛拟頁緩沖在實體頁中,未命中意味着頁表項有效位為0,此時需要發送缺頁中斷。
頁表項命中的步驟如下圖:
頁表項命中翻譯步驟
1、CPU将虛拟位址(VA)送入MMU,MMU根據頁表基址寄存器中頁表的起始位址加上虛拟頁号,找到了頁表項的實體位址PTEA。
2、MMU将PTEA送入到高速緩沖或者記憶體。
3.從高速緩沖或者記憶體中找到頁表項(PTE),傳回頁表項(PTE)給MMU。
4.MMU根據PTE找出實體頁号,然後加上虛拟頁偏移量形成實體位址(PA),送入到高速緩沖或者記憶體。
5.高速緩沖或者記憶體擷取資料,傳回資料給處理器。
頁表項未命中的步驟如下圖:
頁表項未命中翻譯步驟
1.CPU将虛拟位址(VA)送入MMU,MMU根據頁表基址寄存器中頁表的起始位址加上虛拟頁号,找到了頁表項的實體位址PTEA。
2.MMU将PTEA送入到高速緩沖或者記憶體。
4.MMU根據PTE,發現頁不在記憶體中,未命中,是以MMU發送一個缺頁中斷,交由缺頁異常處理程式處理。
5.缺頁異常處理程式根據頁置換算法,選擇出一個犧牲頁,如果這個頁面已經被修改了,則寫出到磁盤上,最後将這個犧牲頁的頁表項有效位設定為0,存入磁盤位址。
6.缺頁異常程式處理程式調入新的頁面,如果該虛拟頁尚未配置設定磁盤空間,則配置設定磁盤空間,然後磁盤空間的頁資料拷貝到空閑的實體頁上,并更新PTE的有效位為1,更新實體頁号,缺頁異常處理程式傳回後,再回到發生缺頁中斷的指令處,重新按照頁表項命中的步驟執行。
虛拟位址翻譯實體位址的過程介紹完了,另外要說的是現代的CPU和作業系統為了加快虛拟位址翻譯實體位址的過程,做了以下兩點優化:
1.建立了虛拟号(VPN)和頁表項(PTE)的映射關系,存儲在TLB中,當MMU根據虛拟位址擷取頁表項時,先查詢TLB,在TLB找到了頁表項後,就不需要從高速緩沖或者記憶體中擷取了,找不到了才會計算頁表項位址PTEA,然後再從高速緩沖或者記憶體中擷取頁表項(PTE)。
2.某些熱點實體位址對應的資料,存儲在L1緩沖中,MMU根據實體位址擷取頁表項或者代碼資料時,先從L1緩沖中擷取,找不到再從記憶體中擷取。
上述的翻譯過程是通過一級頁表來翻譯,現在作業系統支援多級頁表,多級頁表與一級頁表比較類似,如下圖所示:
K頁表
上圖為K級頁表,頁表基址寄存器存儲的是一級頁表的位址,1到K-1的頁表的每一項存儲的下一級頁表的起始位址,K級頁表的每一項存儲的是實體頁号或者磁盤位址。
好了,關于虛拟位址,虛拟記憶體,虛拟位址空間的話題就介紹到這裡了。