內存地址
x86架構包括三種地址:linux
- 邏輯地址
- 用於機器語言尋址,包括操做數的地址及指令地址等。邏輯地址包括短地址以及偏移地址。
- 線性地址(虛擬地址)
- 32位無符號整數,0x00000000~0xffffffff。最多尋址4GB。
- 0x00000000到0xbfffffff,用戶態內核態進程都可尋址,0xc0000000到0xffffffff,只有內核態進程纔可尋址
- 物理地址
- 用於在存儲芯片中尋址存儲單元。對應從CPU地址引腳發送到內存總線上的電信號。物理地址表示爲32或64位無符號整數。
內存管理單元(MMU)經過分段單元將邏輯地址轉換爲線性地址,接着又經過頁單元將線性地址轉換爲物理地址。
多CPU系統中,全部CPU共享相同地內存。因此RAM芯片可能同時被多個獨立的CPU訪問。因爲對RAM芯片的讀操做和寫操做都必須串行地執行,在總線和每一個內存芯片之間添加了一個叫作內存仲裁器的硬件電路。這個電路的做用是:當RAM芯片處於空閒狀態時,受權給CPU訪問:當RAM芯片處理其餘處理器的請求時,延遲其餘CPU訪問。甚至在單處理器系統中,也會使用內存仲裁器。這是由於這些系統包括了DMA控制器,它與CPU也存在併發操做。固然,多處理器系統中的內存仲裁器電路更爲複雜,由於它有更多的輸入端口。從編程的角度看,仲裁器是不可見的,由於它徹底硬件電路管理。
硬件分段
段選擇符與段寄存器
邏輯地址包括兩部分:段標識符
和段內相對偏移地址
。段標識符是16-bit的域,稱爲段選擇符;偏移地址是32-bit。
編程
- index:指明GDT或LDT中的段描述符項。因爲段描述符是8字節,它在GDT或LDT中的相對地址經過index字段乘8得到。addr = gdtr(ldtr) + 8*index。GDT最多能夠保存2^13-1個段描述符。
- TI:0,GDT;1,LDT
- RPL:當段選擇符加載到cs寄存器時,指明CPL;當訪問數據段時,也用來選擇性地減弱CPL。
爲了方便快速檢索段選擇符,處理器提供了6個專用分段寄存器來保存段選擇符,它們是cs,ss,ds,es,fs和gs. 程序能夠經過S/L來
重用段寄存器。緩存
- cs:代碼段寄存器,指向含有程序指令的段;cs寄存器還有一個重要的功能:它包含一個指定當前CPU特權級(CPL)的的2-bit域。0表示最高優先級;3表示最低優先級。 Linux只使用了0和3,以區份內核模式和用戶模式
- ss:堆棧段寄存器,指向包含當前程序棧的段
- ds:數據段寄存器,指向包含靜態和全局數據的段
其它三個是通用分段寄存器,能夠指向任意類型的段。
段描述符
每一個段都由8字節的段描述符表示,它描述了段的特徵。段描述符要麼存儲在全局描述符表(GDT)裏,要麼存儲在本地描述符表(LDT)裏。一般只定義了一個GDT,若是進程須要額外建立除了GDT中以外的段,每一個進程也容許有本身的LDT。主存裏GDT的地址和大小都包含在gdtr控制寄存器中,而當前正在使用的LDT的地址和大小則包含在ldtr控制寄存器中。
數據結構
- Base:段首字節的線性地址
- G:若是爲0,段大小用字節表示,不然用4096字節的倍數表示
- Limit:段中最後一個內存單元的偏移地址,即段的長度。G爲0時,段長度爲1~1MB;G爲1時,長度爲4KB~4GB
- S:若是置爲0,這個段是系統段,保存關鍵的數據結構;不然是普通的代碼段或數據段
- Type:指明段的類型及訪問權限,包括代碼段,數據段,任務狀態段以及LDT段
- DPL: 描述符特權級,指明瞭訪問此段所需的最小CPL。0級DPL只能被0級CPL訪問;3級DPL能夠被任意等級CPL訪問
- P:0表明此段不在主存中。Linux不會將整個段交換到硬盤,全部這一字段老是1
- D or B:1表明段偏移是32位,0表明16位
- AVL:Linux中不使用
段描述符的快速訪問
前面提到,邏輯地址由16位段選擇符和32位段偏移構成,而且段寄存器只用來存儲段選擇符。
爲了加速邏輯地址想線性地址的轉換,x86處理器爲6個段寄存器各自添加了一個不可編程寄存器,存儲了段寄存器中段選擇符所指定的段描述符。當段選擇符加載到段寄存器時,段描述符被加載到不可編程的段寄存器中。所以,邏輯地址東側轉換不用訪問主存中的GDT或LDT。只有當段寄存器改變時,才須要訪問GDT或LDT。
架構
分段單元
分段單元經過如下操做將邏輯地址轉化爲線性地址。
併發
- 檢查TL字段,決定從gdtr仍是ldtr獲取基址
- index*8加上基址獲得段描述符的地址
- 將段描述符Base字段加上offset獲得線性地址
因爲不可變成寄存器的存在,只有當段寄存器發送改變時才須要作前兩步。
Linux分段
Linux以極爲有限的方式使用分段,而更喜歡使用分頁。操作系統
- 若是全部進程使用相同的段寄存器,即共享一套線性地址時,內存管理更爲容易
- Linux的設計目標是對普遍架構提供可移植性,RISC架構對分段支持有限
2.6版本僅在x86架構中使用分段。全部用戶態進程使用一樣的一對指令段和數據段進行尋址。一樣,所用內核態的進程也是如此。對應的段選擇符經過4個宏定義:__USER_CS, __USER_DS, __KERNEL_CS,和__KERNEL_DS。與這些分段對應的線性地址都從0起始,而且尋址限制長度爲2^32-1,即不管用戶態和內核態進程都使用相同的邏輯地址。而且邏輯地址的offset字段與對應的線性地址一致。
硬件分頁
分段與分頁必定程度上是冗餘的,它們均可覺得不一樣的進程區分物理地址空間。分段將不一樣的線性地址空間賦給每一個進程;分頁將相同的線性地址空間映射到不一樣的物理地址空間。分頁單元將線性地址轉換爲物理地址。分頁單元的主要任務是檢查訪問類型與訪問權限是否匹配。若是內存訪問是無效的,將會產生頁錯誤異常。
出於高效,線性地址被分組爲固定長度的區間,稱爲頁。同一頁中的連續線性地址被映射爲連續的物理地址。這樣,內核能夠指定頁的物理地址和訪問權限而不用指定頁中包含的全部線性地址的物理地址和訪問權限。一般,「頁」既指一組線性地址,也包含這組地址中的數據。
分頁單元將所有RAM分紅固定大小的頁框(物理頁)。每一個頁框包含一個頁。頁框是主存的組成部分,所以也是存儲區域。頁和頁框的區別在於,前者只是一個數據塊,能夠存放在任何頁框或硬盤上。將線性地址映射爲物理地址的數據結構稱爲頁表。頁表存儲在主存中,而且在啓用分頁單元以前應當被內核初始化。從80386開始,x86處理器支持分頁,經過置位cr0寄存器的PG標誌啓用,若PG爲0,線性地址被解釋爲物理地址。設計
常規分頁
從80386開始,分頁單元處理4KB的頁。32位線性地址被分爲3個字段。3d
- 目錄:高10位
- 頁表:中間10位
- 偏移:低12位
線性地址的轉換由兩步完成,第一步是頁目錄,第二步是頁表。兩級模式減小了每一個進程頁表所需的RAM空間。一級模式若進程使用整個4G地址空間,則須要2^20個表項,二級模式能夠只爲進程實際使用的虛擬內存區請求頁表。每一個活動的進程都要有頁目錄,不須要立刻爲全部頁表分配內存,只有當進程實際須要頁表的時候再爲其分配內存會更加有效率。
正在使用的頁目錄的物理地址存在cr3寄存器中。線性地址中的Dirtecory字段決定頁目錄中的目錄項,目錄項指向頁表。Table字段決定頁表中的表項,包含頁框所在的物理地址。Offset字段決定頁框中的相對位置。
頁目錄和頁表具備相同的結構。目錄項與頁表項分配給進程的物理地址。其他項均填0,訪問這些項的線性地址將產生缺頁異常:指針
- Present:若是置位,代表所指的頁或頁表包含在主存中;若是爲0,頁不在主存,其餘位可被操做系統使用。若須要地址轉換的目錄項或頁表項中Present被重置,線性地址存在cr2中,產生缺頁異常
- Accessed:當分頁單元對頁框進行尋址時置位。用於操做系統換出頁。重置由操做系統完成
- Dirty:只用於頁表項。當頁框執行寫操做時置位,用於操做系統換出頁。重置由操做系統完成
- Read/Write:頁或頁表的讀寫權限。若爲0,只讀,不然,可讀寫
- User/Supervisor:訪問頁或頁表所需的特權級。若爲0,CPL小於3(內核態)才能對頁尋址,不然,總能對頁尋址
- PCD和PMT:控制硬件高速緩存處理頁或頁表的方式
- Page Size:只用於頁目錄項,若是置位,頁目錄項指的是2M或4M頁框
- Global:只用於頁表項,防止經常使用頁從TLB高速緩存中刷新出去
擴展分頁
x86微處理器提供擴展分頁,容許頁框大小達到4MB,要求Page Size字段置位。內核不使用中間頁表,節省內存並保留了TLB項。
- Directroy:高10位
- Offset:低22位
物理地址擴展分頁機制
處理器支持的RAM容量受地址總線上的地址引腳數量限制。Intel將引腳數量由32擴展到36位支持64GBRAM,做爲32位x86架構的擴展。須要引進新的分頁機制將32位線性地址轉換爲36位物理地址。
- 64GBRAM分爲2^24個頁框。頁表項的頁字段由20位擴展到24位。所以頁表項有32位變爲64位。
- 引入新級別頁目錄指針表(PDPT),由4個64位表項組成
- cr3存儲27-bit的PDPT基址
硬件高速緩存
微處理器時鐘頻率有幾個GHz,DRAM存取時間是週期的數百倍。爲了縮小CPU與RAM速度不匹配,引入硬件高速緩存內存,基於局部性原理。使用小而快的內存存放最近最經常使用的代碼與數據。當訪問RAM時,CPU分爲命中高速緩存與沒有命中。當命中時,控制器從高速緩存行中取數據到CPU寄存器。對於寫操做,分爲通寫和回寫。通寫既寫高速緩存行也謝RAM。回寫只更新高速緩存行,只有當CPU執行刷新指令或FLUSH硬件信號(一般不命中)產生寫回到RAM中。Linux對全部頁框都啓用高速緩存,對全部寫操做採起回寫策略。在多處理器系統中,每一個處理器都有單獨的硬件高速緩存,若是一個CPU修改了高速緩存,其餘包含這塊數據的CPU須要保持數據同步,稱爲高速緩存偵聽。
TLB:轉換後援緩衝器,用於加快線性地址的轉換。當線性地址經過計算得出的物理地址存放在TLB表項中。多處理器系統中,每一個CPU都有本身的TLB,無需同步。當cr3寄存器修改時,本地TLB全部項失效,由於新的一組頁表被啓動。
Linux中的分頁
Linux使用了常規分頁。爲了適用32位於64位架構,採用4級分頁模型:
- 頁全局目錄
- 頁上級目錄
- 頁中間目錄
- 頁表
對於未啓用物理地址擴展的32位系統,2級分頁已經足夠。Linux將上級和中間目錄置爲0。可是兩個目錄在指針序列中的位置被保留,所以它們能夠在32和64位系統上使用。內核保留上級和中間目錄,將它們項數設爲1,而且映射到全局目錄的表項中。 對於啓用物理地址擴展的32位系統,使用3級分頁。全局目錄與x86的頁目錄表一致,上層目錄被省略,中間目錄對應x86頁目錄,頁表對於x86頁表。64位系統取決於硬件對線性地址位的劃分。 Linux進程處理很大程度依賴於分頁。給每一個進程分配不一樣的物理地址空間,有效防止尋址錯誤;能夠將頁框中的頁保存到磁盤上,再從新裝入其餘頁框中。每一個進程都有本身的頁全局目錄和頁表,發生進程切換時,將cr3寄存器保存在換出進程描述符中,將換入進程對應的值存入cr3。