《奔跑吧 Linux內核》之處理器體系結構

本文摘自人民郵電出版社異步社區《奔跑吧Linux內核》
html

第1章 處理器體系結構


京東購書: item.jd.com/12152745.ht…
試讀地址: www.epubit.com.cn/book/detail…
本章思考題
1.請簡述精簡指令集RISC和複雜指令集CISC的區別。
2.請簡述數值0x12345678在大小端字節序處理器的存儲器中的存儲方式。
3.請簡述在你所熟悉的處理器(好比雙核Cortex-A9)中一條存儲讀寫指令的執行全過程。
4.請簡述內存屏障(memory barrier)產生的緣由。
5.ARM有幾條memory barrier的指令?分別有什麼區別?
6.請簡述cache的工做方式。
7.cache的映射方式有full-associative(全關聯)、direct-mapping(直接映射)和set-associative(組相聯)3種方式,請簡述它們之間的區別。爲何現代的處理器都使用組相聯的cache映射方式?
8.在一個32KB的4路組相聯的cache中,其中cache line爲32Byte,請畫出這個cache的cache line、way和set的示意圖。
9.ARM9處理器的Data Cache組織方式使用的VIVT,即虛擬Index虛擬Tag,而在Cortex-A7處理器中使用PIPT,即物理Index物理Tag,請簡述PIPT比VIVT有什麼優點?
10.請畫出在二級頁表架構中虛擬地址到物理地址查詢頁表的過程。
11.在多核處理器中,cache的一致性是如何實現的?請簡述MESI協議的含義。
12.cache在Linux內核中有哪些應用?
13.請簡述ARM big.LITTLE架構,包括總線鏈接和cache管理等。
14.cache coherency和memory consistency有什麼區別?
15.請簡述cache的write back有哪些策略。
16.請簡述cache line的替換策略。
17.多進程間頻繁切換對TLB有什麼影響?現代的處理器是如何面對這個問題的?
18.請簡述NUMA架構的特色。
19.ARM從Cortex系列開始性能有了質的飛越,好比Cortex-A8/A15/A53/A72,請說說Cortex系列在芯片設計方面作了哪些重大改進?

Linux 4.x內核已經支持幾十種的處理器體系結構,目前市面上最流行的兩種體系結構是x86和ARM。x86體系結構以Intel公司的PC和服務器市場爲主導,ARM體系結構則是以ARM公司爲主導的芯片公司佔領了移動手持設備等市場。本書重點講述Linux內核的設計與實現,可是離開了處理器體系結構,就猶如空中樓閣,畢竟操做系統只是爲處理器服務的一種軟件而已。目前大部分的Linux內核書籍都是基於x86架構的,可是國內仍是有至關多的開發者採用ARM處理器來進行開發產品,好比手機、IoT設備、嵌入式設備等。所以本書基於ARM體系結構來說述Linux內核的設計與實現。
關於ARM體系結構,ARM公司的官方文檔已經有不少詳細資料,其中描述ARMv7-A和ARMv8-A架構的手冊包括:
前端

  • <ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition>

  • <ARM Architecture Reference Manual, ARMv8, for ARMv8-A architecture profile>

  • 另外還有一本很是棒的官方資料,講述ARM Coxtex系統處理器編程技巧:node

  • <ARM Coxtex-A Series Programmer’s Guide, version 4.0>
  • <ARM Coxtex-A Series Programmer’s Guide for ARMv8-A, version 1.0>
  • 讀者能夠從ARM官方網站中下載到上述4本資料[1]。本書的重點集中在Linux內核自己,不會用過多的篇幅來介紹ARM體系結構的細節,所以本章以快問快答的方式來介紹一些ARM體系結構相關的問題。linux


    可能有些讀者對ARM處理器的命名感到疑惑。ARM公司除了提供處理器IP和配套工具之外,主要仍是定義了一系列的ARM兼容指令集來構建整個ARM的軟件生態系統。從ARMv4指令集開始爲國人所熟悉,兼容ARMv4指令集的處理器架構有ARM7-TDMI,典型處理器是三星的S3C44B0X。兼容ARMv5指令集的處理器架構有ARM920T,典型處理器是三星的S3C2440,有些讀者還買過基於S3C2440的開發板。兼容ARMv6指令集的處理器架構有ARM11 MPCore。到了ARMv7指令集,處理器系列以Cortex命名,又分紅A、R和M系列,一般A系列針對大型嵌入式系統(例如手機),R系列針對實時性系統,M系列針對單片機市場。Cortex-A7和Coxtex-A9處理器是前幾年手機的主流配置。Coxtex-A系列處理器面市後,因爲處理性能的大幅提升以及傑出功耗控制,使得手機和平板電腦市場迅猛發展。另一些新的應用需求正在醞釀,好比大內存、虛擬化、安全特性(Trustzone[2]),以及更好的能效比(大小核)等。虛擬化和安全特性在ARMv7上已經實現,可是大內存的支持顯得有點捉襟見肘,雖然能夠經過LPAE(Large Physical Address Extensions)技術支持40位的物理地址空間,可是因爲32位的處理器最高支持4GB的虛擬地址空間,所以不適合虛擬內存需求巨大的應用。因而ARM公司設計了一個全新的指令集,即ARMv8-A指令集,支持64位指令集,而且保持向前兼容ARMv7-A指令集。所以定義AArch64和AArch32兩套運行環境分別來運行64位和32位指令集,軟件能夠動態切換運行環境。爲了行文方便,在本書中AArch64也稱爲ARM64,AArch32也稱爲ARM32。程序員


    1.請簡述精簡指令集RISC和複雜指令集CISC的區別。

    20世紀70年代,IBM的John Cocke研究發現,處理器提供的大量指令集和複雜尋址方式並不會被編譯器生成的代碼用到:20%的簡單指令常常被用到,佔程序總指令數的80%,而指令集裏其他80%的複雜指令不多被用到,只佔程序總指令數的20%。基於這種思想,將指令集和處理器進行從新設計,在新的設計中只保留了經常使用的簡單指令,這樣處理器不須要浪費太多的晶體管去作那些很複雜又不多使用的複雜指令。一般,簡單指令大部分時間都能在一個cycle內完成,基於這種思想的指令集叫做RISC(Reduced Instruction Set Computer)指令集,之前的指令集叫做CISC(Complex Instruction Set Computer)指令集。
    IBM和加州大學伯克利分校的David Patterson以及斯坦福大學的John Hennessy是RISC研究的先驅。Power處理器來自IBM,ARM/SPARC處理器受到伯克利RISC的影響,MIPS來自斯坦福。當下還在使用的最出名的CISC指令集是Intel/AMD的x86指令集。

    RISC處理器經過更合理的微架構在性能上超越了當時傳統的CISC處理器,在最初的較量中,Intel處理器敗下陣來,服務器市場的處理器大部分被RISC陣營佔據。Intel的David Papworth和他的同事一塊兒設計了Pentium Pro處理器,x86指令集被解碼成相似RISC指令的微操做指令(micro-operations,簡稱uops),之後執行的過程採用RISC內核的方式。CISC這個古老的架構經過巧妙的設計,又一次煥發生機,Intel的x86處理器的性能逐漸超過同期的RISC處理器,搶佔了服務器市場,致使其餘的處理器廠商只能向低功耗或者嵌入式方向發展。算法


    RISC和CISC都是時代的產物,RISC在不少思想上更爲先進。Intel的CSIC指令集也憑藉向前兼容這一利器,戰勝全部的RISC廠商,包括DEC、SUN、Motorola和IBM,一統PC和服務器領域。不過最近在手機移動業務方面,以ARM爲首的廠商佔得先機。編程


    2.請簡述數值0x12345678在大小端字節序處理器的存儲器中的存儲方式。

    在計算機系統中是以字節爲單位的,每一個地址單元都對應着一個字節,一個字節爲8個比特位。但在32位處理器中,C語言中除了8比特的char類型以外,還有16比特的short型,32bit的int型。另外,對於位數大於8位的處理器,例如16位或者32位的處理器,因爲寄存器寬度大於一個字節,那麼必然存在着如何安排多個字節的問題,所以致使了大端存儲模式(Big-endian)和小端存儲模式(Little-endian)。例如一個16比特的short型變量X,在內存中的地址爲0x0010,X的值爲0x1122,那麼0x11爲高字節,0x22爲低字節。對於大端模式,就將0x11放在低地址中;0x22放在高地址中。小端模式則恰好相反。不少的ARM處理器默認使用小端模式,有些ARM處理器還能夠由硬件來選擇是大端模式仍是小端模式。Cortex-A系列的處理器能夠經過軟件來配置大小端模式。大小端模式是在處理器Load/Store 訪問內存時用於描述寄存器的字節順序和內存中的字節順序之間的關係。緩存


    大端模式:指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中。例如:安全


    內存視圖:

    0000430: 1234 5678 0100 1800 53ef 0100 0100 0000
    0000440: c7b6 1100 0000 3400 0000 0000 0100 ffff複製代碼

    在大端模式下,前32位應該這樣讀:12 34 56 78。性能優化


    所以,大端模式下地址的增加順序與值的增加順序相同。


    小端模式:指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中。例如:


    內存視圖:

    0000430: 7856 3412 0100 1800 53ef 0100 0100 0000
    0000440: c7b6 1100 0000 3400 0000 0000 0100 ffff複製代碼

    在小端模式下,前32位應該這樣讀:12 34 56 78。


    所以,小端模式下地址的增加順序與值的增加順序相反。


    如何檢查處理器是大端模式仍是小端模式?聯合體Union的存放順序是全部成員都從低地址開始存放的,利用該特性能夠輕鬆獲取CPU對內存採用大端模式仍是小端模式讀寫。


    int checkCPU(void)
    {
    union w
    {
    int a;
    char b;
    } c;
    c.a = 1;
    return (c.b == 1);
    }複製代碼

    若是輸出結果是true,則是小端模式,不然是大端模式。


    3.請簡述在你所熟悉的處理器(好比雙核Cortex-A9)中一條存儲讀寫指令的執行全過程。

    經典處理器架構的流水線是五級流水線:取指、譯碼、發射、執行和寫回。


    現代處理器在設計上都採用了超標量體系結構(Superscalar Architecture)和亂序執行(out-of-order)技術,極大地提升了處理器計算能力。超標量技術可以在一個時鐘週期內執行多個指令,實現指令級的並行,有效提升了ILP(Instruction Level Parallelism)指令級的並行效率,同時也增長了整個cache和memory層次結構的實現難度。


    一條存儲讀寫指令的執行全過程很難用一句話來回答。在一個支持超標量和亂序執行技術的處理器當中,一條存儲讀寫指令的執行過程被分解爲若干步驟。指令首先進入流水線(pipeline)的前端(Front-End),包括預取(fetch)和譯碼(decode),通過分發(dispatch)和調度(scheduler)後進入執行單元,最後提交執行結果。全部的指令採用順序方式(In-Order)經過前端,並採用亂序的方式(Out-of-Order,OOO)進行發射,而後亂序執行,最後用順序方式提交結果,並將最終結果更新到LSQ(Load-Store Queue)部件。LSQ部件是指令流水線的一個執行部件,能夠理解爲存儲子系統的最高層,其上接收來自CPU的存儲器指令,其下鏈接着存儲器子系統。其主要功能是未來自CPU的存儲器請求發送到存儲器子系統,並處理其下存儲器子系統的應答數據和消息。


    不少程序員對亂序執行的理解有偏差。對於一串給定的指令序列,爲了提升效率,處理器會找出非真正數據依賴和地址依賴的指令,讓它們並行執行。可是在提交執行結果時,是按照指令次序的。總的來講,順序提交指令,亂序執行,最後順序提交結果。例若有兩條沒有數據依賴的數據指令,後面那條指令的讀數據先被返回,它的結果也不能先寫回到最終寄存器,而是必須等到前一條指令完成以後才能夠。


    對於讀指令,當處理器在等待數據從緩存或者內存返回時,它處於什麼狀態呢?是等在那不動,仍是繼續執行別的指令?對於亂序執行的處理器,能夠執行後面的指令;對於順序執行的處理器,會使流水線停頓,直到讀取的數據返回。


    如圖1.1所示,在x86微處理器經典架構中,存儲指令從L1指令cache中讀取指令,L1指令cache會作指令加載、指令預取、指令預解碼,以及分支預測。而後進入Fetch & Decode單元,會把指令解碼成macro-ops微操做指令,而後由Dispatch部件分發到Integer Unit或者FloatPoint Unit。Integer Unit由Integer Scheduler和Execution Unit組成,Execution Unit包含算術邏輯單元(arithmetic-logic unit,ALU)和地址生成單元(address generation unit,AGU),在ALU計算完成以後進入AGU,計算有效地址完畢後,將結果發送到LSQ部件。LSQ部件首先根據處理器系統要求的內存一致性(memory consistency)模型肯定訪問時序,另外LSQ還須要處理存儲器指令間的依賴關係,最後LSQ須要準備L1 cache使用的地址,包括有效地址的計算和虛實地址轉換,將地址發送到L1 Data Cache中。



    圖1.1 x86微處理器經典架構圖


    如圖1.2所示,在ARM Cortex-A9處理器中,存儲指令首先經過主存儲器或者L2 cache加載到L1指令cache中。在指令預取階段(instruction prefetch stage),主要是作指令預取和分支預測,而後指令經過Instruction Queue隊列被送到解碼器進行指令的解碼工做。解碼器(decode)支持兩路解碼,能夠同時解碼兩條指令。在寄存器重名階段(Register rename stage)會作寄存器重命名,避免機器指令沒必要要的順序化操做,提升處理器的指令級並行能力。在指令分發階段(Dispatch stage),這裏支持4路猜想發射和亂序執行(Out-of-Order Multi-Issue with Speculation),而後在執行單元(ALU/MUL/FPU/NEON)中亂序執行。存儲指令會計算有效地址併發射到內存系統中的LSU部件(Load Store Unit),最終LSU部件會去訪問L1數據cache。在ARM中,只有cacheable的內存地址才須要訪問cache。



    圖1.2 Cortex-A9結構框圖[3]


    在多處理器環境下,還須要考慮Cache的一致性問題。L1和L2 Cache控制器須要保證cache的一致性,在Cortex-A9中cache的一致性是由MESI協議來實現的。Cortex-A9處理器內置了L1 Cache模塊,由SCU(Snoop Control Unit)單元來實現Cache的一致性管理。L2 Cache須要外接芯片(例如PL310)。在最糟糕狀況下須要訪問主存儲器,並將數據從新傳遞給LSQ,完成一次存儲器讀寫的全過程。


    這裏涉及計算機體系結構中的衆多術語,比較晦澀難懂,如今對部分術語作簡單解釋。



    • 超標量體系結構(Superscalar Architecture):早期的單發射結構微處理器的流水線設計目標是作到每一個週期能平均執行一條指令,但這一目標不能知足處理器性能增加的要求,爲了提升處理器的性能,要求處理器具備每一個週期能發射執行多條指令的能力。所以超標量體系結構是描述一種微處理器設計理念,它可以在一個時鐘週期執行多個指令。

    • 亂序執行(Out-of-order Execution):指CPU採用了容許將多條指令不按程序規定的順序分開發送給各相應電路單元處理的技術,避免處理器在計算對象不可獲取時的等待,從而致使流水線停頓。

    • 寄存器重命名(Register Rename):現代處理器的一種技術,用來避免機器指令或者微操做的沒必要要的順序化執行,從而提升處理器的指令級並行的能力。它在亂序執行的流水線中有兩個做用,一是消除指令之間的寄存器讀後寫相關(Write-after-Read,WAR)和寫後寫相關(Write-after-Write,WAW);二是當指令執行發生例外或者轉移指令猜想錯誤而取消後面的指令時,可用來保證現場的精確。其思路爲當一條指令寫一個結果寄存器時不直接寫到這個結果寄存器,而是先寫到一箇中間寄存器過渡,當這條指令提交時再寫到結果寄存器中。

    • 分支預測(Branch Predictor):當處理一個分支指令時,有可能會產生跳轉,從而打斷流水線指令的處理,由於處理器沒法肯定該指令的下一條指令,直到分支指令執行完畢。流水線越長,處理器等待時間便越長,分支預測技術就是爲了解決這一問題而出現的。所以,分支預測是處理器在程序分支指令執行前預測其結果的一種機制。在ARM中,使用全局分支預測器,該預測器由轉移目標緩衝器(Branch Target Buffer,BTB)、全局歷史緩衝器(Global History Buffer,GHB)、MicroBTB,以及Return Stack組成。

    • 指令譯碼器(Instruction Decode):指令由操做碼和地址碼組成。操做碼錶示要執行的操做性質,即執行什麼操做;地址碼是操做碼執行時的操做對象的地址。計算機執行一條指定的指令時,必須首先分析這條指令的操做碼是什麼,以決定操做的性質和方法,而後才能控制計算機其餘各部件協同完成指令表達的功能,這個分析工做由譯碼器來完成。例如,Cortex-A57能夠支持3路譯碼器,即同時執行3條指令譯碼,而Cortex-A9處理器只能同時譯碼2條指令。

    • 調度單元(Dispatch):調度器負責把指令或微操做指令派發到相應的執行單元去執行,例如,Cortex-A9處理器的調度器單元有4個接口和執行單元鏈接,所以每一個週期能夠同時派發4條指令。

    • ALU算術邏輯單元:ALU是處理器的執行單元,主要是進行算術運算,邏輯運算和關係運算的部件。

    • LSQ/LSU部件(Load Store Queue/Unit):LSQ部件是指令流水線的一個執行部件,其主要功能是未來自CPU的存儲器請求發送到存儲器子系統,並處理其下存儲器子系統的應答數據和消息。


    4.請簡述內存屏障(memory barrier)產生的緣由。

    程序在運行時的實際內存訪問順序和程序代碼編寫的訪問順序不一致,會致使內存亂序訪問。內存亂序訪問的出現是爲了提升程序運行時的性能。內存亂序訪問主要發生在以下兩個階段。


    (1)編譯時,編譯器優化致使內存亂序訪問。


    (2)運行時,多CPU間交互引發的內存亂序訪問。


    編譯器會把符合人類思考的邏輯代碼(例如C語言)翻譯成CPU運算規則的彙編指令,編譯器瞭解底層CPU的思惟邏輯,所以它會在翻譯成彙編時進行優化。例如內存訪問指令的從新排序,提升指令級並行效率。然而,這些優化可能會違背程序員原始的代碼邏輯,致使發生一些錯誤。編譯時的亂序訪問能夠經過volatile關鍵字來規避。


    #define barrier() asm volatile ("" ::: "memory")複製代碼

    barrier()函數告訴編譯器,不要爲了性能優化而將這些代碼重排。


    因爲現代處理器廣泛採用超標量技術、亂序發射以及亂序執行等技術來提升指令級並行的效率,所以指令的執行序列在處理器的流水線中有可能被打亂,與程序代碼編寫時序列的不一致。另外現代處理器採用多級存儲結構,如何保證處理器對存儲子系統訪問的正確性也是一大挑戰。


    例如,在一個系統中含有n個處理器P1~Pn,假設每一個處理器包含Si個存儲器操做,那麼從全局來看可能的存儲器訪問序列有多種組合。爲了保證內存訪問的一致性,須要按照某種規則來選出合適的組合,這個規則叫作內存一致性模型(Memory Consistency Model)。這個規則須要保證正確性的前提,同時也要保證多處理器訪問較高的並行度。


    在一個單核處理器系統中,訪問內存的正確性比較簡單。每次存儲器讀操做所得到的結果是最近寫入的結果,可是在多處理器併發訪問存儲器的狀況下就很難保證其正確性了。咱們很容易想到使用一個全局時間比例部件(Global Time Scale)來決定存儲器訪問時序,從而判斷最近訪問的數據。這種內存一致性訪問模型是嚴格一致性(Strict Consistency)內存模型,也稱爲Atomic Consistency。全局時間比例方法實現的代價比較大,那麼退而求其次,採用每個處理器的本地時間比例部件(Local Time Scale)的方法來肯定最新數據的方法被稱爲順序一致性內存模型(Sequential Consistency)。處理器一致性內存模型(Processor Consistency)是進一步弱化,僅要求來自同一個處理器的寫操做具備一致性的訪問便可。


    以上這些內存一致性模型是針對存儲器讀寫指令展開的,還有一類目前普遍使用的模型,這些模型使用內存同步指令,也稱爲內存屏障指令。在這種模型下,存儲器訪問指令被分紅數據指令和同步指令兩大類,弱一致性內存模型(weak consistency)就是基於這種思想的。


    1986年,Dubois等發表的論文描述了弱一致性內存模型的定義。



    • 對同步變量的訪問是順序一致的。

    • 在全部以前的寫操做完成以前,不能訪問同步變量。

    • 在全部以前同步變量的訪問完成以前,不能訪問(讀或者寫)數據。


    弱一致性內存模型要求同步訪問是順序一致的,在一個同步訪問能夠被執行以前,全部以前的數據訪問必須完成。在一個正常的數據訪問能夠被執行以前,全部以前的同步訪問必須完成。這實質上把一致性問題留給了程序員來決定。


    ARM的Cortex-A系列處理器實現弱一致性內存模型,同時也提供了3條內存屏障指令。


    5.ARM有幾條memory barrier的指令?分別有什麼區別?

    從ARMv7指令集開始,ARM提供3條內存屏障指令。


    (1)數據存儲屏障(Data Memory Barrier,DMB)


    數據存儲器隔離。DMB指令保證:僅當全部在它前面的存儲器訪問操做都執行完畢後,才提交(commit)在它後面的存取訪問操做指令。當位於此指令前的全部內存訪問均完成時,DMB指令纔會完成。


    (2)數據同步屏障(Data synchronization Barrier,DSB)


    數據同步隔離。比DMB要嚴格一些,僅當全部在它前面的存儲訪問操做指令都執行完畢後,纔會執行在它後面的指令,即任何指令都要等待DSB前面的存儲訪問完成。位於此指令前的全部緩存,如分支預測和TLB(Translation Look-aside Buffer)維護操做所有完成。


    (3)指令同步屏障(Instruction synchronization Barrier,ISB)


    指令同步隔離。它最嚴格,沖洗流水線(Flush Pipeline)和預取buffers(pretcLbuffers)後,纔會從cache或者內存中預取ISB指令以後的指令。ISB一般用來保證上下文切換的效果,例如更改ASID(Address Space Identifier)、TLB維護操做和C15寄存器的修改等。


    內存屏障指令的使用例子以下。


    例1:假設有兩個CPU核A和B,同時訪問Addr1和Addr2地址。


    Core A:
    STR R0, [Addr1]
    LDR R1, [Addr2]

    Core B:
    STR R2, [Addr2]
    LDR R3, [Addr1]複製代碼

    對於上面代碼片斷,沒有任何的同步措施。對於Core A、寄存器R一、Core B和寄存器R3,可能獲得以下4種不一樣的結果。



    • A獲得舊的值,B也獲得舊的值。

    • A獲得舊的值,B獲得新的值。

    • A獲得新的值,B獲得舊的值。

    • A獲得新的值,B獲得新的值。


    例2:假設Core A寫入新數據到Msg地址,Core B須要判斷flag標誌後纔讀入新數據。


    Core A:
    STR R0, [Msg] @ 寫新數據到Msg地址
    STR R1, [Flag] @ Flag標誌新數據能夠讀

    Core B:
    Poll_loop:
    LDR R1, [Flag]
    CMP R1,#0 @ 判斷flag有沒有置位
    BEQ Poll_loop
    LDR R0, [Msg] @ 讀取新數據複製代碼

    在上面的代碼片斷中,Core B可能讀不到最新的數據,由於Core B可能由於亂序執行的緣由先讀入Msg,而後讀取Flag。在弱一致性內存模型中,處理器不知道Msg和Flag存在數據依賴性,因此程序員必須使用內存屏障指令來顯式地告訴處理器這兩個變量有數據依賴關係。Core A須要在兩個存儲指令之間插入DMB指令來保證兩個store存儲指令的執行順序。Core B須要在「LDR R0, [Msg]」以前插入DMB指令來保證直到Flag置位纔讀入Msg。


    例3:在一個設備驅動中,寫入一個命令到一個外設寄存器中,而後等待狀態的變化。


    STR R0, [Addr]        @ 寫一個命令到外設寄存器
    DSB
    Poll_loop:
    LDR R1, [Flag]
    CMP R1,#0 @ 等待狀態寄存器的變化
    BEQ Poll_loop複製代碼

    在STR存儲指令以後插入DSB指令,強制讓寫命令完成,而後執行讀取Flag的判斷循環。


    6.請簡述cache的工做方式。

    處理器訪問主存儲器使用地址編碼方式。cache也使用相似的地址編碼方式,所以處理器使用這些編碼地址能夠訪問各級cache。如圖1.3所示,是一個經典的cache架構圖。



    圖1.3 經典cache架構


    處理器在訪問存儲器時,會把地址同時傳遞給TLB(Translation Lookaside Buffer)和cache。TLB是一個用於存儲虛擬地址到物理地址轉換的小緩存,處理器先使用EPN(effective page number)在TLB中進行查找最終的RPN(Real Page Number)。若是這期間發生TLB miss,將會帶來一系列嚴重的系統懲罰,處理器須要查詢頁表。假設這裏TLB Hit,此時很快得到合適的RPN,並獲得相應的物理地址(Physical Address,PA)。


    同時,處理器經過cache編碼地址的索引域(Cache Line Index)能夠很快找到相應的cache line組。可是這裏的cache block的數據不必定是處理器所須要的,所以有必要進行一些檢查,將cache line中存放的地址和經過虛實地址轉換獲得的物理地址進行比較。若是相同而且狀態位匹配,那麼就會發生cache命中(Cache Hit),那麼處理器通過字節選擇和偏移(Byte Select and Align)部件,最終就能夠獲取所須要的數據。若是發生cache miss,處理器須要用物理地址進一步訪問主存儲器來得到最終數據,數據也會填充到相應的cache line中。上述描述的是VIPT(virtual Index phg sical Tag)的cache組織方式,將會在問題9中詳細介紹。


    如圖1.4所示,是cache的基本的結構圖。


    圖1.4 cache結構圖



    • cache地址編碼:處理器訪問cache時的地址編碼,分紅3個部分,分別是偏移域(Offset)、索引域(Index)和標記域(Tag)。

    • Cache Line:cache中最小的訪問單元,包含一小段主存儲器中的數據,常見的cache line大小是32Byte或64Byte等。

    • 索引域(Index):cache地址編碼的一部分,用於索引和查找是在cache中的哪一行。

    • 組(Set):相同索引域的cache line組成一個組。

    • 路(Way):在組相聯的cache中,cache被分紅大小相同的幾個塊。

    • 標記(Tag):cache地址編碼的一部分,用於判斷cache line存放的數據是否和處理器想要的一致。


    7.cache的映射方式有full-associative(全關聯)、direct-mapping(直接映射)和set-associative(組相聯)3種方式,請簡述它們之間的區別。爲何現代的處理器都使用組相聯的cache映射方式?

    (1)直接映射(Direct-mapping)


    根據每一個組(set)的高速緩存行數,cache能夠分紅不一樣的類。當每一個組只有一行cache line時,稱爲直接映射高速緩存。


    如圖1.5所示,下面用一個簡單小巧的cache來講明,這個cache只有4行cache line,每行有4個字(word,一個字是4個Byte),共64 Byte。這個cache控制器可使用兩個比特位(bits[3:2])來選擇cache line中的字,以及使用另外兩個比特位(bits[5:4])做爲索引(Index),選擇4個cache line中的一個,其他的比特位用於存儲標記值(Tag)。


    在這個cache中查詢,當索引域和標記域的值和查詢的地址相等,而且有效位顯示這個cache line包含有效數據時,則發生cache命中,那麼可使用偏移域來尋址cache line中的數據。若是cache line包含有效數據,可是標記域是其餘地址的值,那麼這個cache line須要被替換。所以,在這個cache中,主存儲器中全部bit [5:4]相同值的地址都會映射到同一個cache line中,而且同一時刻只有一個cache line,由於cache line被頻繁換入換出,會致使嚴重的cache顛簸(cache thrashing)。




    圖1.5 直接眏射的cache和cache地址


    假設在下面的代碼片斷中,result、data1和data2分別指向0x00、0x40和0x80地址,它們都會使用同一個cache line。


    void add_array(int data1, int data2, int *result, int size)
    {
    int i;
    for (i=0 ; i<size ; i++) {
    result[i] = data1[i] + data2[i];
    }
    }複製代碼


    • 當第一次讀data1即0x40地址時,由於不在cache裏面,因此讀取從0x40到0x4f地址的數據填充到cache line中。

    • 當讀data2即0x80地址的數據時,數據不在cache line中,須要把從0x80到0x8f地址的數據填充到cache line中,由於地址0x80和0x40映射到同一個cache line,因此cache line發生替換操做。

    • result寫入到0x00地址時,一樣發生了cache line替換操做。

    • 因此這個代碼片斷髮生嚴重的cache顛簸,性能會很糟糕。


    (2)組相聯(set associative)


    爲了解決直接映射高速緩存中的cache顛簸問題,組相聯的cache結構在現代處理器中獲得普遍應用。


    如圖1.6所示,下面以一個2路組相聯的cache爲例,每一個路(way)包括4個cache line,那麼每一個組(set)有兩個cache line能夠提供cache line替換。



    圖1.6 2路組相聯的映射關係


    地址0x00、0x40或者0x80的數據能夠映射到同一個組中任意一個cache line。當cache line要發生替換操做時,就有50%的機率能夠不被替換,從而減少了cache顛簸。


    8.在一個32KB的4路組相聯的cache中,其中cache line爲32Byte,請畫出這個cache的cache line、way和set的示意圖。

    在Cortex-A7和Cortex-A9的處理器上能夠看到32KB 大小的4路組相聯cache。下面來分析這個cache的結構圖。


    cache的總大小爲32KB,而且是4路(way),因此每一路的大小爲8KB:


    way_size = 32 / 4 = 8(KB)


    cache Line的大小爲32Byte,因此每一路包含的cache line數量爲:


    num_cache_line = 8KB/32B = 256


    因此在cache編碼地址Address中,bit[4:0]用於選擇cache line中的數據,其中bit [4:2]能夠用於尋址8個字,bit [1:0]能夠用於尋址每一個字中的字節。bit [12:5]用於索引(Index)選擇每一路上cache line,其他的bit [31:13]用做標記位(Tag),如圖1.7所示。


    9.ARM9處理器的Data Cache組織方式使用的VIVT,即虛擬Index虛擬Tag,而在Cortex-A7處理器中使用PIPT,即物理Index物理Tag,請簡述PIPT比VIVT有什麼優點?

    處理器在進行存儲器訪問時,處理器訪問地址是虛擬地址(virtual address,VA),通過TLB和MMU的映射,最終變成了物理地址(physical address,PA)。那麼查詢cache組是用虛擬地址,仍是物理地址的索引域(Index)呢?當找到cache組時,咱們是用虛擬地址,仍是物理地址的標記域(Tag)來匹配cache line呢?


    cache能夠設計成經過虛擬地址或者物理地址來訪問,這個在處理器設計時就肯定下來了,而且對cache的管理有很大的影響。cache能夠分紅以下3類。



    • VIVT(Virtual Index Virtual Tag):使用虛擬地址索引域和虛擬地址的標記域。

    • VIPT(Virtual Index Physical Tag):使用虛擬地址索引域和物理地址的標記域。



    圖1.7 32KB 4路組相聯cache結構圖



    • PIPT(Physical Index Physical Tag):使用物理地址索引域和物理地址的標記域。


    在早期的ARM處理器中(好比ARM9處理器)採用VIVT的方式,不用通過MMU的翻譯,直接使用虛擬地址的索引域和標記域來查找cache line,這種方式會致使高速緩存別名(cache alias)問題。例如一個物理地址的內容能夠出如今多個cache line中,當系統改變了虛擬地址到物理地址映射時,須要清洗(clean)和無效(invalidate)這些cache,致使系統性能降低。


    ARM11系列處理器採用VIPT方式,即處理器輸出的虛擬地址同時會發送到TLB/MMU單元進行地址翻譯,以及在cache中進行索引和查詢cache組。這樣cache和TLB/MMU能夠同時工做,當TLB/MMU完成地址翻譯後,再用物理標記域來匹配cache line。採用VIPT方式的好處之一是在多任務操做系統中,修改了虛擬地址到物理地址映射關係,不須要把相應的cache進行無效(invalidate)操做。


    ARM Cortex-A系列處理器的數據cache開始採用PIPT的方式。對於PIPT方式,索引域和標記域都採用物理地址,cache中只有一個cache組與之對應,不會產生高速緩存別名的問題。PIPT的方式在芯片設計裏的邏輯比VIPT要複雜得多。


    採用VIPT方式也有可能致使高速緩存別名的問題。在VIPT中,使用虛擬地址的索引域來查找cache組,這時有可能致使多個cache組映射到同一個物理地址上。以Linux kernel爲例,它是以4KB大小爲一個頁面進行管理的,那麼對於一個頁來講,虛擬地址和物理地址的低12bit(bit [11:0])是同樣的。所以,不一樣的虛擬地址映射到同一個物理地址,這些虛擬頁面的低12位是同樣的。若是索引域位於bit [11:0]範圍內,那麼就不會發生高速緩存別名。例如,cache line是32Byte,那麼數據偏移域offset佔5bit,有128個cache組,那麼索引域佔7bit,這種狀況下恰好不會發生別名。另外,對於ARM Cortex-A系列處理器來講,cache總大小是能夠在芯片集成中配置的。如表1.1所示,列舉出了Cortex-A系列處理器的cache配置狀況。


    表1.1 ARM處理器的cache概況
























































     

    Cortex-A7

    Cortex-A9

    Cortex-A15

    Cortex-A53

    數據緩存實現方式

    PIPT

    PIPT

    PIPT

    PIPT

    指令緩存實現方式

    VIPT

    VIPT

    PIPT

    VIPT

    L1數據緩存大小

    8KB~64KB

    16KB/32KB/64KB

    32KB

    8KB~64KB

    L1數據緩存結構

    4路組相聯

    4路組相聯

    2路組相聯

    4路組相聯

    L2緩存大小

    128KB~1MB

    External

    512KB~4MB

    128KB~2MB

    L2緩存結構

    8路組相聯

    External

    16路組相聯

    16路組相聯


    10.請畫出在二級頁表架構中虛擬地址到物理地址查詢頁表的過程。

    如圖1.8所示,ARM處理器的內存管理單元(Memory Management Unit, MMU)包括TLB和Table Walk Unit兩個部件。TLB是一塊高速緩存,用於緩存頁錶轉換的結果,從而減小內存訪問的時間。一個完整的頁表翻譯和查找的過程叫做頁表查詢(Translation table walk),頁表查詢的過程由硬件自動完成,可是頁表的維護須要軟件來完成。頁表查詢是一個相對耗時的過程,理想的狀態下是TLB裏存有頁表相關信息。當TLB Miss時,纔會去查詢頁表,而且開始讀入頁表的內容。



    圖1.8 ARM內存管理架構


    (1)ARMv7-A架構的頁表


    ARMv7-A架構支持安全擴展(Security Extensions),其中Cortex-A15開始支持大物理地址擴展(Large Physical Address Extension,LPAE)和虛擬化擴展,使得MMU的實現比之前的ARM處理器要複雜得多。


    如圖1.9所示,若是使能了安全擴展,ARMv7-A處理器分紅安全世界(Secure World)和非安全世界(Non-secure World,也稱爲Normal World)。



    圖1.9 ARMv7-A架構的運行模式和特權


    若是處理器使能了虛擬化擴展,那麼處理器會在非安全世界中增長一個Hyp模式。


    在非安全世界中,運行特權被劃分爲PL0、PL1和PL2。



    • PL0等級:這個特權等級運行在用戶模式(User Mode),用於運行用戶程序,它是沒有系統特權的,好比沒有權限訪問處理器內部的硬件資源。

    • PL1等級:這個等級包括ARMv6架構中的System模式、SVC模式、FIQ模式、IRQ模式、Undef模式,以及Abort模式。Linux內核運行在PL1等級,應用程序運行在PL0等級。若是使能了安全擴展,那麼安全模式裏有一個Monitor模式也是運行在secure PL1等級,管理安全世界和非安全世界的狀態轉換。

    • PL2等級:若是使能了虛擬化擴展,那麼超級管理程序(Hypervisor)就運行這個等級,它運行在Hyp模式,管理GuestOS之間的切換。


    當處理器使能了虛擬化擴展,MMU的工做會變得更復雜。咱們這裏只討論處理器沒有使能安全擴展和虛擬化擴展的狀況。ARMv7處理器的二級頁表根據最終頁的大小能夠分爲以下4種狀況。



    • 超級大段(SuperSection):支持16MB大小的超級大塊。

    • 段(section):支持1MB大小的段。

    • 大頁面(Large page):支持64KB大小的大頁。

    • 頁面(page):4KB的頁,Linux內核默認使用4KB的頁。


    若是隻須要支持超級大段和段映射,那麼只須要一級頁表便可。若是要支持4KB頁面或64KB大頁映射,那麼須要用到二級頁表。不一樣大小的映射,一級或二級頁表中的頁表項的內容也不同。如圖1.10所示,以4KB頁的映射爲例。



    圖1.10 ARMv7-A二級頁表查詢過程


    當TLB Miss時,處理器查詢頁表的過程以下。



    • 處理器根據頁表基地址控制寄存器TTBCR和虛擬地址來判斷使用哪一個頁表基地址寄存器,是TTBR0仍是TTBR1。頁表基地址寄存器中存放着一級頁表的基地址。

    • 處理器根據虛擬地址的bit[31:20]做爲索引值,在一級頁表中找到頁表項,一級頁表一共有4096個頁表項。

    • 第一級頁表的表項中存放有二級頁表的物理基地址。處理器根據虛擬地址的bit[19:12]做爲索引值,在二級頁表中找到相應的頁表項,二級頁表有256個頁表項。

    • 二級頁表的頁表項裏存放有4KB頁的物理基地址,所以處理器就完成了頁表的查詢和翻譯工做。


    如圖 1.11 所示的4KB映射的一級頁表的表項,bit[1:0]表示是一個頁映射的表項,bit[31:10]指向二級頁表的物理基地址。



    圖1.11 4KB映射的一級頁表的表項


    如圖1.12所示的4KB映射的二級頁表的表項,bit[31:12]指向4KB大小的頁面的物理基地址。



    圖1.12 4KB映射的二級頁表的表項


    (2)ARMv8-A架構的頁表


    ARMv8-A架構開始支持64bit操做系統。從ARMv8-A架構的處理器能夠同時支持64bit和32bit應用程序,爲了兼容ARMv7-A指令集,從架構上定義了AArch64架構和AArch32架構。


    AArch64架構和ARMv7-A架構同樣支持安全擴展和虛擬化擴展。安全擴展把ARM的世界分紅了安全世界和非安全世界。AArch64架構的異常等級(Exception Levels)肯定其運行特權級別,相似ARMv7架構中特權等級,如圖1.13所示。



    • EL0:用戶特權,用於運行普通用戶程序。

    • EL1:系統特權,一般用於運行操做系統。

    • EL2:運行虛擬化擴展的Hypervisor。

    • EL3:運行安全世界中的Secure Monitor。


    在AArch64架構中的MMU支持單一階段的地址頁錶轉換,一樣也支持虛擬化擴展中的兩階段的頁錶轉換。



    • 單一階段頁表:虛擬地址(VA)翻譯成物理地址(PA)。

    • 兩階段頁表(虛擬化擴展):



    圖1.13 AArch64架構的異常等級


    階段1——虛擬地址翻譯成中間物理地址(Intermediate Physical Address,IPA)。


    階段2——中間物理地址IPA翻譯成最終物理地址PA。


    在AArch64架構中,由於地址總線帶寬最多48位,因此虛擬地址VA被劃分爲兩個空間,每一個空間最大支持256TB。



    • 低位的虛擬地址空間位於0x0000_0000_0000_0000到0x0000_FFFF_FFFF_FFFF。若是虛擬地址最高位bit63等於0,那麼就使用這個虛擬地址空間,而且使用TTBR0 (Translation Table Base Register)來存放頁表的基地址。

    • 高位的虛擬地址空間位於0xFFFF_0000_0000_0000到0xFFFF_FFFF_FFFF_FFFF。 若是虛擬地址最高位bit63等於1,那麼就使用這個虛擬地址空間,而且使用TTBR1來存放頁表的基地址。


    如圖1.14所示,AArch64架構處理地址映射圖,其中頁面是4KB的小頁面。AArch64架構中的頁表支持以下特性。



    圖1.14 AArch64架構地址映射圖(4KB頁)



    • 最多能夠支持4級頁表。

    • 輸入地址最大有效位寬48bit。

    • 輸出地址最大有效位寬48bit。

    • 翻譯的最小粒度能夠是4KB、16KB或64KB。


    11.在多核處理器中,cache的一致性是如何實現的?請簡述MESI協議的含義。

    高速緩存一致性(cache coherency)產生的緣由是在一個處理器系統中不一樣CPU核上的數據cache和內存可能具備同一個數據的多個副本,在僅有一個CPU核的系統中不存在一致性問題。維護cache一致性的關鍵是跟蹤每個cache line的狀態,並根據處理器的讀寫操做和總線上的相應傳輸來更新cache line在不一樣CPU核上的數據cache中的狀態,從而維護cache一致性。cache一致性有軟件和硬件兩種方式,有的處理器架構提供顯式操做cache的指令,例如PowerPC,不過如今大多數處理器架構採用硬件方式來維護。在處理器中經過cache一致性協議來實現,這些協議維護一個有限狀態機(Finite State Machine,FSM),根據存儲器讀寫指令或總線上的傳輸,進行狀態遷移和相應的cache操做來保證cache一致性,不須要軟件介入。


    cache一致性協議主要有兩大類別,一類是監聽協議(Snooping Protocol),每一個cache都要被監聽或者監聽其餘cache的總線活動;另一類是目錄協議(Directory Protocol),全局統一管理cache狀態。


    1983年,James Goodman提出Write-Once總線監聽協議,後來演變成目前最流行的MESI協議。總線監聽協議依賴於這樣的事實,即全部的總線傳輸事務對於系統內全部的其餘單元是可見的,由於總線是一個基於廣播通訊的介質,於是能夠由每一個處理器的cache來進行監聽。這些年來人們已經提出了數十種協議,這些協議基本上都是write-once協議的變種。不一樣的協議須要不一樣的通訊量,要求太多的通訊量會浪費總線帶寬,使總線爭用變多,留下來給其餘部件使用的帶寬就減小。所以,芯片設計人員嘗試將保持一致性的協議所須要的總線通訊量減小到最小,或者嘗試優化某些頻繁執行的操做。


    目前,ARM或x86等處理器普遍使用相似MESI協議來維護cache一致性。MESI協議的得名源於該協議使用的修改態(Modified)、獨佔態(Exclusive)、共享態(Shared)和失效態(Invalid)這4個狀態。cache line中的狀態必須是上述4種狀態中的一種。MESI協議還有一些變種,例如MOESI協議等,部分的ARMv7-A和ARMv8-A處理器使用該變種。


    cache line中有兩個標誌:dirty和valid。它們很好地描述了cache和內存之間的數據關係,例如數據是否有效、數據是否被修改過。在MESI協議中,每一個cache line有4個狀態,可用2bit來表示。


    如表1.2和表1.3所示,分別是MESI協議4個狀態的說明和MESI協議各個狀態的轉換關係。


    表1.2 MESI協議定義



























    狀態

    描述

    M(修改態)

    這行數據有效,數據被修改,和內存中的數據不一致,數據只存在本cache中

    E(獨佔態)

    這行數據有效,數據和內存中數據一致,數據只存在於本cache中

    S(共享態)

    這行數據有效,數據和內存中數據一致,多個cache有這個數據副本

    I(無效態)

    這行數據無效


    表1.3 MESI狀態說明





































































































    當前狀態

    操做

    響應

    遷移狀態

    修改態M

    總線讀

    Flush該cache line到內存,以便其餘CPU能夠訪問到最新的內容,狀態變成S態

    S

    總線寫

    Flush該cache line到內存,而後其餘CPU修改cache line,所以本cache line執行清空數據操做,狀態變成I態

    I

    處理器讀

    本地處理器讀該cache line,狀態不變

    M

    處理器寫

    本地處理器寫該cache line,狀態不變

    M

    獨佔態E

    總線讀

    獨佔狀態的cache line是乾淨的,所以狀態變成S

    S

    總線寫

    數據被修改,該cache line不能再使用了,狀態變成I

    I

    本地讀

    從該cache line中取數據,狀態不變

    E

    本地寫

    修改該cache line數據,狀態變成M

    M

    共享態S

    總線讀

    狀態不變

    S

    總線寫

    數據被修改,該cache line不能再使用了,狀態變成I

    I

    本地讀

    狀態不變

    S

    本地寫

    修改了該cache line數據,狀態變成M;其餘核上共享的cache line的狀態變成I

    M

    無效態I

    總線讀

    狀態不變

    I

    總線寫

    狀態不變

    I

    本地讀

    ● 若是cache miss,則從內存中取數據,cache line變成E;

    ● 若是其餘cache有這份數據,且狀態爲M,則將數據更新到內存,本cache再從內存中取數據,兩個cache line的狀態都爲S;

    ● 若是其餘cache有這份數據,且狀態是S或E,本cache從內存中取數據,這些cache line都變成S

    E/S

    本地寫

    ● 若是cache miss,從內存中取數據,在cache中修改,狀態變成M;

    ● 若是其餘cache有這份數據,且狀態爲M,則要先將數據更新到內存,其餘cache line狀態變成I,而後修改本cache line的內容

    M

    • 修改和獨佔狀態的cache line,數據都是獨有的,不一樣點在於修改狀態的數據是髒的,和內存不一致,而獨佔態的數據是乾淨的和內存一致。擁有修改態的cache line會在某個合適的時候把該cache line寫回內存中,其後的狀態變成共享態。
    • 共享狀態的cache line,數據和其餘cache共享,只有乾淨的數據才能被多個cache共享。
    • I的狀態表示這個cache line無效。

    • MOESI協議增長了一個O(Owned)狀態,並在MESI協議的基礎上從新定義了S狀態,而E、M和I狀態與MESI協議的對應狀態相同。



      • O位。O位爲1,表示在當前cache 行中包含的數據是當前處理器系統最新的數據複製,並且在其餘CPU中可能具備該cache行的副本,狀態爲S。若是主存儲器的數據在多個CPU的cache中都具備副本時,有且僅有一個CPU的Cache行狀態爲O,其餘CPU的cache行狀態只能爲S。與MESI協議中的S狀態不一樣,狀態爲O的cache行中的數據與存儲器中的數據並不一致。

      • S位。在MOESI協議中,S狀態的定義發生了細微的變化。當一個cache行狀態爲S時,其包含的數據並不必定與存儲器一致。若是在其餘CPU的cache中不存在狀態爲O的副本時,該cache行中的數據與存儲器一致;若是在其餘CPU的cache中存在狀態爲O的副本時,cache行中的數據與存儲器不一致。


      12.cache在Linux內核中有哪些應用?

      cache line的空間都很小,通常也就32 Byte。CPU的cache是線性排列的,也就是說一個32 Byte的cache line與32 Byte的地址對齊,另外相鄰的地址會在不一樣的cache line中錯開,這裏是指32n的相鄰地址。


      cache在linux內核中有不少巧妙的應用,讀者能夠在閱讀本書後面章節遇到相似的狀況時細細體會,暫時先總結概括以下。


      (1)內核中經常使用的數據結構一般是和L1 cache對齊的。例如,mm_struct、fs_cache等數據結構使用「SLAB_HWCACHE_ALIGN」標誌位來建立slab緩存描述符,見proc_caches_init()函數。


      (2)一些經常使用的數據結構在定義時就約定數據結構以L1 Cache對齊,使用「_cacheline_internodealigned_in_smp」和「_cacheline_aligned_in_smp」等宏來定義數據結構,例如struct zone、struct irqaction、softirq_vec[ ]、irq_stat[ ]、struct worker_pool等。


      cache和內存交換的最小單位是cache line,若結構體沒有和cache line對齊,那麼一個結構體有可能佔用多個cache line。假設cache line的大小是32 Byte,一個自己小於32 Byte的結構體有可能橫跨了兩條cache line,在SMP中會對系統性能有不小的影響。舉個例子,如今有結構體C1和結構體C2,緩存到L1 Cache時沒有按照cache line對齊,所以它們有可能同時佔用了一條cache line,即C1的後半部和C2的前半部在一條cache line中。根據cache 一致性協議,CPU0修改結構體C1的時會致使CPU1的cache line失效,同理,CPU1對結構體C2修改也會致使CPU0的cache line失效。若是CPU0和CPU1反覆修改,那麼會致使系統性能降低。這種現象叫作「cache line僞共享」,兩個CPU本來沒有共享訪問,由於要共同訪問同一個cache line,產生了事實上的共享。解決上述問題的一個方法是讓結構體按照cache line對齊,典型地以空間換時間。include/linux/cache.h文件定義了有關cache相關的操做,其中cacheline_aligned_in_smp的定義也在這個文件中,它和L1_CACHE_BYTES對齊。


      [include/linux/cache.h]

      #define SMP_CACHE_BYTES L1_CACHE_BYTES

      #define 複製代碼cacheline_aligned attribute ((aligned (SMP_CACHE_BYTES)))
      #define cacheline_aligned_in_smp cacheline_aligned

      #ifndef cacheline_aligned
      #define
      cacheline_aligned \
      attribute ((aligned (SMP_CACHE_BYTES), \
      section (".data..cacheline_aligned")))
      #endif /複製代碼cacheline_aligned */

      #define cacheline_aligned_in_smp
      cacheline_aligned

      #define __
      cacheline_internodealigned_in_smp \
      attribute ((aligned (1 << (INTERNODE_CACHE_SHIFT))))複製代碼



      (3)數據結構中頻繁訪問的成員能夠單獨佔用一個cache line,或者相關的成員在cache line中彼此錯開,以提升訪問效率。例如,struct zone數據結構中zone->lock和zone-> lru_lock這兩個頻繁被訪問的鎖,可讓它們各自使用不一樣的cache line,以提升獲取鎖的效率。


      再好比struct worker_pool數據結構中的nr_running成員就獨佔了一個cache line,避免多CPU同時讀寫該成員時引起其餘臨近的成員「顛簸」現象,見第5.3節。


      (4)slab的着色區,見第2.5節。


      (5)自旋鎖的實現。在多CPU系統中,自旋鎖的激烈爭用過程致使嚴重的CPU cacheline bouncing現象,見第4章關於自旋鎖的部份內容。


      13.請簡述ARM big.LITTLE架構,包括總線鏈接和cache管理等。

      ARM提出大小核概念,即big.LITTLE架構,針對性能優化過的處理器內核稱爲大核,針對低功耗待機優化過的處理器內核稱爲小核。


      如圖1.15所示,在典型big.LITTLE架構中包含了一個由大核組成的集羣(Cortex-A57)和小核(Cortex-A53)組成的集羣,每一個集羣都屬於傳統的同步頻率架構,工做在相同的頻率和電壓下。大核爲高性能核心,工做在較高的電壓和頻率下,消耗更多的能耗,適用於計算繁重的任務。常見的大核處理器有Cortex-A1五、Cortex-A5七、Cortex-A72和Cortex-A73。小核性能雖然較低,但功耗比較低,在一些計算負載不大的任務中,不用開啓大核,直接用小核便可,常見的小核處理器有Cortex-A7和Cortex-A53。



      圖1.15 典型的big.LITTLE架構


      如圖1.16所示是4核Cortex-A15和4核Cortex-A7的系統總線框圖。



      • Quad Cortex-A15:大核CPU簇。

      • Quad Cortex-A7:小核CPU簇。



      圖1.16 4核A15和4核A7的系統總線框圖



      • CCI-400模塊[4]:用於管理大小核架構中緩存一致性的互連模塊。CCI-400只能支持兩個CPU簇(cluster),而最新款的CCI-550能夠支持6個CPU簇。

      • DMC-400[5]:內存控制器。

      • NIC-400[6]:用於AMBA總線協議的鏈接,能夠支持AXI、AHB和APB總線的鏈接。

      • MMU-400[7]:系統內存管理單元。

      • Mali-T604:圖形加速控制器。

      • GIC-400:中斷控制器。


      ARM CoreLink CCI-400模塊用於維護大小核集羣的數據互聯和cache一致性。大小核集羣做爲主設備(Master),經過支持ACE協議的從設備接口(Slave)鏈接到CCI-400上,它能夠管理大小核集羣中的cache一致性和實現處理器間的數據共享。此外,它還支持3個ACE-Lite從設備接口(ACE-Lite Slave Interface),能夠支持一些IO主設備,例如GPU Mali-T604。經過ACE-Lite協議,GPU能夠監聽處理器的cache。CCI-400還支持3個ACE-Lite主設備接口,例如經過DMC-400來鏈接LP-DDR2/3或DDR內存設備,以及經過NIC-400總線來鏈接一些外設,例如DMA設備和LCD等。


      ACE協議,全稱爲AMBA AXI Coherency Extension協議,是AXI4協議的擴展協議,增長了不少特性來支持系統級硬件一致性。模塊之間共享內存不須要軟件干預,硬件直接管理和維護各個cache之間的一致性,這能夠大大減小軟件的負載,最大效率地使用cache,減小對內存的訪問,進而下降系統功耗。


      14.cache coherency和memory consistency有什麼區別?

      cache coherency高速緩存一致性關注的是同一個數據在多個cache和內存中的一致性問題,解決高速緩存一致性的方法主要是總線監聽協議,例如MESI協議等。而memory consistency關注的是處理器系統對多個地址進行存儲器訪問序列的正確性,學術上對內存訪問模型提出了不少,例如嚴格一致性內存模型、處理器一致性內存模型,以及弱一致性內存模型等。弱內存訪問模型在如今處理器中獲得普遍應用,所以內存屏障指令也獲得普遍應用。


      15.請簡述cache的write back有哪些策略。

      在處理器內核中,一條存儲器讀寫指令通過取指、譯碼、發射和執行等一系列操做以後,率先到達LSU部件。LSU部件包括Load Queue和Store Queue,是指令流水線的一個執行部件,是處理器存儲子系統的最頂層,鏈接指令流水線和cache的一個支點。存儲器讀寫指令經過LSU以後,會到達L1 cache控制器。L1 cache控制器首先發起探測(Probe)操做,對於讀操做發起cache讀探測操做並將帶回數據,寫操做發起cache寫探測操做。寫探測操做以前須要準備好待寫的cache line,探測工做返回時將會帶回數據。當存儲器寫指令得到最終數據並進行提交操做以後纔會將數據寫入,這個寫入能夠Write Through或者Write Back。


      對於寫操做,在上述的探測過程當中,若是沒有找到相應的cache block,那麼就是Write Miss,不然就是Write Hit。對於Write Miss的處理策略是Write-Allocate,即L1 cache控制器將分配一個新的cache line,以後和獲取的數據進行合併,而後寫入L1 cache中。


      若是探測的過程是Write Hit,那麼真正寫入有兩種模式。



      • Write Through(直寫模式):進行寫操做時,數據同時寫入當前的cache、下一級cache或主存儲器中。Write Through策略能夠下降cache一致性的實現難度,其最大的缺點是消耗比較多的總線帶寬。

      • Write Back(回寫模式):在進行寫操做時,數據直接寫入當前cache,而不會繼續傳遞,當該Cache Line被替換出去時,被改寫的數據纔會更新到下一級cache或主存儲器中。該策略增長了cache一致性的實現難度,可是有效下降了總線帶寬需求。


      16.請簡述cache line的替換策略。

      因爲cache的容量遠小於主存儲器,當Cache Miss發生時,不只僅意味着處理器須要從主存儲器中獲取數據,並且須要將cache的某個cache line替換出去。在cache的Tag陣列中,除了具備地址信息以外還有cache block的狀態信息。不一樣的cache一致性策略使用的cache狀態信息並不相同。在MESI協議中,一個cache block一般含有M、E、S和I這4個狀態位。


      cache的替換策略有隨機法(Random policy)、先進先出法(FIFO)和最近最少使用算法(LRU)。



      • 隨機法:隨機地肯定替換的cache block,由一個隨機數產生器來生成隨機數肯定替換塊,這種方法簡單,易於實現,但命中率比較低。

      • 先進先出法:選擇最早調入的那個cache block進行替換,最早調入的塊有可能被屢次命中,可是被優先替換,於是不符合局部性規律。

      • 最近最少使用算法:LRU算法根據各塊使用的狀況,老是選擇最近最少使用的塊來替換,這種算法較好地反映了程序局部性規律。


      在Cortex-A57處理器中,L1 cache採用LRU算法,而L2 cache採用隨機算法。在最新的Cortex-A72處理器中,L2 cache採有僞隨機算法(pseudo-random policy)或僞LRU算法(pseudo-least-recently-used policy)。


      17.多進程間頻繁切換對TLB有什麼影響?現代的處理器是如何面對這個問題的?

      在現代處理器中,軟件使用虛擬地址訪問內存,而處理器的MMU單元負責把虛擬地址轉換成物理地址,爲了完成這個映射過程,軟件和硬件共同來維護一個多級映射的頁表。當處理器發現頁表中沒法映射到對應的物理地址時,會觸發一個缺頁異常,掛起出錯的進程,操做系統軟件須要處理這個缺頁異常。咱們以前有提到過二級頁表的查詢過程,爲了完成虛擬地址到物理地址的轉換,查詢頁表須要兩次訪問內存,即一級頁表和二級頁表都是存放在內存中的。


      TLB(Translation Look-aside Buffer)專門用於緩存內存中的頁表項,通常在MMU單元內部。TLB是一個很小的cache,TLB表項(TLB entry)數量比較少,每一個TLB表項包含一個頁面的相關信息,例若有效位、虛擬頁號、修改位、物理頁幀號等。當處理器要訪問一個虛擬地址時,首先會在TLB中查詢。若是TLB表項中沒有相應的表項,稱爲TLB Miss,那麼就須要訪問頁表來計算出相應的物理地址。若是TLB表項中有相應的表項,那麼直接從TLB表項中獲取物理地址,稱爲TLB命中。


      TLB內部存放的基本單位是TLB表項,TLB容量越大,所能存放的TLB表項就越多,TLB命中率就越高,可是TLB的容量是有限的。目前Linux內核默認採用4KB大小的小頁面,若是一個程序使用512個小頁面,即2MB大小,那麼至少須要512個TLB表項才能保證不會出現TLB Miss的狀況。可是若是使用2MB大小的大頁,那麼只須要一個TLB表項就能夠保證不會出現TLB Miss的狀況。對於消耗內存以GB爲單位的大型應用程序,還可使用以1GB爲單位的大頁,從而減小TLB Miss狀況。


      18.請簡述NUMA架構的特色。

      如今絕大數ARM系統都採用UMA的內存架構(Uniform Memory Architechture),即內存是統一結構和統一尋址。對稱多處理器(Symmetric Multiple Processing,SMP)系統大部分都採用UMA內存架構。所以在UMA架構的系統中有以下特色。



      • 全部硬件資源都是共享的,每一個處理器都能訪問到系統中的內存和外設資源。

      • 全部處理器都是平等關係。

      • 統一尋址訪問內存。

      • 處理器和內存經過內部的一條總線鏈接在一塊兒。


      如圖1.17所示,SMP系統相對比較簡潔,可是缺點也很明顯。由於全部對等的處理器都經過一條總線鏈接在一塊兒,隨着處理器數量的增多,系統總線成爲系統的最大瓶頸。


      NUMA系統[8]是從SMP系統演化過來的。如圖1.18所示,NUMA系統由多個內存節點組成,整個內存體系能夠做爲一個總體,任何處理器均可以訪問,只是處理器訪問本地內存節點擁有更小的延遲和更大的帶寬,處理器訪問遠程內存節點速度要慢一些。每一個處理器除了擁有本地的內存以外,還能夠擁有本地總線,例如PCIE、STAT等。



      圖1.17 SMP架構示意圖



      圖1.18 NUMA架構示意圖


      如今的x86陣營的服務器芯片早已支持NUMA架構了,例如Intel的至強服務器。對於ARM陣營,2016年Cavium公司發佈的基於ARMv8-A架構設計的服務器芯片「ThunderX2」[9]也開始支持NUMA架構。


      19.ARM從Cortex系列開始性能有了質的飛越,好比Cortex-A8/A15/A53/A72,請說說Cortex系列在芯片設計方面作了哪些重大改進?

      計算機體系結構是一個權衡的藝術,尺有所短,寸有所長。在處理器領域經歷多年的優勝劣汰,市面上流行的處理器內核在技術上日漸趨同。


      ARM處理器在Cortex系列以後,加入了不少現代處理器的一些新技術和特性,已經具有了和Intel一較高下的能力,例如2016年發佈的Cortex-A73處理器。


      2005年發佈的Cortex-A8內核是第一個引入超標量技術的ARM處理器,它在每一個時鐘週期內能夠並行發射兩條指令,但依然使用靜態調度的流水線和順序執行方式。Cortex-A8內核採用13級整型指令流水線和10級NEON指令流水線。分支目標緩衝器(Branch Target Buffer,BTB)使用的條目數增長到512,同時設置了全局歷史緩衝器(Global History Buffer,GHB)和返回堆棧(Return Stack,RS)部件,這些措施極大地提升了指令分支預測的成功率。另外,還加入了way-prediction部件。


      2007年Cortex-A9發佈,引入了亂序執行和猜想執行機制以及擴大L2 cache的容量。


      2010年Cortex-A15發佈,最高主頻能夠到2.5GHz,最多支持8個處理器核心,單個cluster最多支持4個處理器核心,採有超標量流水線技術,具備1TB物理地址空間,支持虛擬化技術等新技術。指令預取總線寬度爲128bit,一次能夠預取4~8條指令,和Cortex-A9相比,提升了一倍。Decode部件一次能夠譯碼3條指令。Cortex-A15引入了Micro-Ops概念。Micro-ops指令和X86的uops指令想法較爲相似。在x86處理器中,指令譯碼單元把複雜的CISC指令轉換成等長的upos指令,再進入到指令流水線中;而Cortex-A15,指令譯碼單元把RISC指令進一步細化爲Micro-ops指令,以充分利用指令流水線中的多個併發執行單元。指令譯碼單元爲3路指令譯碼,在一個時鐘週期能夠同時譯碼3條指令。


      2012年發佈64位的Cortex-A53和Cortex-A57,ARM開始進軍服務器領域。Cortex-A57是首款支持64位的ARM處理器內核,採用3發亂序執行流水線(Out-of-Order pipeline),而且增長數據預取功能。


      2015年發佈Cortex-A57的升級版本Cortex-A72,如圖1.19所示。A72在A57架構的基礎上作了大量優化工做,包括新的分支預測單元,改善解碼流水線設計等。在指令分發



      圖1.19 Cortex-A72處理器架構圖[10]


      單元(Dispatch)也作了很大優化,由原來A57架構的3發射變成了5發射,同時發射5條指令,而且還支持並行執行8條微操做指令,從而提升解碼器的吞吐量。


      最新近展


      最近幾年,x86和ARM陣營都在各自領域中不斷創新。異構計算是一個很熱門的技術方向,好比Intel公司最近發佈了集成FPGA的至強服務器芯片。FPGA能夠在客戶的關鍵算法中提供可編程、高性能的加速能力,另外提供了靈活性,關鍵算法的更新優化,不須要購買大量新硬件。在數據中心領域,從事海量數據處理的應用中有很多關鍵算法須要優化,如密鑰加速、圖像識別、語音轉換、文本搜索等。在安防監控領域,FPGA能夠實現對大量車牌的並行分析。強大的至強處理器加上靈活高效的FPGA會給客戶在雲計算、人工智能等新興領域帶來新的技術創新。對於ARM陣營,ARM公司發佈了最新的Cortex-A75處理器以及最新處理器架構DynamIQ等新技術。DynmaIQ技術新增了針對機器學習和人工智能的全新處理器指令集,並增長了多核配置的靈活性。另外ARM公司也發佈了一個用於數據中心應用的指令集——Scalable Vector Extensions,最高支持2048 bit可伸縮的矢量計算。


      除了x86和ARM兩大陣營的創新外,最近幾年開源指令集(指令集架構,Instruction Set Architecture,ISA)也是很火熱的新發展方向。開源指令集的表明做是OpenRISC,而且Open Risk已經被Linux內核接受,成爲官方Linux內核支持的一種體系結構。可是因爲OpenRISC是由愛好者維護的,所以更新緩慢。最近幾年,伯克利大學正在嘗試從新設計一個全新的開源指令集,而且不受專利的約束和限制,這就是RISC-V,其中「V」表示變化(variation)和向量(vectors)。RISC-V包含一個很是小的基礎指令集和一系列可選的擴展指令集,最基礎的指令集只包含40條指令,經過擴展能夠支持64位和128位運算以及變長指令。


      伯克利大學對RISC-V指令集不斷改進,迅速獲得工業界和學術屆的關注。2016年,RISC-V基金會成立,成員包括谷歌、惠普、甲骨文、西部數據、華爲等巨頭,將來這些大公司很是有可能會將RISC-V運用到雲計算或者IoT等產品中。RISC-V指令集相似Linux內核,是一個開源的、現代的、沒有專利問題和歷史包袱的全新指令集,而且以BSD許可證發佈。


      目前RISC-V已經進入了GCC/Binutils的主線,相信很快也會被官方Linux內核接受。另外目前已經有多款開源和閉源的RISC-V CPU的實現,不少第三方工具和軟件廠商也開始支持RISC-V。RISC-V是否會變成開源硬件或是開源芯片領域的Linux呢?讓咱們拭目以待吧!


      推薦書籍


      計算機體系結構是一門計算機科學的基礎課程,除了閱讀ARM的芯片手冊之外,還能夠閱讀一些經典的書籍和文章。



      • 《計算機體系結構:量化研究方法》,英文版是《Computer Architecture : A Quantitative》,做者John L. Hennessy, David A. Patterson。

      • 《計算機組成與體系結構:性能設計》,做者William Stallings。

      • 《大話處理器:處理器基礎知識讀本》,做者萬木楊。

      • 《淺談cache memory》,做者王齊。

      • 《ARM與x86》,做者王齊。

      • 《現代體系結構上的UNIX系統:內核程序員的對稱多處理和緩存技術》,做者Curt Schimmel。




      [1] infocenter.arm.com

      [2] Trustzone技術在ARMv6架構中已實現,在ARMv7-A架構的Cortex-A系列處理器中開始大規模使用。

      [3] 該圖參考pc.watch.impress.co.jp/docs/column…。雖然該圖出自非ARM官方資料,可是對理解Cortex-A系列處理器內部架構頗有幫助。

      [4] 詳見<ARM CoreLink CCI-400 Cache Coherent Interconnect Technical Reference Manual>。

      [5] 詳見<ARM CoreLink DMC-400 Dynamic Memory Controller Technical Reference>。

      [6] 詳見<ARM CoreLink NIC-400 Network Interconnect Technical Reference>。

      [7] 詳見<ARM CoreLink MMU-400 System Memory Management Technical Reference>。

      [8] frankdenneman.nl/2016/07/06/…

      [9] www.cavium.com/ThunderX2_A…

      [10] pc.watch.impress.co.jp/img/pcw/doc…

      相關文章
      相關標籤/搜索