Labs 導讀
標準服務器技術是網絡功能虛擬化(NFV)實現的一個關鍵因素,了解一些x86架構的基礎知識對大家后續(xù)了解電信云關鍵技術,尤其是掌握虛擬化技術原理和關鍵優(yōu)化方案是必須具備的。本文主要從x86架構的CPU指令集增強,內存管理、中斷和異常、IO架構等部分進行闡述,以及包含一些基礎IT的基本概念的講解。
1 x86-64指令集的增強
Intel的x86體系結構是世界上最流行的處理器架構,從1978年8086/8088處理器問世到現在的Core i7和Core i9,以及Xeon系列處理器,Intel x86體系結構已經在CPU領域叱咤40多年。
x86-64是x86架構的延伸產品,是一種64位微處理器架構及其相應的指令集。在x86-64出現以前,Intel與惠普聯合推出IA-64架構,此架構不與x86兼容,且市場反應冷淡。于是,與x86兼容的x86-64架構應運而生。1999年,AMD首次公開64位指令集為IA-32提供擴展,稱為x86-64(后來改名為AMD64)。此架構后來也為Intel所采用,也就是現在的Intel 64。
x86-64能有效地把x86架構移植到64位環(huán)境,并且兼容原有的x86應用程序,市場前景廣闊。外界使用x84-64或者x64稱呼這個64位架構,以保持中立,不偏袒任何一家廠商。
AMD 64指令集主要特點有:支持64位通用寄存器、64位整數及邏輯運算和64位虛擬地址。
Intel 64架構加入了額外的寄存器和其他改良的指令集,可使處理器直接訪問超過4GB的內存,允許運行更大的應用程序。通過64位的存儲器地址上限,其理論存儲器容量上限達16EB,目前大多數操作系統(tǒng)和應用程序已基本支持完整的64位地址。
2 x86的內存架構
硬件架構中最復雜、最核心的部分就是其內存架構。此部分詳細內容因為篇幅有限無法詳細展開,面向的人員主要包括CPU架構設計、操作系統(tǒng)開發(fā)和內核底層優(yōu)化等領域,至于運維方面后期如果不做內核優(yōu)化的同事了解即可,如感興趣可參考《手把手教你設計CPU-RISC-V處理器》、《嵌入式操作系統(tǒng)原理》和《處理器虛擬化技術》等書籍。
2.1 地址空間
地址空間是所有可用資源的集合,我們姑且將它看做一個大大的數組,那么地址就是這個數組的索引。地址空間可以劃分為物理地址空間和線性地址空間兩大類。
2.1.1 物理地址空間
硬件平臺通常劃分為CPU、內存和其他硬件設備三個部分。其中,CPU 是整個硬件平臺的主導者,內存和其他硬件設備都是CPU 可以使用的資源。這些資源組合在一起,分布在CPU的物理地址空間內,CPU使用物理地址索引這些資源。物理地址空間的大小由CPU實現的物理地址位數所決定,物理地址位數由CPU經過MMU(Memory Management Unit,內存管理單元)轉換后的外地址總線位數決定。外地址總線位數與CPU處理數據的能力(即CPU位數)沒有必然的聯系,例如:16位的8086 CPU具有20位地址空間。
一個硬件平臺只有一個物理地址空間,但每個程序都認為自己獨享整個平臺的硬件資源。為了讓多個程序能夠有效地相互隔離,也為了它們能夠有效地使用物理地址空間的資源,引入了線性地址空間的概念。
2.1.2 線性地址空間
線性地址空間的大小由CPU實現的線性地址位數決定,線性地址位數由CPU的內地址總線位數決定。由于CPU的內地址總線與CPU的執(zhí)行單元直連,所以,內地址總線位數往往與CPU位數一致,如果是32位處理器,那么它就實現了32位線性地址,其線性地址空間為4GB,如果是64位處理器,那么它的線性地址空間的為2的64次方,即16384GB。需要注意的是,線性地址空間的大小與物理地址空間的大小沒有必然聯系,Intel的PAE平臺具有4GB的線性地址空間,而其物理地址空間為64GB。但是,線性地址空間會被映射到某一部分物理地址空間或整個物理地址空間。也就是說,線性地址空間小于等于物理地址空間。
CPU負責將線性地址空間轉換成物理地址空間,保證程序能夠正確訪問到該線性地址空間所映射到的物理地址空間。在現代操作系統(tǒng)中,每個進程通常都擁有自己的私有線性地址空間。一個典型的線性地址空間構造如下圖所示。
線性地址空間
2.2 地址
地址是訪問地址空間的索引。根據訪問地址空間的不同,索引可以分為物理地址和線性地址。但由于x86特殊的段機制,還存在一種額外的地址——邏輯地址。
2.2.1 邏輯地址
邏輯地址是程序直接使用的地址(x86無法禁用段機制,邏輯地址一直存在)。邏輯地址由一個16位的段選擇符和一個32位的偏移量(32位平臺)構成。下面以具體程序為例進行解釋。比如:我們寫下面一段c語言代碼:
int a = 100; # 定義一個整型變量
a int *p = &a; # 定義一個整型指針p,指向變量a在內存中的地址
上述語句中的指針變量p存儲的就是變量a的邏輯地址。實際上,p中存儲的僅是邏輯地址的偏移部分,而偏移對應的段選擇符位于段寄存器中,并沒有在程序中顯示。
2.2.2 線性地址
線性地址又稱虛擬地址。線性地址是邏輯地址轉換后的結果,用于索引線性地址空間。當CPU使用分頁機制時,還需要將線性地址轉換成物理地址才能訪問物理平臺內存或其他硬件設備;當分頁機制未啟用時,線性地址與物理地址相同。
2.2.3 物理地址
物理地址是物理地址空間的索引,是CPU提交到總線用于訪問物理平臺內存或其他硬件設備的最終地址,在x86下,物理地址有時也被稱為總線地址。
根據上面的描述,我們可以總結如下:
分段機制啟用,分頁機制未啟用:邏輯地址--->線性地址=物理地址
分段機制、分頁機制同時啟用:邏輯地址--->線性地址--->物理地址
3 x86內存管理機制
x86架構的內存管理機制分為兩部分:分段機制和分頁機制。分段機制為程序提供彼此隔離的代碼區(qū)域、數據區(qū)域、棧區(qū)域,從而避免了同一個處理器上運行的多個程序互相影響。
分頁機制實現了傳統(tǒng)的按需分頁、虛擬內存機制,可以將程序的執(zhí)行環(huán)境按需映射到物理內存。此外,分頁機制還可以用于提供多任務的隔離。
分段機制和分頁機制都可以通過配置,支持簡單的單任務系統(tǒng)、多任務系統(tǒng)或共享內存的多處理器系統(tǒng)。需要強調的一點是,處理器無論在何種運行模式下都不可以禁止分段機制,但是分頁機制卻是可選選項。
3.1 分段機制
分段機制是x86架構下的樸素內存管理機制,不可以禁用。了解分段機制有利于對后續(xù)內存虛擬化原理和優(yōu)化方案有更深的了解。
分段機制將內存劃分成以基地址(Base)和長度(Limit)描述的塊。段可以與程序最基本的元素聯系起來,程序可以簡單地劃分為代碼、數據和棧,段機制就有相應的代碼段、數據段和棧段。
一個程序根據分段機制在內存中由邏輯地址、段選擇符、段描述符和段描述符表4個基本部分構成。
1)當程序使用邏輯地址訪問內存的某個部分時,CPU通過邏輯地址中的段選擇符索引段描述符表,進而得到該內存對應的段描述符(段描述符描述段的基地址、長度以及讀/寫、訪問權限等屬性信息)
2)根據段描述符中的段屬性信息檢測程序的訪問是否合法,如果合法,再根據段描述符中的基地址將邏輯地址轉換為線性地址。
這個流程可以用如下圖示進行總結:
內存分段機制
段選擇符是邏輯地址的一個組成部分,用于索引段描述符表以獲得該段對應的段描述符。段選擇符作為邏輯地址的一部分,對應用程序是可見的。但是,正如前面在邏輯地址中介紹的,應用程序中只存儲和使用邏輯地址的偏移部分,段選擇符的修改和分配由連接器和加載器完成。
為了使CPU能夠快速地獲得段選擇符,x86架構提供了6個段寄存器存放當前程序中各個段的段選擇符。這6個段寄存器分別如下:
CS(Code-Segment,代碼段):存放代碼段的段選擇符。
DS(Data-Segment,數據段):存放數據段的段選擇符。
SS(Stack-Segment,棧段):存放棧的段選擇符。
ES、FS、GS:可以存放額外三個數據段的段選擇符,由程序自由使用。
由于段選擇符的存在最終是為了索引段描述符表中的段描述符,為了加速段描述符的訪問,x86架構在不同的段寄存器后增加了一個程序不可見的段描述符寄存器。當相應段寄存器中加入一個新的段選擇符后,CPU自動將該段選擇符索引的段描述符加載到這個不可見的段描述符寄存器中。各個段寄存器的構造如下:
段寄存器
段描述符描述某個段的基地址、長度以及各種屬性(例如,讀/寫屬性、訪問權限等)。這是分段機制的核心思想。當CPU通過一個邏輯地址的段選擇符獲得該段對應的段描述符后,會使用段描述符中各種屬性字段對訪問進行檢查,一旦確認訪問合法,CPU將段描述符中的基地址和程序中邏輯地址的偏移量相加就得到程序的線性地址。
正如前面講到的,x86架構在每個段寄存器后增加了一個程序不可見的段描述符寄存器,每當段寄存器加入一個新的段選擇符后,CPU自動將該段選擇符索引的段描述符加載到這個段描述符寄存器中。后續(xù)只要不發(fā)生段寄存器的更新操作,CPU就不再查詢段描述符表而是直接使用這個段描述符寄存器中的值,從而加快CPU的執(zhí)行效率。
x86架構提供了兩種段描述符表:GDT(全局段描述符表Global Descriptor Table)和LDT(本地段描述符表Local Descriptor Table)。具體選擇哪個段描述符表,由段選擇符中的TI字段決定,當TI=0時,索引GDT,當TI=1時索引LDT。系統(tǒng)中至少有一個GDT可以被所有的進程訪問。與此同時,系統(tǒng)中可以有一個或多個LDT,可以被某個進程私有,也可以被多個進程共享。
GDT是內存中的一個數據結構。簡單地講,可以將GDT看成是一個數組,由基地址(Base)和長度(Limit)描述。
LDT是一個段,需要用一個段描述符來描述。LDT的段描述符存放在GDT中,當系統(tǒng)中有多個LDT時,GDT中必須有對應數量的段描述符。
為了加速對GDT和LDT的訪問,x86架構提供了GDTR寄存器和LDTR寄存器。關于這兩種寄存器的具體描述如下:
GDTR:包括一個32位的基地址(Base)和一個16 位的長度(Limit)。
LDTR:結構與段寄存器相同(同樣包含對程序不可見的段描述符寄存器)。
通過段選擇符索引GDT/LDT的過程如下圖所示:
段選擇符索引
➢ x86架構內存管理中分段機制總結:
1)在程序加載階段,該進程LDT的段選擇符首先索引GDT,獲得LDT的段描述符并將其加載到LDTR寄存器中。此外,該進程的CS、DS、SS中加入相應的段選擇符,CPU根據段選擇符的TI字段索引相應的段描述符表,獲得相應的段描述符,并加載入CS、DS、SS對應的程序不可見的段描述符寄存器。
2)程序執(zhí)行到讀/寫內存中的數據時,把程序中相應變量的邏輯地址轉換為線性地址:
進行必要的屬性、訪問權限檢查;
從DS對應的段描述符寄存器獲得該段的基地址;
將變量的32位偏移量和段描述符中的基地址相加,獲得該變量的線性地址。
3.2 分頁機制
分段機制的目的是將內存中的線性地址空間劃分成以基地址和長度描述的多個段進行管理,程序對應的邏輯地址以基地址和偏移量來描述,實現邏輯地址到線性地址空間的映射。而分頁機制是使用單位“頁”來管理線性地址空間和物理地址空間的映射關系。同時,分頁機制允許一個頁面存放在物理內存中或磁盤的交換區(qū)域(如Linux下的Swap分區(qū),Windows下的虛擬內存文件)中,程序可以使用比機器物理內存更大的內存區(qū)域,從而實現現代操作系統(tǒng)中虛擬內存機制。(注意:操作系統(tǒng)的虛擬內存原理和映射關系和后面要講的計算虛擬化技術中內存虛擬化技術基本一致,只是VMM實現時又多了一層嵌套)。
在x86架構下,頁的典型大小為4KB,于是一個4GB的線性地址空間被劃分成1024×1024個頁面,參見本文線性地址空間示意圖。物理地址空間的劃分與此類似。x86架構允許大于4KB的頁面大小(如2MB、4MB、1GB)等。
分頁機制的核心思想是通過頁表將線性地址轉換為物理地址,并配合旁路轉換緩沖區(qū)(Translation Lookaside Buffer,后面簡稱為TLB)來加速地址轉換的過程。分頁機制主要由頁表、CR3寄存器和TLB三個部件構成,如下圖所示。
內存分頁機制
頁表是用于將線性地址轉換成物理地址的主要數據結構。一個地址對齊到頁邊界后的值稱為頁幀號(或者頁框架),它實際上就是該地址所在頁面的基地址。比如:一個頁大小為4kB,那么第一個頁幀號就是0,第二個頁幀號就是4097,依次類推。線性地址對應的頁幀號叫做虛擬頁幀號(Virtual Frame Number,下面簡稱為VFN),物理地址對應的頁幀號叫做物理頁幀號(Physical Frame Number,下面簡稱為PFN)或機器頁幀號。頁表實際上是存儲VFN到PFN映射的數據結構。
在傳統(tǒng)的32位的保護模式中(未啟用物理地址擴展PAE功能),x86處理器使用兩級轉換方案,在這種方案中,CR3寄存器指向一個4KB大小的頁目錄表,頁目錄中共有1024個記錄,每一項記錄大小4B空間,都指向一個4KB大小的頁表,頁表中也有1024項,每項大小4B空間,所以,最后整個線性地址空間大小就是1024 個長為4KB的頁,即總共4GB大小的空間。未啟用PAE 的4KB大小的頁面如下圖所示。
頁表結構
頁目錄項(Page Directory Entry,下面簡稱為PDE),包含頁表的物理地地址,PDE存放在頁目錄表中。
頁表項(Page Table Entry,下面簡稱為PTE):包含該線性地址對應的物理頁幀號PFN,PTE存在頁表中,確定物理頁幀號PFN 后,再將線性地址的0~11位偏移量與其相加,就可以確定該線性地址對應的物理地址。
虛擬內存實現的關鍵在于PDE和PTE都包含一個P(Present)字段:
當P=1時,物理頁面存在于物理內存中,CPU完成地址轉換后可以直接訪問該頁面。
當P=0時,物理頁面不在物理內存中(在硬盤的交換分區(qū)中),當CPU訪問該頁面時,會產生一個缺頁錯誤中斷,由操作系統(tǒng)的缺頁處理機制將存放在硬盤上的頁面調入物理內存,使訪問可以繼續(xù)。同時,由于程序的局部性特點,操作系統(tǒng)會將該頁面附近的頁面一起調入物理內存,方便CPU的訪問。所以,為了減少內存占用,要求程序開發(fā)人員盡量少的使用全局索引或遞歸調用等機制。
P=0時的PDE和PTE的1~31位都將為操作系統(tǒng)提供物理頁面在硬盤上的信息,這些位存儲著物理頁面在硬盤上的位置。
啟用物理地址擴展(之后簡稱為PAE)后,頁表結構將發(fā)生相應的變化。頁表和頁目錄的總大小仍是4KB,但頁表和頁目錄中的表項都從32位擴為64位,以使用附加的地址位。這樣,頁表和頁目錄都只有512個表項,變成了原來方案的一半,所以又加入了一個級:CR3指向頁目錄指針表,即一個包含4個頁目錄指針的表。啟用PAE 的4KB大小的頁面使用的三級頁表如下圖所示:
三級頁表結構
CR3寄存器也稱為頁目錄基地址寄存器(Page-Directory Base Register,PDBR),存放著頁目錄的物理地址。一個進程在運行前,必須將其頁目錄的基地址存入CR3,而且,頁目錄的基地址必須對齊到4KB頁邊界。啟用PAE時,CR3指向頁目錄指針表,每一項都指向一個頁目錄表,共有4個頁目錄表。
為了提高地址轉換的效率,x86架構使用TLB對最近用到的頁面映射進行緩存。TLB中存放著VFN到PFN的轉換記錄,當CPU訪問某個線性地址時,如果其所在頁面的映射存在于TLB中,無須查找頁表,即可得到該線性地址對應的PFN,CPU 再將它與線性地址的偏移相加,就能得到最后的物理地址。
➢ x86架構內存管理中心分頁機制總結:
1)CPU訪問一個線性地址,在TLB中進行匹配,如果地址轉換在TLB中,則跳到步驟6。否則,發(fā)生了一次TLB Miss(TLB 缺失),繼續(xù)步驟2。
2)查找頁表,如果頁面在物理內存中,則跳到步驟4。
3)如果頁面不在物理內存中,則產生缺頁錯誤,由操作系統(tǒng)的缺頁錯誤處理程序進行以下處理。
將頁面從磁盤復制到物理內存中。
更改對應的PTE,設置P 位為1,并對其他字段進行相應的設置。
刷新TLB 中對應的PTE。
從缺頁錯誤處理程序中返回。
4)此時,頁面已經存在于物理內存中,并且頁表也已經包含了這個映射。重新在TLB中進行匹配,如果地址轉換在TLB中,則跳到步驟6。否則,發(fā)生了一次TLB Miss(TLB 缺失),繼續(xù)步驟5。
5)CPU重新查頁表,把對應的映射插入到TLB中。
6)此時,TLB已經包含了該線性地址對應的PFN。將PFN和線性地址中的偏移量相加,就得到了對應的物理地址。
未完待續(xù)......