1.1 CPU的內部結構解析
CPU和內存是由許多晶體管組成的電子部件,通常稱為IC(Integrated Circuit,集成電路)。從功能方面來看,CPU的內部由寄存器,控制器,運算器和時鍾四部分構成,各部分之間由電流信號相互連通。
寄存器:可用來暫存指令,數據等處理對象,可以將其看做是內存的一種。根據種類的不同,一個CPU內部會有20~100個寄存器。
控制器:負責把內存上的指令,數據等讀入寄存器,並根據指令的執行結果來控制整個計算機。
運算器:負責運算從內存讀入寄存器的數據。
時鍾:負責發出CPU開始計時的時鍾信號。不過,也有些計算機的時鍾位於CPU的外部。
1.2 CPU是寄存器的集合體
程序是把寄存器作為對象來描述的
使用高級語言編寫的程序會在編譯后轉化成機器語言,然后通過CPU內部的寄存器來處理。不同類型的CPU,其內部寄存器的數量,種類以及寄存器存儲的數值范圍都是不同的。根據功能的不同,我們可以將寄存器大致划分為八類。
累加寄存器:存儲執行運算的數據和運算后的數據。
標志寄存器:存儲運算處理后的CPU的狀態。
程序計數器:存儲下一條指令所在內存的地址。
基址寄存器:存儲數據內存的起始地址。
變址寄存器:存儲基址寄存器的相對地址。
通用寄存器:存儲任意數據。
指令寄存器:存儲指令。CPU內部使用,程序員無法通過程序對該寄存器進行讀寫操作。
棧寄存器:存儲棧區域的起始地址。
其中,程序計數器,累加寄存器,標志寄存器,指令寄存器和棧寄存器都只有一個,其他的寄存器一般有多個。
1.3 決定程序流程的程序計數器
地址0100是程序運行的開始位置。Windows等操作系統把程序從硬盤復制到內存后,會將程序計數器(CPU寄存器的一種)設定為0100,然后程序開始運行。CPU每執行一個指令,程序計數器的值就會自動加1。例如,CPU執行0100地址的指令后,程序計算器的值就變成0101(當執行的指令占據多個內存地址時,增加與指令長度相應的數值)。然后,CPU的控制器就會參照程序計數器的數值,從內存中讀取命令並執行。也就是說,程序計數器決定着程序的流程。
1.4 條件分支和循環機制
程序的流程分為順序執行, 條件分支和循環三種。順序執行是指按照地址內容的順序執行指令。條件分支是指根據條件執行任意地址的指令。循環是指重復執行同一地址的指令。
上圖表示把內存中存儲的數值的絕對值輸出到顯示器的程序的內存狀態。程序運行的開始位置是0100地址。隨着程序計數器數值的增加,當達到0102地址時,如果累加寄存器的值是正數,則執行跳轉指令(jump指令)跳轉到0104地址。此時,由於累加寄存器的數值是123,為正數,因此0103地址的指令被跳過,程序的流程直接跳轉到0104地址。也就是說,“跳轉到0104地址”這個指令間接執行了“將程序計數器設定成0104地址”這個操作。
條件分支和循環中使用的跳轉指令,會參照當前執行的運算結果來判斷是否跳轉。無論當前累加寄存器的運算結果是負數,零還是正數,標志寄存器都會將其保存。CPU在進行運算時,標志寄存器的數值會根據運算結果自動設定。條件分支在跳轉指令前會進行比較運算。至於是否執行跳轉指令,則由CPU在參考標志寄存器的數值后進行判斷。運算結果的正,零,負三種狀態由標志寄存器的三個位表示。如圖:
標志寄存器的第一個字節位,第二個字節位和第三個字節位的值為1時,表示運算結果分別為正數、零和負數。
CPU執行比較的機制很有意思,因此請大家務必牢記。例如,假設要比較累加寄存器中存儲的XXX值和通用寄存器中存儲的YYY值,執行比較的指令后,CPU的運算裝置就會在內部(暗中)進行XXX-YYY的減法運算。而無論減法運算的結果是正數、零還是負數,都會保存在標志寄存器中。程序中的比較指令,就是在CPU內部做減法運算。
1.5函數的調用機制
哪怕是高級語言編寫的程序,函數調用處理也是通過把程序計數器的值設定成函數的存儲地址來實現的。
上圖是給變量a和b分別代入123和456后,將其賦值給參數(parameter)來調用MyFunc函數的C語言程序。圖中地址是將C語言編譯成機器語言后運行的地址。由於1行C語言程序在編譯后通常會變成多行的機器語言,所以圖中的地址是離散的。
此外,通過跳轉指令把程序計數器的值設定成0260也可實現調用MyFunc函數。函數的調用原點(0132地址)和被調用函數(0260地址)之間的數據傳遞,可以通過內存或寄存器來實現。
機器語言的call指令和return指令能夠解決這個問題。函數調用使用的是call指令,而不是跳轉指令。在將函數的入口地址設定到程序計數器之前,call指令會把調用函數后要執行的指令地址存儲在名為棧的主存內。函數處理完畢后,再通過函數的出口來執行return命令。return的功能是把保持在棧中的地址設定到程序計數器中。如圖所示,MyFunc函數被調用之前,0514地址保持在棧中。MyFunc函數的處理完畢后,棧中的0514地址就會被讀取出來,然后再被設定到程序計數器中。
1.6 通過地址和索引實現數組
如圖所示,出現的基址寄存器和變址寄存器。通過這兩個寄存器,我們可以對主內存上特定的內存區域進行划分,從而實現類似於數組的操作。
首先,用十六進制數將計算機內存上00000000~FFFFFFFF的地址分出來。那么,凡是該范圍的內存區域,只要是有一個32位的寄存器,即可查看全部的內存地址。但如果想要像數組那樣分割特定的內存區域以達到連續查看的目的,使用兩個寄存器會更方便些。例如查看10000000地址~1000FFFF地址時,可以將10000000存入基址寄存器,並使變址寄存器的值在0000000~0000FFFF變化。CPU則會把基址寄存器+變址寄存器的值解釋為實際查看的內存地址。變址寄存器的值就相當於高級編程語言程序中數組的索引功能。