Intel x86 CPU的地址轉換加速機制

在x86 CPU中分頁機制引入了線性地址和物理地址的概念,從而在一個獨立的物理地址空間上,能夠同時存在多個相同且獨立的線性地址空間。在分頁使能的情況下,CPU訪問內存的時候,線性地址首先通過頁表查詢的機制轉化為物理地址,然後再通過物理地址完成物理內存的訪問。

對於64位的x86 CPU,若採用4級的分頁機制,其基本結構如下圖所示:

首先CR3寄存器存放著分級地址轉換頁表的入口地址,該地址指向第一級頁表PML4(Page Map Level 4),PML4表中的每一項都會包含一個PDPTR(Page Directory Pointer Table )表的指針,然後PDPTR表中的每一項都會包含一個PD(Page Directory Table)表的指針,接著PD表中的每一項都會包含一個PT(Page Table)表的指針,最後每個Page Table表中的每一項都會包含一個Page Frame的地址,即目標物理內存頁,將物理內存頁的基地址加上偏移量Offset就可以得到最終的物理地址。通常情況下,對於採用4KB頁的情況而言,PML4、PDPTR、PDE和PTE的寬度都為9bit,即每個頁表的大小為512個entry,即每個entry的大小為64bit,Offset的寬度為12bit,正好可以索引4KB的空間。各級頁表的索引值都是直接來自線性地址對應的地址位。當完成地址轉換得到最終的物理內存地址的時候,CPU才會將物理地址打包到內存訪問請求包中,發送到內存控制器上,完成最終的內存訪問請求。

在沒有任何加速的情況下,一次線性地址的訪問需要多次的內存訪問,至少需要4次的頁表訪問和一次最終的目標物理地址訪問;若頁表不存在(Page Fault),則需要的內存訪問次數將會大大提升。所以從CPU的角度來看,即使是直接的物理內存訪問,速度也不快。

下面是從gist.github.com/jboner/ 引用的一個比較有參考性的時延表格:

Latency Comparison Numbers
--------------------------
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns 14x L1 cache
Mutex lock/unlock 25 ns
Main memory reference 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 3,000 ns 3 us
Send 1K bytes over 1 Gbps network 10,000 ns 10 us
Read 4K randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD
Read 1 MB sequentially from memory 250,000 ns 250 us
Round trip within same datacenter 500,000 ns 500 us
Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory
Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip
Read 1 MB sequentially from disk 20,000,000 ns 20,000 us 20 ms 80x memory, 20X SSD
Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms

對於沒有任何cache幫助的物理內存訪問而言,其時延(100ns)將會是L1 cache命中(0.5ns)的200倍,是L2 cache命中(7ns)的20倍左右。從這個可以很清楚地看出cache對於CPU性能的重要性。

為了提高CPU的性能,CPU內部實現了很多種類的cache,一種是直接將物理內存的數據cache過來,也就是我們通常所理解的L1 cache、L2 cache甚至是L3 cache,即直接將目標數據放到CPU的cache上。另外一種則是為了加快地址轉換的cache,也就是這裡要討論的。

x86 CPU為了加快內存地址轉換引入了兩種cache,即TLB(Translation Lookaside Buffers)和Paging-Structure Cache。

  • TLB用於直接將線性地址的高位(即page number)直接轉換到物理內存頁(即page frame)基地址,然後加上偏移量,即一步到位地將線性地址轉換為物理地址。這種地址轉換最快,只需要一次的TLB索引就可以完成,不需要訪問地址轉換頁表。
  • Paging-Structure Cache,即cache的對象是地址轉換頁表,直接加快地址轉換頁表的訪問速度,最終達到地址轉換加速的效果。

對於一個4級的頁表來說,它包含了如下種類的paging-structure cache:

  • PML4 cache,該cache的索引值(輸入)來自線性地址的47:39位,對應到線性地址的PML4,cache的目標為PML4E的物理地址,通過該cache,可以直接得到PML4E;
  • PDPTE cache,該cache的索引值來自線性地址的47:30位,對應線性地址的PML4和Directory Ptr,通過該cache,可以直接得到PDPTE;
  • PDE cache,該cache的索引值來自線性地址的47:21位,對應到線性地址的PML4、Directory Ptr和Directory這幾個區域,通過該cache可以得到PDE。

可以看出PML4 cache、PDPTE cache和PDE cache的關係是對地址轉換頁表的一級一級延伸,同時也可以將TLB看做是PTE cache,因為TLB中,它利用線性地址的47:12位來索引cache,對應到線性地址的PML4、Directory Ptr、Directory和Table這幾個區域,通過它可以找到最終的page frame的基地址。如下圖所示:

TLB cache直接省去了所有地址轉換頁表的訪問;PDE cache則省去了對PML4 Table、Page Directory Pointer Table和Page Directory Table的訪問;PDPTE cache則省去了PML4 Table和Page Directory Pointer Table的訪問;PML4 cache則省去了對PML4 table的訪問。這些用於地址轉換的cache就是通過省去對地址轉換頁表的訪問來達到加速的效果。

如上圖所示,當CPU嘗試去訪問一個線性地址的時候,它會進行如下操作:

  1. 首先CPU會利用線性地址的47:12位作為索引,到TLB中去查找是否有相應的項,如果有,則直接利用TLB entry提供的物理頁基地址進行最終的內存訪問;如果沒有則繼續到第2步。
  2. CPU利用線性地址的47:21位,去PDE cache中查找,如果有相依的cache項,則直接利用該cache項提供的Page DirectoryTable地址,結合線性地址中PTE的值,直接訪問頁表查找Page Table的地址,從那開始正常的地址轉換流程。如果PDE 中找不到相應項,則繼續到第3步。
  3. CPU利用線性地址的47:30位,去PDPTE cache中查找,如果有相應的cache項,則直接利用該cache項提供的Page Directory Pointer Table地址,結合線性地址中PDE的值,直接訪問頁表查找Page Directory Table的地址,從那開始正常的地址轉換流程。如果在PDPTE cache中找不到對應項,則繼續到第4步。
  4. CPU利用線性地址的47:39位,去PML4 cache中查找,如果有相應的cache項,則利用該cache提供的PML4 Table地址,集合線性地址中的PDPTE的值,直接訪問頁表,查找Page Directory Pointer Table的地址,從那開始正常的地址轉換流程。如果在PML4 cache中查找不到對應項,則繼續到第5步。
  5. 到這裡說明地址轉換沒有任何cache可用,則只能老老實實從CR3提供的物理地址中,一步一步地走地址轉換的流程,這也是最慢的地址轉換方法。

因為在操作系統中,不同的進程的線性地址空間是獨立的,且可用線性地址空間的範圍是一致的,這就導致如果發生進程切換的時候,為了不使切換進來的進程不會使用到被切換出去的進程的地址轉換cache,就需要對這些cache進行flush操作,即將這些cache全部清除掉,這樣會影響到系統的性能。為了解決這一問題,引入了PCID(Processor Context Identifiers)機制,即這些地址轉換的cache被打上了PCID的標籤(系統中,每個進程的PCID不一樣,並且PCID在進程被切換進來的時候會被寫到CR3寄存器的11:0位),當CPU內部查找地址轉換的cache的時候,會先對cache裡面的PCID信息和當前的PCID,即存放的CR3寄存器的11:0的值做比較,只有兩個地方的PCID相等,CPU才可能會採用該cache項。這樣就可以在每次進程切換的時候都需要對地址轉換的cache進行flush的操作,同時可以保證不同的線性地址空間之間不會產生相互干擾。

總之,x86 CPU中就是通過引入cache的方式來加快線性地址的轉換。

推薦閱讀:

TAG:x86 | 內存(RAM) | 中央處理器(CPU) |