計算機系統007 - 硬體元件之CPU

SniperPan發表於2017-07-10

前一篇計算機系統006 - 硬體元件之RAM中講完五大元件中的RAM部分,剩下最後的硬骨頭CPU還未說明,本篇就試著對其進行儘可能完整的剖析。

開篇之前,放一張CPU全貌鎮場。

工藝很好,封裝很完整,除了一排排針腳,再看不出來其他東西,所幸透過外殼,一探究竟(以Intel Haswell為例)。

說實在的,要是不標英文註釋,我也不知道這裡面每個區域放的是什麼。不過既然有,總比沒有好,這裡暫不討論商業化成熟的CPU方案所有內容,只關注如下兩個部分:

  • SHARED L3 CACHE
    如果還能記得上一期所述的儲存器層級圖的話,那麼你應該能注意到,SHARED L3 CACHE下面一級是MEMORY CONTROLLER IO,也就是說,這一級可以視為Cache,而下一級連線的是RAM,它的上一級又是CORE。由於之前已經對Cache的來由和原理做了講解,這裡就不在贅述。

  • CORE
    CORE實際上是CPU的基礎計算單元(注意這裡說的是計算單元,而非控制單元或處理單元),它可以執行單個程式,維護程式狀態、暫存器值、確保正確的執行次序,以及通過ALU執行操作。一個CPU通常由一至多個CORE,俗稱“多核”,上圖所示的CPU中就含有4個CORE,因此也稱4核。
    既然每個CORE可以單獨執行程式,那麼在預算範圍內自然是越多越好。不過也應當知道,4核CPU整體效能並不能簡單換算成單核乘以4,先不論軟體上是否做了支援以便可以均攤在4核上跑滿,單說4核相互間也必須通過L3 Cache交換資訊,就可以知道在交換上會耗費一部分開銷。

1. CORE

在第4篇 中介紹CPU部分時,給出了下面這張圖。

從圖中可以看出,CPU是由ALU、CU、Memory三部分組成,而CORE可以單獨執行整個程式,且CPU從一開始也是單核形式存在,所以這裡可以等同認為CORE也是由ALU、CU、Memory三部分組成。

1.1 ALU(Arithmetic logic unit)

ALU也稱為算術邏輯單元,是用於執行二進位制表示整型的算術和按位運算組合數位電路。ALU是許多計算電路的基礎模組,與之相對的是FPU(Floating-point unit)浮點單元,FPU處理的是浮點數值。

以計算“1+2”為例,其原理如下圖所示:

  • Integer Operand A, B
    A,B均為輸入運算物件,分別為 1(01),2(10)

  • Opcode
    運算子,作用於運算物件。此處為‘+’。雖然opcode與機器語言opcode在感念上有所區別,但通常會等同使用。

  • Status(可選)
    可選狀態值,輸入輸出均為狀態暫存器,在ALU進行計算時,可能需要參考其中標誌如CF判斷是否有進位,同時在結果中設定該flag

  • Integer Result
    整型結果,輸出閘電路交換後值,此處為3(11)

包括上述加法操作,ALU一共支援如下型別操作:

  • 邏輯運算,包括AND、OR、NOT、XOR、NOR、NAND等
  • 移位操作,左右移位
  • 數學運算,位加減法,也可進一步支援乘、除法

每一個Opcode都對應著不同的電路實現,相互間存在著邏輯閘、觸發器等各種實現差異。而從ALU的使用原理上可以看出,要想完成計算,需如下前提條件:

  • 輸入端載入兩個二進位制整型值
  • 提供運算子
  • 可選地提供狀態值

為了提供這三類值,就必須先和暫存器打交道。

1.2 Memory - Register

Register也稱為暫存器,總的來講,Register是Memory中SRAM的一種,優勢在於快,劣勢在於貴。
快的原因在於無機械部件、無電容充放電,存取的所有過程均基於電路交換,也就是說電路頻率有多快,理論上他就可以做到多快。不過高頻電路中無論是元器件焊點,還是佈線都有可能對整合後電路整體效能和穩定性產生干擾,因此在頻率上也常常有所限制。

設計暫存器時,並非所有Register都以相同電路實現,而是會以所要實現的功能為目標進行取捨。通常,暫存器可分為如下幾類:

  • MAR(Memory Address Register)
    記憶體地址暫存器,儲存資料或指令在記憶體中地址,掛載在地址匯流排上,用於在指令執行期間獲取資料或指令。例如CPU希望在記憶體中儲存或獲取記憶體中某一資料時,可將對應地址放在MAR中

  • PC(Program Counter)
    程式計數器,在Intel X86中也稱為指令指標IP(instruction pointer),始終指向下一條要執行的指令

  • AC(Accumulator) Register
    累加暫存器,儲存CPU計算所得結果。

  • MDR(Memory Data Register)
    記憶體資料暫存器,與MAR類似,區別在於掛載在資料匯流排上。MDR就可以從資料匯流排載入資料,也可以儲存CPU中資料,就像一個Buffer一樣儲存著送往解碼器(見1.3小節)前的資訊。
    MDR是配合MAR使用的,讀取某一記憶體地址資料前,現在MAR中存放讀取地址,傳送讀訊號,就可以在MDR中獲取目標值;同樣在MAR中存入目標地址,MDR中存放要寫入資料後,傳送寫訊號,即可往記憶體中寫出資料。也就是說,MDR和MAR共同組成了CPU對記憶體部分的訪問介面

  • Index Register
    變址暫存器,存放從基址起要偏移的地址數,用於在程式執行過程中調整操作符地址。

  • MBR(Memory Buffer Register)
    快取暫存器,存放已經讀取或寫入記憶體的資料或指令內容,起快取作用。

  • Data Register
    微型計算機中用於臨時存放傳輸給,或讀取自其他外圍裝置資料的資料。

當然,上述暫存器大多是控制單元CU內部使用,對於使用者,可見的暫存器型別為通用暫存器,以8086為例,可見的暫存器如下:

累加暫存器 AX
基址暫存器 BX
計數暫存器 CX
資料暫存器 DX
棧指標暫存器 SP
基址指標暫存器 BP
源變址暫存器 SI
目的編制暫存器 DI

事實上,每個暫存器的背後都有獨立的電路實現,而不同型別的暫存器實現往往各不相同。因此,程式設計時並非強制讓你使用某個暫存器,而是由於該暫存器設計時電路實現所限,它只支援部分功能。例如定址時只能使用BX、BP、SI、DI,不是故意刁難,標新立異,而是因為只有這些暫存器在電路實現時對定址做了支援。

同樣,一個程式執行後,會有對應狀態值,如指令指標、AF、CF、ZF等等標誌位,均儲存在對應暫存器中。CPU只會迴圈讀取下一條指令地址,進行計算,因此只要在讀取下一條指令地址前,將其替換成新程式的指令指標,即可完成程式切換。當然為了確保能順利回來,還必須在替換前儲存現有程式狀態。

1.3 CU(Control Unit)

到現在為止,我們有了支援不同運算操作(Opcode)的ALUs,也有了不同用途儲存不同資料的暫存器Registers,為了實現進一步的自動化計算,應該開始考慮控制單元CU的內部邏輯了。

計算的實質是接收任務描述,按照任務描述的步驟,完成計算,並輸出結果。回到任務描述本身,使用者該如何描述一個任務以使得執行計算的計算機可以理解任務內容?

想象一下現實生活中,需要將某一任務託付給別人時,我們通常需要如下步驟:

  • 選擇一種雙方都可以理解的語言
  • 按照邏輯結構提取要點,轉化成綱要
  • 如有必要,進一步細化綱要,形成內容句子
  • 最後以檔案或是口述等方式傳達給對方

當對方是計算機時,上述步驟就變成:

  • 選擇機器語言或者可最終轉化為機器語言的高階語言,如彙編、C/C++、Java、甚至Python
  • 主函式中依次寫明主要步驟
  • 如有必要,進一步細化主要步驟,形成子函式或其他可呼叫物件
  • 最後以可執行檔案方式儲存於磁碟中

從前面我們瞭解了ALU的運算方法和暫存器如何從記憶體中載入資料,因此這裡只考慮暫存器、ALU、CU三者之間的互動。對於此時的CU來講,暫存器中已經給出了可執行檔案在記憶體中的入口地址,起點已經有了,接下來就是如何按照可執行檔案內容順利執行完整個任務。

CU就像CPU的大腦,負責所有控制行為。通常在CU內部,將計算任務執行過程分為如下三步:

  • 取指(Fetch)
    第一步,獲取指令。程式記憶體中指令地址儲存在程式計數器PC中,每次獲取完指令後,PC自動加上所獲取到指令的長度,也就是指向下一條指令處。通常指令來源於相對較慢的記憶體中,這會阻塞CU直到獲取完成,好在現代處理器通過快取或流水線技術極大緩解了該問題。

  • 解碼(Decode)
    第二步,指令解碼,指令解碼的最終解釋權歸CU指令集ISA所有。指令的一部分為操作碼,標明應該執行何種操作。其餘部分通常會提供該操作所需的額外資訊,如運算元等。

CU實現分電路和微程式設計兩種,前者不可修改,但速度比微程式設計實現更快,由於指令集修改後需要調整電路,代價較高,因此通常用於RISC精簡指令集中;另一方面,微程式設計CU簡化了結構,降低了開發難度。

  • 執行(Execute)
    最後,執行指令。根據CU結構不同,指令中可能包含單個或一系列操作。操作過程中,根據時鐘脈衝,CU不同部分可以協同執行操作的所有或一部分。大部分情況下,結果會寫入暫存器以便於後續操作快速獲取,但有時候也會不得不寫入到緩慢的記憶體中。

簡而言之,就是任務描述中的指令(以彙編級別起始)來源於CPU指令集,其中每條指令在從記憶體中讀取後進行解碼,解碼器將單條指令分解成一至多條微操作(Opcode),通過控制電路訊號,選擇指定ALU完成目標操作

同時,需要注意上述三個步驟執行過程中需要參考時鐘脈衝,時鐘脈衝有特定頻率,電路被時鐘脈衝的上升沿或下降沿所觸發,形成統一步調。

2. CPU

上一節中對單個核心進行了探討,但隨著技術的發展,單核心的速度往往無法滿足日益增長的計算需求,因此在CPU發展史上,也陸續出現瞭如下改進技術。

2.1 流水線 Pipeline

流水線技術的前提是操作可進一步分解,分解後的每個步驟可獨立執行。


如上圖所示,同一任務在分解為5個步驟後,通過合理安排ALU或其他CORE執行任務,原本10個時鐘週期只能完成兩次任務,而現在可以完成6次。

不過需要注意的是,流水線技術並不能增加單個任務的處理效率,即每個任務同樣還是需要5個時鐘週期才能完成,甚至相反,反而會因為進行了任務分解而導致每個步驟間需通過暫存器交換結果。

因此,從本質上來講,流水技術減少的是任務的平均等待時間,而非單個任務完成時間

2.2 並行處理 Parallel

通常大的任務可以分解為小的任務,這樣就可以同時進行處理以減少整體消耗時長。通常有如下3種平行計算方法:

  • 位級別並行
    通過增長處理器字長,減少了同一指令所需運算元目。如8位處理器在進行兩個16位整型數加法時,需要先將低8位相加,再將高8位及進位相加,也就是說,需要兩個操作才能實現一條指令。而如果使用16位處理器,就可以通過一個操作完成相同指令。這樣一來,就減少了一半的執行時間。

  • 指令級別並行
    計算機程式其實就是一些列有序的指令,預設情況下,處理器每個時鐘週期內最多隻能處理一條指令,而事實上,有的指令重新排序後並列執行並不會影響最終結果,因此完全可以在指令級別實現部分並行以提高速度。

  • 任務級別並行
    同一程式的多個程式(程式執行例項,如感興趣,後續作業系統部分會講到)可以針對相同資料或不同資料進行並行處理,甚至同一程式也可以將其子任務分發給不同處理器進行處理。

2.3 中斷

改進至此,卻發現無論CPU如何優化並行、流水線等技術,真正導致程式執行速率緩慢的原因還在於CPU每執行一條指令,至少要與儲存器互動一次,暫存器也好,記憶體也罷,它們的速率即使有了Cache或者分級儲存機制,依然不能夠有效彌補。為了能夠最大限度地使用CPU,就需要對耗時嚴重的I/O操作進行優化。

傳統模型裡,執行一次I/O需要三個步驟:

  • 準備I/O環境,如為相應裝置命令準備引數
  • 實際I/O命令,如無中斷支援,則CPU必須持續輪詢直到結束,耗費CPU資源
  • 完善I/O操作,包括設定標誌位等

從中可以看出,第一、三個步驟並未直接參與主存讀寫,耗時遠比第二個步驟少,為了減少第二個步驟所消耗的CPU資源,於是從硬體層引入了中斷機制。

中斷可以通過單獨控制器實現,也可以整合進CPU中。每個中斷有各自獨立的編碼,硬體上有具有相互獨立的記憶體單元。當一箇中斷出現後,控制器在執行完當前指令後將切換程式至內建ISR(Interrupt Service Routine)或指定的中斷處理程式,執行完畢後,再重新切回原執行程式。

至於如何完成CPU中斷的程式切換,將在後面詳細講述,此處只說明為修改PC地址、重新載入程式被切換前各暫存器狀態值即可。

3. 總結

CPU這一篇就寫到這裡,沒有滿屏的電路圖,也沒有什麼高深的人與自然。個人見解來講,理解CPU本質還是要回歸到計算本身,包括計算實現、計算表示、以及自動化計算流程的分解。希望能夠有所收穫,到本篇為止,硬體部分基本講完,然而有硬體並不能成為計算機,所以下一篇中,將從作業系統開始說起。

相關文章