CPU執行的也不僅是一條指令,通常一個程序包含不少條指令程序員
由於有if…else、for這樣的條件和循環存在,這些指令也不會一路平直執行下去。數組
一個計算機程序是怎麼被分解成一條條指令來執行的呢服務器
CPU裏差很少幾百億個晶體管架構
實際上,一條條計算機指令執行起來很是複雜函數
好在CPU在軟件層面已經爲咱們作好了封裝學習
對於程序員來講,咱們只要知道,寫好的代碼變成了指令以後,是一條一條順序執行測試
無論幾百億的晶體管的背後是怎麼經過電路運轉起來的spa
邏輯上,咱們能夠認爲,CPU其實就是由一堆寄存器組成的3d
而寄存器就是CPU內部,由多個觸發器(Flip-Flop)或者鎖存器(Latches)組成的簡單電路。code
觸發器和鎖存器,其實就是兩種不一樣原理的數字電路組成的邏輯門
若是想要深刻學習的話,能夠學習數字電路的相關課程
N個觸發器或者鎖存器,就能夠組成一個N位(Bit)的寄存器,可以保存N位的數據
比方說,咱們用的64位Intel服務器,寄存器就是64位的
CPU裏有不少種不一樣功能的
寄存器(Register),是中央處理器內的其中組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數據和地址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器。在中央處理器的算術及邏輯部件中,包含的寄存器有累加器。
在計算機體系結構裏,處理器中的寄存器是少許且速度快的計算機存儲器,藉由提供快速共同地訪問數值來加速計算機程序的運行:典型地說就是在已知時間點所做的之計算中間的數值。
寄存器是存儲器層次結構中的最頂端,也是系統操做數據的最快速途徑。寄存器一般都是以他們能夠保存的比特數量來估量,舉例來講,一個8位寄存器或32位寄存器。寄存器如今都以寄存器數組的方式來實現,可是他們也可能使用單獨的觸發器、高速的核心存儲器、薄膜存儲器以及在數種機器上的其餘方式來實現出來。
這個名詞一般都用來意指由一個指令之輸出或輸入能夠直接索引到的寄存器組羣。更適當的是稱他們爲「架構寄存器」。例如,x86指令集定義八個32位寄存器的集合,但一個實現x86指令集的CPU能夠包含比八個更多的寄存器。
亦稱指令地址寄存器(Instruction Address Register)
存放下一條須要執行的計算機指令的內存地址
存放當前正在執行的指令
用裏面的一個一個標記位(Flag),存放CPU進行算術或者邏輯計算的結果
CPU裏面還有更多用來存儲數據和內存地址的寄存器
這樣的寄存器一般一類裏面不止一個
一般根據存放的數據內容來給它們取名字,好比
用來持有隻讀的數值(例如0、一、圓周率等等)。因爲「其中的值不可更改」這一特殊性質,這些寄存器未必會有實體的硬件電路相對應,例如將從零常數寄存器讀的操做實現爲接通目標寄存器的下拉電阻。
通常而言,即便真正在硬件中放置常數寄存器也未必會是出於體系結構理論上的考慮,而極可能是由硬件描述語言爲了簡化操做而自動生成的電路
用來存儲整數數字(參考如下的浮點寄存器)。在某些簡單(或舊)的CPU,特別的數據寄存器是累加器,做爲數學計算之用。
用來存儲浮點數字。
用來存儲由向量處理器運行SIMD指令所獲得的數據。
持有存儲器地址,以及用來訪問存儲器。在某些簡單/舊的CPU裏,特別的地址寄存器是索引寄存器(可能出現一個或多個)。
有些寄存器既能夠存放數據,又能存放地址,咱們就叫它通用寄存器(GPRs)。
程序執行的時候,CPU會
能夠看到,一個程序的一條條指令,在內存裏是連續保存的,也會一條條順序加載
而有些特殊指令,好比上一講咱們講到J類指令,也就是跳轉指令,會修改PC寄存器裏面的地址值
這樣,下一條要執行的指令就不是從內存裏面順序加載的了
事實上,這些跳轉指令的存在,也是咱們能夠在寫程序的時候,使用
的緣由
咱們如今就來看一個包含if…else的簡單程序。
用rand生成了一個隨機數r(0/1)
咱們把這個程序編譯成彙編代碼。你能夠忽略先後無關的代碼,只關注於這裏的if…else條件判斷語句
對於r == 0的條件判斷,被編譯成了cmp和jne兩條指令。
DWORD PTR 表明操做的數據類型是32位的整數
rbp-0x4則是一個寄存器的地址
第一個操做數就是從寄存器裏拿到的變量r的值
第二個操做數0x0就是咱們設定的常量0的16進製表示
cmp指令的比較結果,會存入到條件碼寄存器
狀態寄存器又名條件碼寄存器,它是計算機系統的核心部件——運算器的一部分
狀態寄存器用來存放兩類信息:一類是體現當前指令執行結果的各類狀態信息(條件碼),若有無進位(CF位)、有無溢出(OF位)、結果正負(SF位)、結果是否爲零(ZF位)、奇偶標誌位(P位)等另外一類是存放控制信息(PSW:程序狀態字寄存器),如容許中斷(IF位)、跟蹤標誌(TF位)等
有些機器中將PSW稱爲標誌寄存器FR(Flag Register)。
若是比較結果 True,即 r == 0,就把零標誌條件碼(對應的條件碼是ZF,Zero Flag)設置爲1
條件碼是CPU根據運算結果由硬件設置的位,體現當前指令執行結果的各類狀態信息
例如:算術運算產生的正、負、零或溢出等的結果。條件碼可被測試,做爲分支運算的依據,此外,有些條件碼可被設置,例如對於最高位進位標誌C,可用指令對它置位和復位。
Intel的CPU下還有
最近的操做使最高位產生了進位。能夠用來檢查無符號操做數據的溢出。
最近的操做獲得的結果爲負數。
最近的操做致使一個補碼溢出--正溢出或負溢出
用在不一樣的判斷條件下。
cmp指令執行完成以後,PC寄存器會自增,開始執行下一條jne的指令
跟着的jne指令(jump if not equal),它會查看對應的零標誌位
若是爲0,會跳轉到後面跟着的操做數4a的位置
4a,對應彙編代碼的行號,也就是else條件裏的第一條指令
當跳轉發生,PC寄存器再也不是自增變成下一條指令的地址,而被直接設置4a這個地址
這個時候,CPU再把4a地址裏的指令加載到指令寄存器執行。
跳轉到執行地址爲4a的指令,實際是一條mov指令
第一個操做數和前面的cmp指令同樣,是另外一個32位整型的寄存器地址,以及對應的2的16進制值0x2
mov指令把2設置到對應的寄存器裏去,至關於一個賦值操做
而後,PC寄存器裏的值繼續自增,執行下一條mov指令。
這條mov指令的第一個操做數eax,表明累加寄存器
在中央處理器中,累加器 (accumulator) 是一種寄存器,用來儲存計算產生的中間結果。若是沒有像累加器這樣的寄存器,那麼在每次計算 (加法,乘法,移位等等) 後就必需要把結果寫回到 內存,也許立刻就得讀回來。然而存取主存的速度是比從算術邏輯單元到有直接路徑的累加器存取更慢。
第二個操做數0x0則是16進制的0的表示。這條指令其實沒有實際的做用,它的做用是一個佔位符
if條件若是知足,在賦值的mov指令執行完成以後,有一個jmp的無條件跳轉指令
跳轉的地址就是這一行的地址51
咱們的main函數沒有設定返回值,而mov eax, 0x0 其實就是給main函數生成了一個默認的爲0的返回值到累加器裏面
if條件裏面的內容執行完成以後也會跳轉到這裏,和else裏的內容結束以後的位置是同樣的。
上一講咱們講打孔卡的時候說到,讀取打孔卡的機器會順序地一段一段地讀取指令,而後執行。
執行完一條指令,它會自動地順序讀取下一條指令
若是執行的當前指令帶有跳轉的地址,好比日後跳10個指令,那麼機器會自動將卡片帶日後移動10個指令的位置,再來執行指令
一樣的,機器也能向前移動,去讀取以前已經執行過的指令
這也就是咱們的while/for循環實現的原理。
如何經過if…else和goto來實現循環?
咱們再看一段簡單的利用for循環的程序。咱們循環自增變量i三次,三次以後,i>=3,就會跳出循環。整個程序,對應的Intel彙編代碼就是這樣的:
能夠看到,對應的循環也是用1e這個地址上的cmp比較指令
和緊接着的jle條件跳轉指令來實現的
主要的差異在於,這裏的jle跳轉的地址,在這條指令以前的地址14,而非if…else編譯出來的跳轉指令以後
往前跳轉使得條件知足的時候,PC寄存器會把指令地址設置到以前執行過的指令位置,從新執行以前執行過的指令,直到條件不知足,順序往下執行jle以後的指令,整個循環才結束。
若是你看一長條打孔卡的話,就會看到卡片日後移動一段,執行了以後,又反向移動,去從新執行前面的指令。
jle和jmp指令,有點像程序語言裏面的goto命令,直接指定了一個特定條件下的跳轉位置
雖然咱們在用高級語言開發程序的時候反對使用goto,可是實際在機器指令層面,不管是if…else…也好,仍是for/while也好,都是用和goto相同的跳轉到特定指令位置的方式來實現的。
學習了程序裏的多條指令,到底是怎麼樣一條一條被執行的
除了簡單地經過PC寄存器自增的方式順序執行外
條件碼寄存器會記錄下當前執行指令的條件判斷狀態
而後經過跳轉指令讀取對應的條件碼
修改PC寄存器內的下一條指令的地址
最終實現if…else以及for/while這樣的程序控制流程。
雖然咱們能夠用高級語言,能夠用不一樣的語法,好比 if…else 這樣的條件分支,或者 while/for 這樣的循環方式,來實現不用的程序運行流程
可是迴歸到計算機能夠識別的機器指令級別,其實都只是一個簡單的地址跳轉而已,也就是一個相似於goto的語句。
想要在硬件層面實現這個goto語句,除了自己須要用來保存下一條指令地址,以及當前正要執行指令的PC寄存器、指令寄存器外
咱們只須要再增長一個條件碼寄存器,來保留條件判斷的狀態。這樣簡簡單單的三個寄存器,就能夠實現條件判斷和循環重複執行代碼的功能。
詳細講解了C語言和Intel CPU的彙編語言以及指令的對應關係,以及Intel CPU的各類寄存器和指令集。
Intel指令集相對於以前的MIPS指令集要複雜一些
從1個字節到15個字節不等