在看文章前可以先看下這個,

吳海波:專欄的序。

先有個大概的認識會對閱讀有所幫助。

上一章我們看到分頁機制下的地址翻譯是有多慢,為了更快,os表示已經無能為力,這個時候就要找到os的好基友hardware來幫忙了。為什麼很慢?因為我們要訪問PT,而PT在內存裡面,訪問內存的幾百個時鐘週期實在是太長了,那我們把PT都放到MMU裡面吧,但是上一章已經說了,PT太大,mmu放不下,那好吧,我們退而求其次,借鑒緩存的方式,我把最近常用的PTE放在mmu中,這樣只需要在mmu中很小的空間,就可以提高地址翻譯的效率啦。

加入TLB後,我們的地址翻譯的流程如下:

VPN = (VirtualAddress & VPN_MASK) >> SHIFT//獲取虛擬頁號
(Success, TlbEntry) = TLB_Lookup(VPN)//判斷該頁號是否在TLB中
if (Success == True) // TLB Hit//如果TLB命中
if (CanAccess(TlbEntry.ProtectBits) == True)//並且PTE的訪問標記也是校驗通過
Offset = VirtualAddress & OFFSET_MASK//獲取地址的偏移量
PhysAddr = (TlbEntry.PFN << SHIFT) | Offset//獲取物理地址
Register = AccessMemory(PhysAddr)//訪問內存數據
else
RaiseException(PROTECTION_FAULT)//如果許可權校驗有問題,則報錯
else // TLB Miss,TLB中不包含這個虛擬頁號對應的PTE,那麼就只能按照老方法來做了。
PTEAddr = PTBR + (VPN * sizeof(PTE))
PTE = AccessMemory(PTEAddr)
if (PTE.Valid == False)
RaiseException(SEGMENTATION_FAULT)
else if (CanAccess(PTE.ProtectBits) == False)
RaiseException(PROTECTION_FAULT)
else
TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)//這裡表示緩存改PTE,這樣下次就不會再MISS了。
RetryInstruction()

TLB miss後由誰處理?

那麼如果TLB miss了,是os還是硬體來負責處理呢?對X86來說,是硬體,而對於別的一些架構的cpu來說則是os來處理。如果是硬體來處理,那麼硬體就需要知道PT在哪裡,比如Intel x86的架構,使用一個固定的多級頁表,和CR3寄存器來指向最近使用的PT。如果是OS來處理,在發生tlb miss的時候,硬體只需要產生一個異常就可以了。不過需要注意的是,和其他的一些system call不一樣,這個tlb miss後的異常處理好後,需要執行發生異常的指令而不是下一條指令。另外要注意的是,os來處理tlb miss,要避免產生更多的tlb miss(比如你的處理程序不在內存中,還需要從disk載入,然後再緩存,然後。。。。。),當然避免的方式也有很多,比如tlb miss 的處理程序一直保存在內存中,或者保留一個tlb的條目專門保存處理程序的信息。使用os處理的首要好處就是靈活性,因為os可以在不改變硬體的情況下使用任何一種數據結構來實現頁表。另外就是簡單,當發生tlb miss 的時候硬體只需要一個異常就可以了。

TLB一般包括哪些信息?

讓我們更詳細地查看硬體TLB的內容。一個典型的TLB可能有32、64或128個條目,也就是所謂的全相連(fully associative)(其實這個是對緩存結構的一種劃分,還有各種其他的類型,在高速緩存中應用較多)。基本上,這意味著任何給定的翻譯都可以在TLB中的任何位置,硬體將並行搜索整個TLB以找到所需的轉換(快的飛起啊)。TLB條目可能如下所示:

VPN | PFN | other bits

VPN和PFN這個就不用說了,肯定是要有的,不然虛擬和物理就連接不起來了。更有趣的是「other bits」。例如,TLB通常具有有效位,該有效位表示該條目是否有效。同樣常見的是保護位,其確定頁面如何可以被訪問(就像頁表中一樣)。例如,代碼頁可以標記為「讀取」和「執行」,而堆頁面可能被標記為「讀取」,並且寫。還可以有一些其它欄位,包括地址空間標識符(address-space identifier)、臟位(dirty bit)等等。

TLB怎麼處理進程切換?

因為PT的信息在process是不共享的,所以當process切換為新的時候,老的tlb緩存就不準了。一種方式是在上下文切換的時候清空原有的tlb,另外一種是給每個process一個編號,就像PID一樣,我們稱為address space identifier (ASID),不過這個值比PID要小(比如PID是32位,ASID是8位)

TBL的替換策略是什麼?

當tlb中的條目存儲滿了,再次發生tlb miss就需要替換老的。這裡給出2種策略,一種是最近沒有使用的條目替換掉,一種是隨機選擇條目進行替換。隨機的好處是可以避免第一種策略的一個弊端。比如tlb可以存儲n個條目,但是一個循環是在n+1個條目間運行,那麼就會出現tlb全部miss,但是隨機替換就不會。

一個真實的TLB條目是什麼樣的?

最後,讓我們簡要地看看真正的TLB。此示例來自MIPS R4000[H93],這是一個使用軟體管理的TLB的現代系統;圖19.4中顯示了一個稍微簡化的MIPS TLB條目。MIPS R4000支持具有4KB頁的32位地址空間。因此,虛擬地址中有一個20位VPN和12位偏移.但是,正如在TLB中所看到的,VPN只有19位;事實證明,用戶地址空間只佔整個地址空間的一辦(其餘留給內核),因此只需要19位VPN。VPN轉換為24位物理幀號(PFN),因此可以支持高達64 GB物理內存(2的24次方再乘以4KB)。

在MIPS TLB中還有其他一些有趣的部分。我們看到一個全局位(G),它用於在進程之間共享的頁面。因此,如果設置全局位,則忽略ASID。我們還看到了8位ASID。一個問題是:如果每次運行的進程超過256,操作系統應該怎麼辦?最後,我們看到了3個一致性(C)位,它決定了一個頁面是如何被硬體緩存的;一個臟位(dirty bit),當頁面被寫入時被標記(稍後我們將看到它的使用);一個有效位,標記該條目是否有效。還有一個頁面掩碼欄位(未顯示),它支持多個頁面大小;稍後我們將看到為什麼擁有更大的頁面可能是有用的。最後,64位中的一些是未使用的(圖中的陰影灰色)。

MIPS TLB通常有32條或64條這樣的條目,其中大部分在運行時被用戶進程使用。然而,有一些是為操作系統保留的。操作系統可以設置一個wired 寄存器來告訴硬體TLB有多少插槽為操作系統預留;操作系統使用這些保留映射來處理它想要在關鍵時刻訪問的代碼和數據。因為MIPS TLB是軟體管理的,所以需要有更新TLB的指令。MIPS提供了四個這樣的指令:TLBP,它探測TLB以查看其中是否有特定的翻譯;TLBR,它將TLB條目的內容讀入寄存器;TLBWI,它替換特定的TLB條目;TLBWR,它隨機替換一個TLB條目。操作系統使用這些指令來管理TLB的內容。當然,這些指令是有特權的;想像一下如果用戶進程能夠修改TLB的內容用戶進程可以做什麼(太可怕了)。

推薦閱讀:

相關文章