1,、現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)(Architecture,,也譯作架構(gòu)或者系統(tǒng)結(jié)構(gòu))和CPU微體系結(jié)構(gòu)(Microarchitecture,,也譯作微架構(gòu)或者微結(jié)構(gòu))
現(xiàn)代計(jì)算機(jī)的體系結(jié)構(gòu)基本都是基于馮·諾依曼體系結(jié)構(gòu),也就是“存儲程序方式”,,即程序指令代碼(指令)和數(shù)據(jù)都放在內(nèi)存中,,運(yùn)行時(shí)CPU從內(nèi)存中逐條取指令并執(zhí)行,指令還可以在必要時(shí)訪問內(nèi)存數(shù)據(jù),。
哈佛結(jié)構(gòu)是馮·諾依曼結(jié)構(gòu)的一種變形,,特點(diǎn)是將程序(指令)存儲器和數(shù)據(jù)存儲器分開,即有兩塊獨(dú)立的內(nèi)存,,一塊存放指令,,一塊存放數(shù)據(jù),但沒有脫離“存儲程序方式”這種基本的方式,,因此仍然屬于馮·諾依曼結(jié)構(gòu)的變形或者改進(jìn),。
對于程序員,即使是匯編語言甚至機(jī)器語言程序員,,最多也只能涉及到內(nèi)存指令和數(shù)據(jù)這一級,,計(jì)算機(jī)暴露給程序員的,,只能是馮·諾依曼結(jié)構(gòu)或者哈佛結(jié)構(gòu),稱為計(jì)算機(jī)體系結(jié)構(gòu)(Architecture),。
但CPU本身是由運(yùn)算器,、控制器、寄存器等部件組成的硬件電路,,內(nèi)存指令被取入CPU后,,在CPU內(nèi)部還要經(jīng)過復(fù)雜的指令譯碼等過程,才能驅(qū)動CPU硬件電路相應(yīng)部件,,按照指令具體需求進(jìn)行相應(yīng)動作,,最終完成指令執(zhí)行,這一過程是由CPU內(nèi)部的取指令,、指令譯碼,、部件驅(qū)動等硬件電路完成的,不同CPU差異很大,,即使指令集相同的CPU也可以有不同的電路實(shí)現(xiàn)方法,,這種CPU內(nèi)部硬件電路的結(jié)構(gòu),,稱為CPU的微體系結(jié)構(gòu)(Microarchitecture),。
CPU微結(jié)構(gòu)是設(shè)計(jì)CPU的核心,對于程序員而言,,CPU微結(jié)構(gòu)并不暴露給程序員,,最底層的程序員的極限也只能是通過CPU指令來訪問CPU,換而言之,,程序員使用CPU的最小操作粒度是指令,。但CPU指令在CPU內(nèi)部可能還會被解析成為更小的執(zhí)行單元,最終驅(qū)動CPU的單個(gè)硬件電路部件,,也就是說,,在CPU微結(jié)構(gòu)中有更小的操作粒度,例如微操作(MicroOP,,μOP)或者微指令,,它們都是由CPU微結(jié)構(gòu)決定的。
或者說,,CPU對于程序員而言也是一個(gè)黑盒子,,程序員只要按照CPU指令集,用相應(yīng)的指令編寫程序(如果是高級語言,,則由編譯器或者解釋器將高級語言語句最終轉(zhuǎn)換為CPU指令),,并將指令程序和相應(yīng)數(shù)據(jù)加載到內(nèi)存(現(xiàn)代通常由操作系統(tǒng)完成)即可執(zhí)行程序,程序員只訪問到計(jì)算機(jī)體系結(jié)構(gòu)這一級,,至于指令在CPU內(nèi)部是怎么進(jìn)一步解析并最終執(zhí)行的,,那是CPU微結(jié)構(gòu)決定的,,對程序員不開放,程序員不關(guān)心,,通常也不應(yīng)該關(guān)心,,實(shí)際也無法關(guān)心。
2,、如何提高CPU的運(yùn)行速度
CPU執(zhí)行一條指令,,一般來說,最少也要經(jīng)過從內(nèi)存中取指令,,將指令譯碼解析成微操作(μOP),,微操作最終驅(qū)動硬件電路部件三個(gè)步驟(簡稱取指令、譯碼和執(zhí)行),,如果執(zhí)行一條指令,,要等到這三個(gè)步驟都完成后,才能執(zhí)行下一條指令,,則一條指令執(zhí)行時(shí)間過長(用CPU硬件術(shù)語說就是消耗多個(gè)時(shí)鐘周期),,CPU運(yùn)行速度就無法得到有效提高。
如果在第一條指令譯碼時(shí),,同時(shí)取第二條指令,;然后等到第一條指令執(zhí)行時(shí),第二條指令同時(shí)譯碼,,同時(shí)還取第三條指令……這樣周而復(fù)始,,取指令、譯碼和執(zhí)行就可以重疊進(jìn)行,,就好比生產(chǎn)流水線上的工人,,第一個(gè)工人對第一個(gè)產(chǎn)品加工好第一道工序后,將產(chǎn)品傳遞給第二個(gè)工人加工第二道工序,,這是第一個(gè)工人可以對第二個(gè)產(chǎn)品加工第一道工序了……這樣可以避免工人不必要的空閑等待,,生產(chǎn)效率就會大大提高,這一提高CPU運(yùn)行速度的方法,,也就稱為“流水線”,,現(xiàn)代CPU均使用流水線技術(shù)。
流水線有效減少了單條指令的平均執(zhí)行時(shí)間,,但指令仍然是一條條順序執(zhí)行的,,實(shí)際上,很多指令是彼此不相關(guān)的,,例如訪問兩組完全不同寄存器的兩條指令,,它們完全可以并行執(zhí)行,執(zhí)行順序的先后并不影響最終執(zhí)行結(jié)果,,對于這樣的不相關(guān)指令,,完全可以設(shè)計(jì)多個(gè)執(zhí)行部件電路,,直接扔到不同執(zhí)行部件中去并行執(zhí)行,稱為亂序(Out-of-order)執(zhí)行,,可以進(jìn)一步提高指令執(zhí)行速度,。亂序執(zhí)行在現(xiàn)代CPU中廣泛應(yīng)用,甚至對于有相關(guān)性的指令,,也可以采用寄存器換名等手段將它們變成不相關(guān)指令,,從而進(jìn)行亂序執(zhí)行。
但流水線和亂序執(zhí)行都會碰到一個(gè)問題,,從原理上講它們僅適用于純順序執(zhí)行的指令,,一旦遇到分支,即條件跳轉(zhuǎn)指令,,因?yàn)椴粓?zhí)行到條件跳轉(zhuǎn)指令本身,,是沒法知道程序轉(zhuǎn)向何處執(zhí)行的,也就是條件跳轉(zhuǎn)指令的下一條指令在未執(zhí)行前不確定,,因此無法預(yù)先取得條件跳轉(zhuǎn)指令的后續(xù)指令,,這時(shí)流水線和亂序執(zhí)行都會失效,因?yàn)樗鼈兊那疤崾穷A(yù)先取得后續(xù)指令,。
為了盡量解決這個(gè)問題,,現(xiàn)代CPU廣泛使用“分支預(yù)測”手段,也就是預(yù)測條件跳轉(zhuǎn)指令會跳向哪個(gè)分支,,然后對這個(gè)分支進(jìn)行預(yù)取后續(xù)指令,。分支預(yù)測的常用策略是:如果某一段時(shí)間內(nèi)某一條件跳轉(zhuǎn)都走向某一固定分支,,則可以預(yù)測這條條件跳轉(zhuǎn)指令下一次很大可能也走向這一分支,。
典型例子:程序中的循環(huán)結(jié)構(gòu)一般要循環(huán)多次才結(jié)束,那么在循環(huán)結(jié)束之前,,判斷循環(huán)條件的條件跳轉(zhuǎn)指令顯然都是走向繼續(xù)循環(huán)分支的,。
分支預(yù)測配合流水線和亂序執(zhí)行,能夠大大提高CPU的運(yùn)行速度,,因此現(xiàn)代CPU微結(jié)構(gòu)基本都使用這種設(shè)計(jì),。
3、分支預(yù)測帶來的問題——指令執(zhí)行的“回滾”
分支預(yù)測并不能保證100%的成功預(yù)測,,一旦預(yù)測失敗,,也就是最終執(zhí)行到條件跳轉(zhuǎn)指令時(shí),發(fā)現(xiàn)跳轉(zhuǎn)目標(biāo)不是先前預(yù)測的分支方向,,那么按照分支預(yù)測預(yù)取的后續(xù)指令實(shí)際上失效,,這些指令已經(jīng)完成的工作必須“取消”掉,否則就會造成錯(cuò)誤的指令執(zhí)行,。
用一個(gè)程序員容易理解的比喻:分支預(yù)測的后續(xù)指令執(zhí)行,,好比一個(gè)“事務(wù)”,,如果分支預(yù)測是正確的,那么“事務(wù)”可以“提交”,,這些后續(xù)指令就真正起作用,;如果最終執(zhí)行到條件跳轉(zhuǎn)指令時(shí)發(fā)現(xiàn)分支預(yù)測是錯(cuò)誤的,則“事務(wù)”必須“回滾”,,即使后續(xù)指令已經(jīng)執(zhí)行了,,甚至是亂序執(zhí)行了,已經(jīng)完成的工作也都必須全部“撤銷”,,后續(xù)指令要看起來沒有起任何作用,,重新到正確的分支取新的指令執(zhí)行。
理論上說,,不管指令執(zhí)行“提交”還是“回滾”,,都只與CPU微結(jié)構(gòu)相關(guān),其過程程序員應(yīng)該看不到,,程序員只能看到宏觀指令按照程序流程執(zhí)行,,CPU內(nèi)部對程序員仍然應(yīng)該是黑盒子。
4,、克服CPU運(yùn)行速度與內(nèi)存訪問速度的差異——高速緩存(Cache)
目前CPU主頻已經(jīng)達(dá)到3GHz以上,,普遍采用多核并行,盡管主內(nèi)存(DDR SDRAM)的主頻已經(jīng)達(dá)到2GHz—3GHz甚至更高,,也無法完全滿足多核CPU運(yùn)行速度的需求,,因?yàn)橹噶顖?zhí)行還是必須從內(nèi)存中取指令,如果內(nèi)存訪問速度不夠,,CPU運(yùn)行速度會受到內(nèi)存訪問速度的限制,。
為了克服這個(gè)問題,目前采用在CPU與主內(nèi)存之間插入多級高速緩存(Cache)的方法,,Cache是一種訪問速度極高的存儲器,,甚至可以集成在CPU內(nèi)部,成為CPU微結(jié)構(gòu)的一部分,。Cache與主內(nèi)存之間以塊為單位交換數(shù)據(jù),,塊長一般為數(shù)十字節(jié)。
當(dāng)CPU需要訪問內(nèi)存,,例如從內(nèi)存中取指令時(shí),,第一次需要先將相應(yīng)內(nèi)存塊一次性讀入到空閑的Cache塊,CPU再直接訪問Cache塊,,此時(shí)內(nèi)存訪問速度會慢一些,,因?yàn)榇嬖谥鲀?nèi)存與Cache之間傳輸成塊數(shù)據(jù)的時(shí)間;CPU第二次訪問相同塊內(nèi)存時(shí),,即可直接訪問Cache塊,,而無須訪問主內(nèi)存,,內(nèi)存訪問速度會快得多。
主內(nèi)存—Cache系統(tǒng)構(gòu)成現(xiàn)代CPU的內(nèi)存儲器系統(tǒng),,其原理與操作系統(tǒng)中的硬盤—內(nèi)存系統(tǒng)構(gòu)成虛擬內(nèi)存的原理極其相似,。
5、指令執(zhí)行的“回滾”在主內(nèi)存—Cache系統(tǒng)留下的“痕跡”
如上所述,,如果分支預(yù)測失敗,,則分支預(yù)測預(yù)取的后續(xù)指令,哪怕已經(jīng)亂序執(zhí)行了多條指令,,也必須“回滾”,,指令在CPU微結(jié)構(gòu)中已經(jīng)完成的工作必須全部“撤銷”。
“撤銷”指令是容易的,,指令最終完成的工作,,無外乎是對寄存器或者內(nèi)存的修改,可以暫且將修改“緩存”起來,,如果“撤銷”,,最終不真正修改寄存器或者內(nèi)存即可。
但對于內(nèi)存讀寫指令(在CPU設(shè)計(jì)中通常稱為Load/Store指令或者LD/ST指令),,以讀內(nèi)存指令為例,,如果最終“提交”,就必須讀取實(shí)際的內(nèi)存地址,,如果相應(yīng)內(nèi)存塊還沒有被讀入到Cache塊,,讀取速度就會受到影響,因此在讀內(nèi)存指令最終“提交”或者“回滾”之前,,CPU微結(jié)構(gòu)一般會事先將Cache塊準(zhǔn)備好,,也就是如果相應(yīng)內(nèi)存塊還沒有被讀入到Cache塊則預(yù)先讀入。
也就是說,,即使分支預(yù)測失敗,,已經(jīng)亂序執(zhí)行的多條預(yù)取指令中只要有讀內(nèi)存指令,,就算最后被“回滾”,,對廣義的CPU微結(jié)構(gòu)還是有影響的——相應(yīng)內(nèi)存塊已經(jīng)讀入到了Cache塊。
而內(nèi)存塊是否已經(jīng)讀入到Cache塊,,訪問速度是有一定差異的,,這相當(dāng)于“回滾”的讀內(nèi)存指令在CPU微結(jié)構(gòu)中留下的“痕跡”,這個(gè)“痕跡”是可以被作為“側(cè)信道”利用的,。
6,、Meltdown攻擊原理的通俗簡明解釋
操作系統(tǒng)的內(nèi)核數(shù)據(jù)是受到CPU微結(jié)構(gòu)保護(hù)的,用戶模式的應(yīng)用程序無法訪問,,如果訪問是要引發(fā)CPU錯(cuò)誤異常的,。
構(gòu)造一個(gè)分支,,先檢測讀取內(nèi)存的地址是否合法,合法就讀取相應(yīng)地址內(nèi)存字節(jié),,然后根據(jù)內(nèi)存字節(jié)的值,,讓內(nèi)存字節(jié)的值與映射到不同Cache塊的內(nèi)存塊對應(yīng)起來,再故意讀取一下映射到不同Cache塊的內(nèi)存塊,;如果訪問內(nèi)存的地址非法,,例如操作系統(tǒng)內(nèi)核數(shù)據(jù)地址,直接不讀取,。
顯然,,這樣的分支,無論讀取合法地址還是非法地址都是不會出錯(cuò)的,。
用大循環(huán)執(zhí)行多次這個(gè)分支,,前若干次,讀取內(nèi)存地址都是合法的,,“訓(xùn)練”CPU的分支預(yù)測,,讓CPU微結(jié)構(gòu)認(rèn)為下次也應(yīng)該走向讀取內(nèi)存這一分支。
然后,,突然執(zhí)行一次非法的操作系統(tǒng)內(nèi)核數(shù)據(jù)地址讀取,。
按道理說,讀取內(nèi)存地址非法,,應(yīng)該走向不讀取這一分支,,可是CPU的分支預(yù)測已經(jīng)被“訓(xùn)練”成了“條件反射”,CPU稀里糊涂地預(yù)取了讀取非法地址的指令,,并亂序并行執(zhí)行,,只是暫時(shí)沒有“提交”,因?yàn)闆]有“提交”,,即使讀取非法地址內(nèi)存,,也不會引發(fā)CPU異常;而且此時(shí)根據(jù)非法地址內(nèi)存字節(jié)的值,,故意讀取映射到不同Cache塊內(nèi)存塊的指令,,相應(yīng)的Cache塊也被CPU微結(jié)構(gòu)準(zhǔn)備好了(因?yàn)槭遣⑿衼y序執(zhí)行嘛),也就是相應(yīng)內(nèi)存塊已經(jīng)讀入到了Cache塊,。
當(dāng)然,,最后CPU發(fā)現(xiàn)這次分支預(yù)測錯(cuò)了,沒關(guān)系,,預(yù)取的指令亂序并行執(zhí)行“回滾”,,讀取非法地址的指令根本沒執(zhí)行,沒有引發(fā)CPU異常,皆大歡喜,。
可是,,“痕跡”卻悄悄留下了,與非法地址內(nèi)存字節(jié)值有意對應(yīng)起來的內(nèi)存塊已經(jīng)讀入到了Cache塊,。
用程序遍歷一下所有可能對應(yīng)的內(nèi)存塊,,看誰訪問速度最快,誰就很可能在Cache中,,而非法地址對應(yīng)的操作系統(tǒng)內(nèi)核數(shù)據(jù)字節(jié)值是有意與內(nèi)存塊對應(yīng)的,,原本在用戶模式下不能訪問的操作系統(tǒng)內(nèi)核數(shù)據(jù)字節(jié)值就被推算出來。
這就是所謂的“側(cè)信道泄漏”,。
7,、這一攻擊實(shí)質(zhì)的一句話解釋
CPU微結(jié)構(gòu)內(nèi)部信息通過側(cè)信道向宏觀計(jì)算機(jī)體系結(jié)構(gòu)的泄漏。
8,、一句話教訓(xùn)
設(shè)計(jì)CPU追求速度快是理所當(dāng)然的,,但速度和安全性之間要有平衡點(diǎn),微結(jié)構(gòu)無論怎樣追求高速優(yōu)化,,屁股要擦干凈,,不要向宏觀體系結(jié)構(gòu)泄漏內(nèi)部信息。
1965年intel創(chuàng)世人之一,、時(shí)任仙童半導(dǎo)體公司電子工程師的戈登摩爾提出了摩爾定律,,對人類的計(jì)算之路的快速進(jìn)步做出了預(yù)言。過去二十年,,人類在互聯(lián)網(wǎng)的帶動下,,信息化發(fā)展一路狂奔。這種對速度的追求一定程度上,,透支的是安全的掉隊(duì),。也許這正是一個(gè)重新定義平衡點(diǎn)的時(shí)刻。