程式設計師需要了解的硬核知識之CPU

cxuan發表於2019-10-21

大家都是程式設計師,大家都是和計算機打交道的程式設計師,大家都是和計算機中軟體硬體打交道的程式設計師,大家都是和CPU打交道的程式設計師,所以,不管你是玩兒硬體的還是做軟體的,你的世界都少不了計算機最核心的 - CPU

CPU是什麼

CPU 的全稱是 Central Processing Unit,它是你的電腦中最硬核的元件,這種說法一點不為過。CPU 是能夠讓你的計算機叫計算機的核心元件,但是它卻不能代表你的電腦,CPU 與計算機的關係就相當於大腦和人的關係。它是一種小型的計算機晶片,它嵌入在桌上型電腦、膝上型電腦或者平板電腦的主機板上。通過在單個計算機晶片上放置數十億個微型電晶體來構建 CPU。 這些電晶體使它能夠執行執行儲存在系統記憶體中的程式所需的計算,也就是說 CPU 決定了你電腦的計算能力。

image.png

CPU 實際做什麼

CPU 的核心是從程式或應用程式獲取指令並執行計算。此過程可以分為三個關鍵階段:提取,解碼和執行。CPU從系統的 RAM 中提取指令,然後解碼該指令的實際內容,然後再由 CPU 的相關部分執行該指令。

RAM : 隨機存取儲存器(英語:Random Access Memory,縮寫:RAM),也叫主存,是與 CPU 直接交換資料的內部儲存器。它可以隨時讀寫(重新整理時除外),而且速度很快,通常作為作業系統或其他正在執行中的程式的臨時資料儲存介質

CPU 的內部結構

說了這麼多 CPU 的重要性,那麼 CPU 的內部結構是什麼呢?又是由什麼組成的呢?下圖展示了一般程式的執行流程(以 C 語言為例),可以說了解程式的執行流程是掌握程式執行機制的基礎和前提。

image.png

在這個流程中,CPU 負責的就是解釋和執行最終轉換成機器語言的內容。

CPU 主要由兩部分構成:控制單元算術邏輯單元(ALU)

  • 控制單元:從記憶體中提取指令並解碼執行
  • 算數邏輯單元(ALU):處理算數和邏輯運算

CPU 是計算機的心臟和大腦,它和記憶體都是由許多電晶體組成的電子部件。它接收資料輸入,執行指令並處理資訊。它與輸入/輸出(I / O)裝置進行通訊,這些裝置向 CPU 傳送資料和從 CPU 接收資料。

從功能來看,CPU 的內部由暫存器、控制器、運算器和時鐘四部分組成,各部分之間通過電訊號連通。

image.png

  • 暫存器是中央處理器內的組成部分。它們可以用來暫存指令、資料和地址。可以將其看作是記憶體的一種。根據種類的不同,一個 CPU 內部會有 20 - 100個暫存器。
  • 控制器負責把記憶體上的指令、資料讀入暫存器,並根據指令的結果控制計算機
  • 運算器負責運算從記憶體中讀入暫存器的資料
  • 時鐘 負責發出 CPU 開始計時的時鐘訊號

接下來簡單解釋一下記憶體,為什麼說 CPU 需要講一下記憶體呢,因為記憶體是與 CPU 進行溝通的橋樑。計算機所有程式的執行都是在記憶體中執行的,記憶體又被稱為主存,其作用是存放 CPU 中的運算資料,以及與硬碟等外部儲存裝置交換的資料。只要計算機在執行中,CPU 就會把需要運算的資料調到主存中進行運算,當運算完成後CPU再將結果傳送出來,主存的執行也決定了計算機的穩定執行。

主存通過控制晶片與 CPU 進行相連,由可讀寫的元素構成,每個位元組(1 byte = 8 bits)都帶有一個地址編號,注意是一個位元組,而不是一個位。CPU 通過地址從主存中讀取資料和指令,也可以根據地址寫入資料。注意一點:當計算機關機時,記憶體中的指令和資料也會被清除。

CPU 是暫存器的集合體

在 CPU 的四個結構中,我們程式設計師只需要瞭解暫存器就可以了,其餘三個不用過多關注,為什麼這麼說?因為程式是把暫存器作為物件來描述的。

說到暫存器,就不得不說到組合語言,我大學是學資訊管理與資訊系統的,我就沒有學過彙編這門課(就算有這門課也不會好好學hhhh),出來混總是要還的,要想作為一個硬核程式設計師,不能不瞭解這些概念。說到組合語言,就不得不說到高階語言,說到高階語言就不得不牽扯出語言這個概念。

計算機語言

我們生而為人最明顯的一個特徵是我們能通過講話來實現彼此的交流,但是計算機聽不懂你說的話,你要想和他交流必須按照計算機指令來交換,這就涉及到語言的問題,計算機是由二進位制構成的,它只能聽的懂二進位制也就是機器語言,但是普通人是無法看懂機器語言的,這個時候就需要一種電腦既能識別,人又能理解的語言,最先出現的就是組合語言。但是組合語言晦澀難懂,所以又出現了像是 C,C++,Java 的這種高階語言。

所以計算機語言一般分為兩種:低階語言(機器語言,組合語言)和高階語言。使用高階語言編寫的程式,經過編譯轉換成機器語言後才能執行,而組合語言經過彙編器才能轉換為機器語言。

組合語言

首先來看一段用匯編語言表示的程式碼清單

mov eax, dword ptr [ebp-8]   /* 把數值從記憶體複製到 eax */
add eax, dword ptr [ebp-0Ch] /* 把 eax 的數值和記憶體的數值相加 */
mov dword ptr [ebp-4], eax /* 把 eax 的數值(上一步的結果)儲存在記憶體中*/

這是採用組合語言(assembly)編寫程式的一部分。組合語言採用 助記符(memonic) 來編寫程式,每一個原本是電訊號的機器語言指令會有一個與其對應的助記符,例如 mov,add 分別是資料的儲存(move)和相加(addition)的簡寫。組合語言和機器語言是一一對應的。這一點和高階語言有很大的不同,通常我們將組合語言編寫的程式轉換為機器語言的過程稱為 彙編;反之,機器語言轉化為組合語言的過程稱為 反彙編

組合語言能夠幫助你理解計算機做了什麼工作,機器語言級別的程式是通過暫存器來處理的,上面程式碼中的 eax,ebp 都是表示的暫存器,是 CPU 內部暫存器的名稱,所以可以說 CPU 是一系列暫存器的集合體。在記憶體中的儲存通過地址編號來表示,而暫存器的種類則通過名字來區分。

不同型別的 CPU ,其內部暫存器的種類,數量以及暫存器儲存的數值範圍都是不同的。不過,根據功能的不同,可以將暫存器劃分為下面這幾類

種類 功能
累加暫存器 儲存執行的資料和運算後的資料。
標誌暫存器 用於反應處理器的狀態和運算結果的某些特徵以及控制指令的執行。
程式計數器 程式計數器是用於存放下一條指令所在單元的地址的地方。
基址暫存器 儲存資料記憶體的起始位置
變址暫存器 儲存基址暫存器的相對地址
通用暫存器 儲存任意資料
指令暫存器 儲存正在被執行的指令,CPU內部使用,程式設計師無法對該暫存器進行讀寫
棧暫存器 儲存棧區域的起始位置

其中程式計數器、累加暫存器、標誌暫存器、指令暫存器和棧暫存器都只有一個,其他暫存器一般有多個。

image.png

程式計數器

程式計數器(Program Counter)是用來儲存下一條指令所在單元的地址。

程式執行時,PC的初值為程式第一條指令的地址,在順序執行程式時,控制器首先按程式計數器所指出的指令地址從記憶體中取出一條指令,然後分析和執行該指令,同時將PC的值加1指向下一條要執行的指令。

我們還是以一個事例為準來詳細的看一下程式計數器的執行過程

image.png

這是一段進行相加的操作,程式啟動,在經過編譯解析後會由作業系統把硬碟中的程式複製到記憶體中,示例中的程式是將 123 和 456 執行相加操作,並將結果輸出到顯示器上。由於使用機器語言難以描述,所以這是經過翻譯後的結果,實際上每個指令和資料都可能分佈在不同的地址上,但為了方便說明,把組成一條指令的記憶體和資料放在了一個記憶體地址上。

地址 0100 是程式執行的起始位置。Windows 等作業系統把程式從硬碟複製到記憶體後,會將程式計數器作為設定為起始位置 0100,然後執行程式,每執行一條指令後,程式計數器的數值會增加1(或者直接指向下一條指令的地址),然後,CPU 就會根據程式計數器的數值,從記憶體中讀取命令並執行,也就是說,程式計數器控制著程式的流程

條件分支和迴圈機制

我們都學過高階語言,高階語言中的條件控制流程主要分為三種:順序執行、條件分支、迴圈判斷三種,順序執行是按照地址的內容順序的執行指令。條件分支是根據條件執行任意地址的指令。迴圈是重複執行同一地址的指令。

  • 順序執行的情況比較簡單,每執行一條指令程式計數器的值就是 + 1。
  • 條件和迴圈分支會使程式計數器的值指向任意的地址,這樣一來,程式便可以返回到上一個地址來重複執行同一個指令,或者跳轉到任意指令。

下面以條件分支為例來說明程式的執行過程(迴圈也很相似)

image.png

​ 條件迴圈的執行流程

程式的開始過程和順序流程是一樣的,CPU 從0100處開始執行命令,在0100和0101都是順序執行,PC 的值順序+1,執行到0102地址的指令時,判斷0106暫存器的數值大於0,跳轉(jump)到0104地址的指令,將數值輸出到顯示器中,然後結束程式,0103 的指令被跳過了,這就和我們程式中的 if() 判斷是一樣的,在不滿足條件的情況下,指令會直接跳過。所以 PC 的執行過程也就沒有直接+1,而是下一條指令的地址。

標誌暫存器

條件和迴圈分支會使用到 jump(跳轉指令),會根據當前的指令來判斷是否跳轉,上面我們提到了標誌暫存器,無論當前累加暫存器的運算結果是正數、負數還是零,標誌暫存器都會將其儲存(也負責溢位和奇偶校驗)

溢位(overflow):是指運算的結果超過了暫存器的長度範圍

奇偶校驗(parity check):是指檢查運算結果的值是偶數還是奇數

CPU 在進行運算時,標誌暫存器的數值會根據當前運算的結果自動設定,運算結果的正、負和零三種狀態由標誌暫存器的三個位表示。標誌暫存器的第一個位元組位、第二個位元組位、第三個位元組位各自的結果都為1時,分別代表著正數、零和負數。

image.png

CPU 的執行機制比較有意思,假設累加暫存器中儲存的 XXX 和通用暫存器中儲存的 YYY 做比較,執行比較的背後,CPU 的運算機制就會做減法運算。而無論減法運算的結果是正數、零還是負數,都會儲存到標誌暫存器中。結果為正表示 XXX 比 YYY 大,結果為零表示 XXX 和 YYY 相等,結果為負表示 XXX 比 YYY 小。程式比較的指令,實際上是在 CPU 內部做減法運算。

函式呼叫機制

接下來,我們繼續介紹函式呼叫機制,哪怕是高階語言編寫的程式,函式呼叫處理也是通過把程式計數器的值設定成函式的儲存地址來實現的。函式執行跳轉指令後,必須進行返回處理,單純的指令跳轉沒有意義,下面是一個實現函式跳轉的例子

image.png

圖中將變數 a 和 b 分別賦值為 123 和 456 ,呼叫 MyFun(a,b) 方法,進行指令跳轉。圖中的地址是將 C 語言編譯成機器語言後執行時的地址,由於1行 C 程式在編譯後通常會變為多行機器語言,所以圖中的地址是分散的。在執行完 MyFun(a,b)指令後,程式會返回到 MyFun(a,b) 的下一條指令,CPU 繼續執行下面的指令。

函式的呼叫和返回很重要的兩個指令是 callreturn 指令,再將函式的入口地址設定到程式計數器之前,call 指令會把呼叫函式後要執行的指令地址儲存在名為棧的主存內。函式處理完畢後,再通過函式的出口來執行 return 指令。return 指令的功能是把儲存在棧中的地址設定到程式計數器。MyFun 函式在被呼叫之前,0154 地址儲存在棧中,MyFun 函式處理完成後,會把0154的地址儲存在程式計數器中。這個呼叫過程如下

image.png

在一些高階語言的條件或者迴圈語句中,函式呼叫的處理會轉換成 call 指令,函式結束後的處理則會轉換成 return 指令。

通過地址和索引實現陣列

接下來我們看一下基址暫存器和變址暫存器,通過這兩個暫存器,我們可以對主存上的特定區域進行劃分,來實現類似陣列的操作,首先,我們用十六進位制數將計算機記憶體上的 00000000 - FFFFFFFF 的地址劃分出來。那麼,凡是該範圍的記憶體地址,只要有一個 32 位的暫存器,便可檢視全部地址。但如果想要想陣列那樣分割特定的記憶體區域以達到連續檢視的目的的話,使用兩個暫存器會更加方便。

例如,我們用兩個暫存器(基址暫存器和變址暫存器)來表示記憶體的值

image.png

這種表示方式很類似陣列的構造,陣列是指同樣長度的資料在記憶體中進行連續排列的資料構造。用陣列名錶示陣列全部的值,通過索引來區分陣列的各個資料元素,例如: a[0] - a[4],[]內的 0 - 4 就是陣列的下標。

CPU 指令執行過程

那麼 CPU 是如何執行一條條的指令的呢?

幾乎所有的馮·諾伊曼型計算機的CPU,其工作都可以分為5個階段:取指令、指令譯碼、執行指令、訪存取數、結果寫回

  • 取指令階段是將記憶體中的指令讀取到 CPU 中暫存器的過程,程式暫存器用於儲存下一條指令所在的地址
  • 指令譯碼階段,在取指令完成後,立馬進入指令譯碼階段,在指令譯碼階段,指令譯碼器按照預定的指令格式,對取回的指令進行拆分和解釋,識別區分出不同的指令類別以及各種獲取運算元的方法。
  • 執行指令階段,譯碼完成後,就需要執行這一條指令了,此階段的任務是完成指令所規定的各種操作,具體實現指令的功能。
  • 訪問取數階段,根據指令的需要,有可能需要從記憶體中提取資料,此階段的任務是:根據指令地址碼,得到運算元在主存中的地址,並從主存中讀取該運算元用於運算。
  • 結果寫回階段,作為最後一個階段,結果寫回(Write Back,WB)階段把執行指令階段的執行結果資料“寫回”到某種儲存形式:結果資料經常被寫到CPU的內部暫存器中,以便被後續的指令快速地存取;

總結

本篇文章我們主要講述了

  • CPU 是什麼,CPU 的重要性,CPU 執行程式的過程
  • 還講述了 CPU 的內部結構,它的組成部分
  • 提到了組合語言和高階語言
  • 提到了CPU 與 暫存器的關係
  • 提到了主要的暫存器的功能,程式計數器,標誌暫存器,基址暫存器和變址暫存器
  • 還提到了函式呼叫機制是怎樣的。
  • CPU 指令的執行過程

下面為自己做個宣傳,歡迎關注公眾號 Java建設者,號主是Java技術棧,熱愛技術,喜歡閱讀,熱衷於分享和總結,希望能把每一篇好文章分享給成長道路上的你。我為你準備了百餘種優質電子書和各種資源,相信你一定會喜歡並收藏的。

參考資料:

Central_processing_unit

what-is-a-cpu

暫存器

記憶體

程式計數器

《程式是怎樣跑起來的》

相關文章