你們都是程序員,你們都是和計算機打交道的程序員,你們都是和計算機中軟件硬件打交道的程序員,你們都是和CPU
打交道的程序員,因此,無論你是玩兒硬件的仍是作軟件的,你的世界都少不了計算機最核心的 - CPUhtml
CPU 的全稱是 Central Processing Unit
,它是你的電腦中最硬核
的組件,這種說法一點不爲過。CPU 是可以讓你的計算機叫計算機
的核心組件,可是它卻不能表明你的電腦,CPU 與計算機的關係就至關於大腦和人的關係。它是一種小型的計算機芯片,它嵌入在臺式機、筆記本電腦或者平板電腦的主板上。經過在單個計算機芯片上放置數十億個微型晶體管來構建 CPU。 這些晶體管使它可以執行運行存儲在系統內存中的程序所需的計算,也就是說 CPU 決定了你電腦的計算能力。git
CPU 的核心是從程序或應用程序獲取指令並執行計算。此過程能夠分爲三個關鍵階段:提取,解碼和執行。CPU從系統的 RAM 中提取指令,而後解碼該指令的實際內容,而後再由 CPU 的相關部分執行該指令。程序員
RAM : 隨機存取存儲器(英語:Random Access Memory,縮寫:RAM),也叫主存,是與 CPU 直接交換數據的內部存儲器。它能夠隨時讀寫(刷新時除外),並且速度很快,一般做爲操做系統或其餘正在運行中的程序的臨時數據存儲介質數組
說了這麼多 CPU 的重要性,那麼 CPU 的內部結構是什麼呢?又是由什麼組成的呢?下圖展現了通常程序的運行流程(以 C 語言爲例),能夠說了解程序的運行流程是掌握程序運行機制的基礎和前提。dom
在這個流程中,CPU 負責的就是解釋和運行最終轉換成機器語言的內容。函數
CPU 主要由兩部分構成:控制單元
和 算術邏輯單元(ALU)
操作系統
CPU 是計算機的心臟和大腦,它和內存都是由許多晶體管組成的電子部件。它接收數據輸入,執行指令並處理信息。它與輸入/輸出(I / O)設備進行通訊,這些設備向 CPU 發送數據和從 CPU 接收數據。.net
從功能來看,CPU 的內部由寄存器、控制器、運算器和時鐘四部分組成,各部分之間經過電信號連通。翻譯
寄存器
是中央處理器內的組成部分。它們能夠用來暫存指令、數據和地址。能夠將其看做是內存的一種。根據種類的不一樣,一個 CPU 內部會有 20 - 100個寄存器。控制器
負責把內存上的指令、數據讀入寄存器,並根據指令的結果控制計算機運算器
負責運算從內存中讀入寄存器的數據時鐘
負責發出 CPU 開始計時的時鐘信號接下來簡單解釋一下內存,爲何說 CPU 須要講一下內存呢,由於內存是與 CPU 進行溝通的橋樑。計算機全部程序的運行都是在內存中運行的,內存又被稱爲主存
,其做用是存放 CPU 中的運算數據,以及與硬盤等外部存儲設備交換的數據。只要計算機在運行中,CPU 就會把須要運算的數據調到主存中進行運算,當運算完成後CPU再將結果傳送出來,主存的運行也決定了計算機的穩定運行。3d
主存經過控制芯片與 CPU 進行相連,由可讀寫的元素構成,每一個字節(1 byte = 8 bits)都帶有一個地址編號,注意是一個字節,而不是一個位。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內部使用,程序員沒法對該寄存器進行讀寫 |
棧寄存器 | 存儲棧區域的起始位置 |
其中程序計數器、累加寄存器、標誌寄存器、指令寄存器和棧寄存器都只有一個,其餘寄存器通常有多個。
程序計數器(Program Counter)
是用來存儲下一條指令所在單元的地址。
程序執行時,PC的初值爲程序第一條指令的地址,在順序執行程序時,控制器
首先按程序計數器所指出的指令地址從內存中取出一條指令,而後分析和執行該指令,同時將PC的值加1指向下一條要執行的指令。
咱們仍是以一個事例爲準來詳細的看一下程序計數器的執行過程
這是一段進行相加的操做,程序啓動,在通過編譯解析後會由操做系統把硬盤中的程序複製到內存中,示例中的程序是將 123 和 456 執行相加操做,並將結果輸出到顯示器上。因爲使用機器語言難以描述,因此這是通過翻譯後的結果,實際上每一個指令和數據均可能分佈在不一樣的地址上,但爲了方便說明,把組成一條指令的內存和數據放在了一個內存地址上。
地址 0100
是程序運行的起始位置。Windows 等操做系統把程序從硬盤複製到內存後,會將程序計數器做爲設定爲起始位置 0100,而後執行程序,每執行一條指令後,程序計數器的數值會增長1(或者直接指向下一條指令的地址),而後,CPU 就會根據程序計數器的數值,從內存中讀取命令並執行,也就是說,程序計數器控制着程序的流程。
咱們都學太高級語言,高級語言中的條件控制流程主要分爲三種:順序執行、條件分支、循環判斷
三種,順序執行是按照地址的內容順序的執行指令。條件分支是根據條件執行任意地址的指令。循環是重複執行同一地址的指令。
下面以條件分支爲例來講明程序的執行過程(循環也很類似)
程序的開始過程和順序流程是同樣的,CPU 從0100處開始執行命令,在0100和0101都是順序執行,PC 的值順序+1,執行到0102地址的指令時,判斷0106寄存器的數值大於0,跳轉(jump)到0104地址的指令,將數值輸出到顯示器中,而後結束程序,0103 的指令被跳過了,這就和咱們程序中的 if()
判斷是同樣的,在不知足條件的狀況下,指令會直接跳過。因此 PC 的執行過程也就沒有直接+1,而是下一條指令的地址。
條件和循環分支會使用到 jump(跳轉指令)
,會根據當前的指令來判斷是否跳轉,上面咱們提到了標誌寄存器
,不管當前累加寄存器的運算結果是正數、負數仍是零,標誌寄存器都會將其保存(也負責溢出和奇偶校驗)
溢出(overflow):是指運算的結果超過了寄存器的長度範圍
奇偶校驗(parity check):是指檢查運算結果的值是偶數仍是奇數
CPU 在進行運算時,標誌寄存器的數值會根據當前運算的結果自動設定,運算結果的正、負和零三種狀態由標誌寄存器的三個位表示。標誌寄存器的第一個字節位、第二個字節位、第三個字節位各自的結果都爲1時,分別表明着正數、零和負數。
CPU 的執行機制比較有意思,假設累加寄存器中存儲的 XXX 和通用寄存器中存儲的 YYY 作比較,執行比較的背後,CPU 的運算機制就會作減法運算。而不管減法運算的結果是正數、零仍是負數,都會保存到標誌寄存器中。結果爲正表示 XXX 比 YYY 大,結果爲零表示 XXX 和 YYY 相等,結果爲負表示 XXX 比 YYY 小。程序比較的指令,其實是在 CPU 內部作減法
運算。
接下來,咱們繼續介紹函數調用機制,哪怕是高級語言編寫的程序,函數調用處理也是經過把程序計數器的值設定成函數的存儲地址來實現的。函數執行跳轉指令後,必須進行返回處理,單純的指令跳轉沒有意義,下面是一個實現函數跳轉的例子
圖中將變量 a 和 b 分別賦值爲 123 和 456 ,調用 MyFun(a,b) 方法,進行指令跳轉。圖中的地址是將 C 語言編譯成機器語言後運行時的地址,因爲1行 C 程序在編譯後一般會變爲多行機器語言,因此圖中的地址是分散的。在執行完 MyFun(a,b)指令後,程序會返回到 MyFun(a,b) 的下一條指令,CPU 繼續執行下面的指令。
函數的調用和返回很重要的兩個指令是 call
和 return
指令,再將函數的入口地址設定到程序計數器以前,call 指令會把調用函數後要執行的指令地址存儲在名爲棧的主存內。函數處理完畢後,再經過函數的出口來執行 return 指令。return 指令的功能是把保存在棧中的地址設定到程序計數器。MyFun 函數在被調用以前,0154 地址保存在棧中,MyFun 函數處理完成後,會把0154的地址保存在程序計數器中。這個調用過程以下
在一些高級語言的條件或者循環語句中,函數調用的處理會轉換成 call 指令,函數結束後的處理則會轉換成 return 指令。
接下來咱們看一下基址寄存器和變址寄存器,經過這兩個寄存器,咱們能夠對主存上的特定區域進行劃分,來實現相似數組的操做,首先,咱們用十六進制數將計算機內存上的 00000000 - FFFFFFFF 的地址劃分出來。那麼,凡是該範圍的內存地址,只要有一個 32 位的寄存器,即可查看所有地址。但若是想要想數組那樣分割特定的內存區域以達到連續查看的目的的話,使用兩個寄存器會更加方便。
例如,咱們用兩個寄存器(基址寄存器和變址寄存器)來表示內存的值
這種表示方式很相似數組的構造,數組
是指一樣長度的數據在內存中進行連續排列的數據構造。用數組名錶示數組所有的值,經過索引來區分數組的各個數據元素,例如: a[0] - a[4],[]
內的 0 - 4 就是數組的下標。
那麼 CPU 是如何執行一條條的指令的呢?
幾乎全部的馮·諾伊曼型計算機的CPU,其工做均可以分爲5個階段:取指令、指令譯碼、執行指令、訪存取數、結果寫回。
取指令
階段是將內存中的指令讀取到 CPU 中寄存器的過程,程序寄存器用於存儲下一條指令所在的地址指令譯碼
階段,在取指令完成後,立馬進入指令譯碼階段,在指令譯碼階段,指令譯碼器按照預約的指令格式,對取回的指令進行拆分和解釋,識別區分出不一樣的指令類別以及各類獲取操做數的方法。執行指令
階段,譯碼完成後,就須要執行這一條指令了,此階段的任務是完成指令所規定的各類操做,具體實現指令的功能。訪問取數
階段,根據指令的須要,有可能須要從內存中提取數據,此階段的任務是:根據指令地址碼,獲得操做數在主存中的地址,並從主存中讀取該操做數用於運算。結果寫回
階段,做爲最後一個階段,結果寫回(Write Back,WB)階段把執行指令階段的運行結果數據「寫回」到某種存儲形式:結果數據常常被寫到CPU的內部寄存器中,以便被後續的指令快速地存取;本篇文章咱們主要講述了
下面爲本身作個宣傳,歡迎關注公衆號 Java建設者
,號主是Java技術棧,熱愛技術,喜歡閱讀,熱衷於分享和總結,但願能把每一篇好文章分享給成長道路上的你。關注公衆號回覆 002
領取爲你特地準備的大禮包,你必定會喜歡並收藏的。
參考資料:
https://en.wikipedia.org/wiki/Central_processing_unit
https://www.digitaltrends.com/computing/what-is-a-cpu/
https://baike.baidu.com/item/寄存器/187682?fr=aladdin
https://baike.baidu.com/item/內存/103614?fr=aladdin
http://www.javashuo.com/article/p-plsuekss-kh.html
https://baike.baidu.com/item/程序計數器/3219536?fr=aladdin
《程序是怎樣跑起來的》