中央處理器的快取記憶體控制 在 8088 的時代,由於中央處理器的速度還不是很快,所以記憶體通常都還來得 及在中央處理器資料處理完之前準備好下一份要處理的資料,而不會形成效率上 的瓶頸。但是到了現在,由於中央處理器的效能進展的太過迅速,記憶體經常會 有跟不上的情形。為了提昇中央處理器的效率,從 i486 開始,所有英代爾設計 的中央處理器都有內建一定大小的快取記憶體 (cache memory) 來增進記憶體讀 取的效率。 快取的原理是利用一小塊高速記憶體來存放最近使用過的的程式碼或資料,如此 一來,這些資料在重覆使用時就不必每次都到系統記憶體上拿。由於程式經常會 重覆存取在同一區域內的記憶體資料,因此快取對系統效率的提昇有相當大的幫 助。通常最快的第一級快取記憶體 (L1 cache) 大小只有數千到數萬位元組而已 ,但是由於它是直接整合在中央處理器內部,因此使用的效能最高。再接下來有 稍慢一點的第二級快取記憶體 (L2 cache) ,最大可以到達一百萬位元組。第二 級快取在某些系統(例如 Pentium 系列)中是做在主機板上, 而 P6 系列(包 括 Pentium Pro 和 Pentium II 兩種處理器) 則是把它整合在中央處理器的內 部。由於所有的這些動作都是由硬體進行,因此它們對軟體來說是透明的;軟體 不用管(通常也管不到)它的內部運作。 這樣聽起來好像很有道理,但是其中有一個小問題:雖然個人電腦的架構在設計 上是把記憶體位址和輸出入位址分開,但是在很多情況下我們還是會有使用到記 憶體對映輸出入 (memory-mapped IO) 的地方。最好的例子就是螢幕顯示的控制 ,以標準的 VGA 卡來說, 當我們在對 0A0000h 到 0BFFFFh 這一段記憶體做動 作的時候,實際上資料是送到顯示卡去,而不是到系統記憶體上。為了維持這類 記憶體對映輸出入的正常運作,這些記憶體位址是不能夠被快取起來的。那麼中 央處理器怎麼知道哪些記憶體位址可以快取而哪些不行呢? 這個問題通常是由軟體和硬體一起合作解決的;在這裡我們將會說明軟體在控制 快取記憶體時可以使用的工具和方法。每一種中央處理器對軟體控制快取的能力 都有不同的規範,我們將以控制範圍從小到大舉出四個例子,來比較其中的相同 和不同點。在本文的最後會利用一個小實驗來說明快取對系統效率的重要性,而 一些比較深入的課題,例如說在多處理器的系統之下該如何維持快取(和記憶體 存取)的一致性,則不在討論之列。 一 英代爾 Pentium 處理器 Pentium 系列的處理器對快取記憶體控制方面提供的軟體工具十分有限,因此大 部分的工作都要靠硬體之間的協調來完成。 由於 Pentium 已經成為業界的標準 ,因此所有的中央處理器都支援相同的控制機制。在真實模式下,軟體能使用的 工具只有兩項: 1. CD 旗標 CD 代表快取除能 (Cache Disable) , 它位在控制暫存器零 (CR0) 的第 三十個位元 (bit 30)。當它被設為 1 之後,快取記憶體的內容將不再新 增(也就是還沒有被快取的記憶體位址就不會再被快取了),但是已經被 快取的記憶體位址中的資料仍會被更新,而且對這些位址的快取會持續地 發揮作用。如果要完全的使快取記憶體停止作用,必須在設定 CD 之後使 快取記憶體中的內容失效。 2. INVD 和 WBINVD 指令 這兩個指令的目的都是在宣告目前快取記憶體中的內容一律無效,唯一的 不同點是 WBINVD 會先把快取記憶體中的資料寫入記憶體中,而 INVD 則 不會。一般都是以使用 WBINVD 為主,只有在特殊情況下(例如作業系統 在進行某些錯誤處理時)才會使用到 INVD 指令。 在保護模式下,如果有啟動分頁模式 (paging) 的話,作業系統可以用個別的分 頁為單位來指定不同的快取方式。其軟體程式介面如下: 3. 控制暫存器中的 PCD 旗標 在控制暫存器三 (CR3) 中, 第四位元 (bit 4) 被稱為 PCD 旗標,它決 定分頁目錄 (page directory) 是否可以被快取。如果它的值是和 CD 的 值都是 0 的話, 表示分頁目錄是可以被快取的,否則分頁目錄就不能夠 被快取起來。 4. 分頁目錄和分頁表格中的 PCD 旗標 在分頁目錄中的第四位元也稱為 PCD 旗標, 它的作用是決定它所指向的 分頁表格 (page table) 能不能被快取。同樣的,在分頁表格的第四位元 也有一個 PCD 旗標, 它的作用則是決定它所指向的那一個分頁能不能被 快取。這三種 PCD 的作用是相互獨立的,但是它們都會受 CD 的影響。 在這裡有一點要注意的,就是分頁目錄和分頁表格並不是被快取在第一級快取記 憶體中, 而是放在轉換後備緩衝器 (TLB, Translation Lookaside Buffer) 裡 面,才能真正的加速保護模式下線性位址的計算。 如果要清除和某些特定位址相 關的 TLB 快取時,可以使用以下的指令: 5. INVLPG 指令 在使用 INVLPG (INVaLidate PaGe) 時必須提供一個記憶體位址作為參數 ,處理器會自動找出和這個位址相關的所有 TLB 快取欄位, 然後宣告其 中的內容無效。這個指令必須在特權階級零 (ring 0) 中執行。 二 超微 K6 處理器 K6 在快取記憶體控制方面,除了 Pentium 所提供的介面之外,另外增加了一些 新的功能。在這些新功能之中,大部分都和寫入配置 (write allocate) 的作用 相關。Pentium 處理器內部的快取機制是只有在讀取的時候才會把那一段記憶體 位址給快取起來,對於寫入來說,如果要寫入的記憶體原本就沒有被快取,那麼 要寫入的資料就會直接被送到系統記憶體。在相同的情況下, K6 如果發現程式 要寫入一塊沒有被快取的記憶體,它會先把它給快取起來,然後再把資料寫入快 取記憶體中;這就是寫入配置。雖然這種做法會使得第一次寫入記憶體花費較多 的時間,但是程式如果緊接著寫入鄰近的記憶體位址,就會因為快取而增加更多 的效率。 雖然 K6 在一般的快取控制上沒有提供很多新的功能,但是在寫入配置方面就有 很多方法可以加以調整及控制,控制方法如下: 1. CI 旗標 這個旗標是位在測試暫存器十二 (TR12) 上的第三個位元 (bit 3),從它 的名字 CI (Cache Inhibit) 就可以知道它也是用來關閉快取功能的。和 之前提到的 CD 旗標不同的是, CI 只關閉處理器上的第一級快取記憶體 ,至於主機板上的第二級快取記憶體則仍然照常運作。這項功能在英代爾 的 Pentium 和 P6 系列都沒有支援, 因為它們已經沒有測試暫存器的設 計了。試圖存取測試暫存器會產生不合法指令的錯誤。 註:請參閱結論部分以得到對 TR12 的更進一步說明。 2. WAE15M 旗標 這也是 K6 特有的功能。 WHCR (Write Handling Control Register) 位 在 MSR 82h 上,裡面含有二個欄位, 可以用來控制寫入配置作用的範圍 。第零個位元是 WAE15M (Write Allocate Enable 15-to-16-Mbyte),它 的值如果是零表示處理器不應該對在 0F00000h 到 1000000h 之間的記憶 體進行寫入配置。這個旗標的設計目的,是為了能夠和一些會把輸出入位 址對應到這一段記憶體位址的週邊設備相容(由此可見人都是很短視的, 在 8086 時代把 0A0000h 到 100000h 留給記憶體對映輸出入使用,後來 有了 286 之後又有人改用 0F00000h 到 1000000h 這一段。從 386 起可 以定址到 4GB 記憶體, 我的顯示卡又把 0E0000000h 到 0E1003FFFh 這 一段位址拿來用掉了。好好的連續記憶體位址給切得亂七八糟的,還好有 分頁模式可以用,不然寫作業系統的人要發狂了)。 3. WAELIM 欄位 這個欄位也是位在 WHCR 上面,它的範圍是從第一個位元到第七個位元, 一共有七個位元寬。 WAELIM (Write Allocate Enable LIMit) 的值代表 了進行寫入配置的位址上限; 以 4MB 為一個單位,它可以指定的最大範 圍是 508MB。一般來說,處理器對這個位址以下的記憶體都會進行寫入配 置,而這個位址以上的則不會,但是這個原則可以被其他的機制打破(例 如說前述的 WAE15M 旗標)。超微建議 WAELIM 的值應該由 BIOS 設為系 統記憶體大小。 三 超微 K5 處理器 和時間比較近的 K6 比起來,比較早期的 K5 處理器在快取的控制上面有了更大 的彈性;不只是在寫入配置方面的選項更多,而且在一般的快取控制上也提供了 很多方法讓軟體以實際的需求來調整快取的運作狀態: 1. WA 旗標 超微在 K5 處理器定義了一個叫 HWCR (HardWare Control Register) 的 暫存器,位置在 MSR 83h 上, 裡面有三個欄位可以用來進行快取的控制 。在第四個位元上的就是 WA (Write Allocate Enable) 旗標,當它的值 被設定為 1 時表示寫入配置是可以運作的。 2. DIC 和 DDC 旗標 這兩個旗標的名字分別是 Disable Instruction Cache 和 Disable Data Cache 的縮寫,位置則是在 HWCR 的第六和第七位元上。這兩個旗標的值 如果被設為 1,指令快取和資料快取就會被關閉。 3. AAR 暫存器 AAR (Array Access Register) 暫存器位在 MSR 82h 上, 它可以用來讀 取和寫入處理器內部任何快取記憶體的內容,包括指令快取,資料快取, 和 TLB 在內。暫存器內分為兩個部分, 較高的三十二位元用來指定要讀 取或寫入快取記憶體的哪一個部分,較低的三十二位元則是要寫入或讀取 的資料。詳細的格式因每種不同的快取記憶體而有所不同,在這裡就不再 多做說明。 下面的這些資料是用來控制 K5 在執行寫入配置時受到的範圍限制。在 K5 處理 器上,對於寫入控制的管理,除了靠 WA 之外,還可以單獨對記憶體裡面三個特 別的區塊進行不同的設定:這三個區塊分別是一個固定區塊 (fixed range),記 憶體頂端 (top-of-memory),以及一個可程式化區塊 (programmable range) 。 詳細說明如下: 4. WATMCR 暫存器 這個字是 Write Allocate Top-of-Memory and Control Register 的縮 寫,暫存器位在 MSR 85h 上面,裡面一共有四個欄位。 最大的一個位在 第零到第十五位元,用來指定記憶體頂端的起點。這個十六位元長的數值 會被用來當做三十二位元線性位址中較高的十六位元,在這個位址以上的 記憶體就算是記憶體頂端,寫入配置也會受相關的限制。 5. TME 旗標 這個欄位和上一個欄位息息相關,因為 TME (Top-of-Memory Enable) 控 制的就是在記憶體頂端這塊區域到底要不要進行寫入配置。這個旗標的位 置就在 WATMCR 的第十八個位元 (bit 18),如果它的值被設為 0 的話, 寫入配置照常進行,否則寫入配置在這塊區域內就不會發揮任何作用。這 種行為正好和名字相違背, 如果叫 TMD (Top-of-Memory Disable) 的話 就好多了。超微建議基本輸出入系統 (BIOS) 在開機時把記憶體頂端設定 在系統記憶體範圍之上沒有記憶體的地方,然後把 TME 設定為 1 。 6. FRE 旗標 它的位置在 WATMCR 的第十六個位元 (bit 16),作用和 TME 非常相似, 只不過 FRE (Fixed Range Enable) 控制的是固定區塊的寫入配置功能。 固定區塊的位置是在 0A0000h 到 100000h 這一段 384K 的區域,由於它 在 8086 時代就已經獨立出來保留給周邊設備使用,因此 K5 對這一塊歷 史區域也做了特殊處理。不必說,這個旗標的建議值還是 1 。 7. WAPMRR 暫存器 WAPMRR (Write Allocate Programmable Memory Range Reigster) 的作 用是設定可程式化區塊的範圍,位置在 MSR 86h 上面。 這個暫存器較高 的三十二位元是保留起來的,較低的三十二位元被切成兩半;第零位元到 第十五位元指定這個區塊的底端位址,而第十六位元到第三十二位元則是 這個區塊的頂端位址。這兩個十六位元數字會被當做三十二位元位線性位 址較高的十六位元,也就是調整的範圍以 64K 為一個單位。 8. PRE 旗標 就像 TME 和 FRE 的作用一榚,PME (Programmable Range Enable) 的作 用是用來控制可程式化區域的寫入配置。它的位置在 WATMCR 的第十七個 位元。可程式化區域的目的是讓基本輸出入系統能夠隨心所欲地在記憶體 上任何一塊區域開洞,以保持和一些特殊周邊系統的相容性。 四 英代爾 Pentium Pro 處理器 在快取控制方面,英代爾的 Pentium Pro 處理器可以說是目前市面上 x86 相容 機種中功能最完整的一個 (我們把 Pentium II 和 Pentium Pro 算在同一系列 裡)。經由稱為 MTRR (Memory Type Range Registers) 的技術,作業系統最多 可以對九十六個不同的記憶體區塊作不同的快取設定;從這裡我們就可以看出它 的傲人之處了。首先來看看和 TLB 相關的部分: 1. PGE 旗標 由於 TLB 中快取的是分頁目錄和分頁表格的內容, 因此在管理上和一般 的記憶體快取有很多的不同之處。舉例來說, TLB 的內容在 CR3 被設定 之後會自動被宣告為不合法 (invalidate) ,但是在某些時候,為了效率 上的考量,我們會希望能夠阻止這個動作在某些分頁目錄或分頁表格上發 生。 PGE (Page Global Enable) 旗標在設為 1 的時候, 在分頁目錄及 分頁表格上的 G 旗標就可以發生作用。PGE 的位置在控制暫存器 CR4 的 第七個位元 (bit 7)。 2. G 旗標 位在分頁目錄及分頁表格的第八位元 (bit 8), G (Global) 旗標在被設 為 1 時能防止所在的分頁目錄或表格在寫入 CR3 時被宣告無效。由於在 進行行程切換 (task switch) 也有改變 CR3 的內容,對於某些常用的分 頁(例如系統核心所在處),設定 G 旗標可以增加系統效能。 接下來的部分將對 MTRR 的設定做一個說明。由於它的功能相當的多,因此設計 上把它分成三組暫存器,各自負責一定的功能。 第一組是 MTRRDefType 暫存器 ,它負責一般的 MTRR 設定,位置則是在 MSR 2FFh 上: 3. Type 欄位 這個欄位的範圍是從 MTRRDefType 的第零位元到第七位元, 一共是八位 元長,它定義了那些 MTRR 沒有特別規範的記憶體範圍應該以什麼樣的方 式進行快取。Pentium Pro 支援的快取型態及代碼如下: Uncacheable (UC) 0 從它的名字就可以很清楚的瞭解它所代表的意義:所有的記憶體存取都 會直接送到系統匯流排 (system bus) ,處理器完全不會進行任何的快 取動作。記憶體對應輸出入所使用的記憶體區域都應該被設為 UC 。 Write Combining (WC) 1 被設定為 WC 的記憶體也不會被快取在快取記憶體中,但是在做記憶體 寫入時,處理器可以先把資料放在寫入緩衝區 (write buffer) 中,等 到適當時機在進行寫入。顯示記憶體所對應到的位置應該被設為 WC 。 Write-through (WT) 4 Writeback (WB) 6 這兩種快取型態都是用在一般記憶體上的,它們都允許記憶體快取,寫 入配置(這是超微的說法,英代爾想必有他們自已的名稱),以及寫入 緩衝區的使用。唯一的不同點在於 WT 在記憶體寫入時除了把資料填入 快取記憶體內外,還會把資料送到寫入緩衝區(這樣資料在很短的時間 之內部會被寫回記憶體)。 Write-protected (WP) 5 這種型態通常只有在多處理器系統上才會發揮作用,在此不做討論。 把其他代碼填入 Type 欄位會造成保護模式例外 (#GP)。 4. FE 旗標 這個旗標代表的是 Fixed-range MTRRs Enable,它如果被設為 1 的話表 示固定範圍的 MTRR 可以發生作用。 它的位置是在 MTRRDefType 的第十 位元 (bit 10) ,有關固定範圍的 MTRR 在下面會有另外的說明。 5. E 旗標 這個旗標的名字就是 MTRRs Enable 縮寫, 位於 MTRRDefType 的第十一 位元 (bit 11),它決定 MTRR 是否能發揮作用。如果它被設為 1 的話, 表示 MTRR 可以作用;反之 MTRR 功能將被關閉,而所有記憶體區域都會 被設定成 UC 。 接下來的這一組暫存器負責的是固定範圍的 MTRR 控制,一共有十一個暫存器。 這十一個暫存器每一個有六十四位元長,切成八個欄位,因此可以對八十八個不 同的記憶體區塊做快取設定: 6. MTRRfix64K_00000 暫存器 這一個暫存器的位置是在 MSR 250h 上,八個欄位從低位元組到高位元組 分別負責從 00000h-0FFFFh 到 70000h-7FFFFh 的 64KB 區域,而快取型 態的代碼還是和 MTRRDefType 裡的 Type 欄位一樣。 7. MTRRfix16K_80000 和 MTRRfix16K_A0000 暫存器 這兩個暫存器分別位於 MSR 258h 和 MSR 259h 上,從低到高的每一個八 位元欄位分別代表在相對 16KB 區域內的記憶體快取型態。 8. MTRRfix4K_C0000 到 MTRRfix4K_F8000 暫存器 剩下的 256KB 記憶體,則是以 4KB 為單位分到剩下的八個暫存器中;因 此,第一個暫存器負責 0C0000h-0C7FFFh 這部分, 第二個暫存器則是負 責設定 0C8000h-0CFFFFh 這一段, 依此類推……。這八個暫存器的位置 是位在 MSR 268h 到 26Fh 上面。 有趣的一點是,這八十八個記憶體區域都在線性位址的前 1MB 裡, 而且越到後 面區塊的密度越高。手冊中沒有提到這樣安排的理由,不過這顯然和 8086 的歷 史包袱是脫不了關係的, 而在 1MB 之上留白的問題,除了有底下介紹的可變範 圍 MTRR 之外,利用 PCD 旗標也能達到類似效果可能也是重要的原因。 第三組暫存器的十六個暫存器負責可變範圍 MTRRs (Variable Range MTRRs) 的 控制,以兩個暫存器為一組,一共可以定義八個可變範圍。同組的兩個暫存器中 的第一個是 MTRRphysBaseN(最後一個 N 代表從零到七的數字), 它的位置就 在 MSR 200h-206h 的偶數編號控制暫存器;第二個暫存器是 MTRRphysMaskN , 位於 MSR 201h-207h 的奇數編號控制暫存器。內部欄位定義如下: 9. Type 欄位 這裡的 Type 欄位是位在 MTRRphysBaseN 的第零到第七位元, 它的內容 就是在這一個記憶體範圍內所應採用的快取型態。有關於快取型態的種類 、實際意義及代碼可參考在 MTRRDefType 裡 Type 欄位的說明。 10. PhysBase 欄位 這個欄位的位置在 MTRRphysBaseN 的第十二到第三十五位元, 一共有二 十四位元長,和 PhysMask 欄位一起用來指定一個記憶體範圍。至於這個 記憶體範圍是怎麼計算出來的,在後面會有說明。 11. V 旗標 這個旗標位在 MTRRphysMaskN 的第十一個位元上, 它實際上就是代表這 一組暫存器是否有作用; 如果設為 1 表示有,否則就沒有。為什麼要稱 為 V (Valid) 而不叫做 E (Enable) 就沒人知道了。 12. PhysMask 欄位 這個欄位的位置在 MTRRphysMaskN 的第十二到第三十五位元, 一共有二 十四位元長,和 PhysBase 欄位一起用來指定一個記憶體範圍。至於這個 記憶體範圍是怎麼計算出來的,在後面會有說明。 在前面提到了 PhysBase 和 PhysMask 這兩個欄位,但是卻沒有說明這兩個欄位 到底和記憶體範圍間有什麼關係。其實很簡單;如果 Mem_Addr 是一個記憶體的 三十六位元線性位址且 (Mem_Addr SHL 12) AND PhysMask = PhysBase AND PhysMask 的話,這個記憶體位址就算在由 PhysBase 和 PhysMask 所定義的記憶體範圍中 。我們可以在這樣的架構下定義出不連續的記憶體範圍,不過英代爾的建議是不 要使用不連續的記憶體範圍當做可變範圍的 MTRR 以免發生問題。 --- 結論 就如同一開始所說的,快取在設計上就是希望能夠在程式設計師不改變程式設計 習慣的情況下增加程式執行的效率,因此一般來說,除了要寫基本輸出入系統和 作業系統的程式員之外,其他人應該都不需要去管快取的運作及控制。雖然如此 ,如果能透過軟體的快取控制來瞭解快取的原理及運作方法,進而在有需要時加 以操縱,對於寫出高效率的優秀程式還是有一定的幫助。 有了以上的資訊之後,我們就可以藉由快取控制來評估系統在有快取和沒有快取 的情況下,效率的差異會有多大。我利用第二個組語作業的八后程式,配上簡短 的快取開關程式碼,在家裡的 K6 上做了一些實驗。以下是幾點在過程中發現值 得注意的地方: 1. 在保護模式下,只有特權階級零的程式才有權力存取各種的系統暫存器, 而在虛擬 86 模式 (VM86) 下的行程一向都是在特權階級三下面執行。因 此,在進行和快取相關的測試時一定要確定現在是在真實模式底下。如果 程式在 Windows 95 的 MS-DOS 模式下執行時會產生保護模式錯誤,而如 果在有載入 EMM386 的 MS-DOS 下執行的話,系統則會當機( EMM386 為 了能夠製造出有 EMS 和 UMB 的假象必須利用分頁機能,所以系統中一但 載入了 EMM386 就已經進入了虛擬 86 模式了。大家要記住,這就是小程 式想假冒作業系統的下場)。 4. 在 486 上好像真的有一類暫存器叫做測試暫存器, 問題是各家的組譯器 都只認識 TR3,TR4 和 TR5。在經過一番研究之後,我才發現 K6 上所謂 的 TR12 原來是做在 MSR 0Eh 上面…… 超微實在應該把它給換個名字, 不要再叫 TR 才對。 3. 最後的測試是在純 MS-DOS 系統下進行的,結果如下: 開啟全部快取 197 ms 關閉 L1 快取 649 ms 關閉所有快取 1815 ms 差距相當的大,光是 L2 快取就可以帶來 1.80 倍的效率提升,如果再加 上 L1 快取效率又可增加 2.29 倍(而且這個程式還是最佳化過的,大部 分資料都直接放在暫存器中)。所以我們可以知道,快取對現代處理器的 效能實在是有重大的貢獻。 --- 參考資科 1. Intel Architecture Software Developer's Manual Volume 2: Instruction Set Referende, Order Number 243191 標準的工具書,對每一個指令的原理、動作、還有可能產生的例外情形都 有詳細的說明,是要寫 x86 組合語言的人桌上不可少的一本書。 2. Intel Architecture Software Developer's Manual Volume 3: System Programming Guide, Order Number 243192 大部分的組合語言書籍不是不敢碰保護模式,就是只說說原理,最多最多 就是解釋一下 DPMI 和 VCPI 的設計。這本可不一樣,裡面的資料可以讓 你寫出一個多處理器的三十二位元保護模式多工作業系統。第九章對軟體 的快取控制有詳盡的說明。 3. AMD-K5 Processor Software Development Guide Publication 20007, Rev E, Amendment 0 我覺得超微真的好像有一點老二心態,出出來的手冊都薄薄的,意思大概 是反正一樣的部分大家去翻英代爾的手冊就好了。第四章有關於 HWCR 的 詳細資料。 4. AMD-K6 MMX Enhanced Processor Data Sheet Publication 20695, Rev E, Amendment 0 這本就不太一樣了,裡面資料包羅萬象,從電氣信號定義、軟體環境、到 處理器散熱可說是一應俱全。第八章有不少和快取相關的資料,不過大部 分都是在解釋原理和機制就是了。 5. AMD K86 Family BIOS and Software Tools Developer's Guide Publication 21062, Rev E, Amendment 0 這本書的內容和前面兩本重覆蠻多的, 不過至少在 AAR 方面的解釋比較 詳細,有興趣的可以參考。 6. Implimentation of Write Allocate in the K86 Processors Publication 21326, Rev C, Amendment 0 這一本書的內容說的都是和寫入配置有關的,包括 K5 和 K6 在這方面提 供的軟體控制功能;反正連封面才十八頁的書也塞不下其他的東西了。 7. 英代爾 Pentium II 廣告文宣 不要笑,你以為 TLB 的中文翻譯我是從哪裡生出來的? --- 作者 林川凱 B86506063