摘要:在中央處理器的控制部件中,包含的寄存器有指令寄存器和程序計數器。這條指令的第一個操作數,代表累加寄存器在中央處理器中,累加器是一種寄存器,用來儲存計算產生的中間結果。第二個操作數則是進制的的表示。
CPU執行的也不只是一條指令,一般一個程序包含很多條指令
因為有if…else、for這樣的條件和循環存在,這些指令也不會一路平直執行下去。
一個計算機程序是怎么被分解成一條條指令來執行的呢
1 CPU如何執行指令CPU里差不多幾百億個晶體管
實際上,一條條計算機指令執行起來非常復雜
好在CPU在軟件層面已經為我們做好了封裝
對于程序員來說,我們只要知道,寫好的代碼變成了指令之后,是一條一條順序執行
不管幾百億的晶體管的背后是怎么通過電路運轉起來的
邏輯上,我們可以認為,CPU其實就是由一堆寄存器組成的
而寄存器就是CPU內部,由多個觸發器(Flip-Flop)或者鎖存器(Latches)組成的簡單電路。
觸發器和鎖存器,其實就是兩種不同原理的數字電路組成的邏輯門
如果想要深入學習的話,可以學習數字電路的相關課程
N個觸發器或者鎖存器,就可以組成一個N位(Bit)的寄存器,能夠保存N位的數據
比方說,我們用的64位Intel服務器,寄存器就是64位的
CPU里有很多種不同功能的
1.1 寄存器寄存器(Register),是中央處理器內的其中組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數據和地址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器。在中央處理器的算術及邏輯部件中,包含的寄存器有累加器。
在計算機體系結構里,處理器中的寄存器是少量且速度快的計算機存儲器,借由提供快速共同地訪問數值來加速計算機程序的運行:典型地說就是在已知時間點所作的之計算中間的數值。
寄存器是存儲器層次結構中的最頂端,也是系統操作數據的最快速途徑。寄存器通常都是以他們可以保存的比特數量來估量,舉例來說,一個8位寄存器或32位寄存器。寄存器現在都以寄存器數組的方式來實現,但是他們也可能使用多帶帶的觸發器、高速的核心存儲器、薄膜存儲器以及在數種機器上的其他方式來實現出來。
這個名詞通常都用來意指由一個指令之輸出或輸入可以直接索引到的寄存器組群。更適當的是稱他們為“架構寄存器”。例如,x86指令集定義八個32位寄存器的集合,但一個實現x86指令集的CPU可以包含比八個更多的寄存器。
1.1.1 PC寄存器(Program Counter Register)亦稱指令地址寄存器(Instruction Address Register)
存放下一條需要執行的計算機指令的內存地址
1.1.2 指令寄存器(Instruction Register)存放當前正在執行的指令
1.1.3 條件碼寄存器(Status Register)用里面的一個一個標記位(Flag),存放CPU進行算術或者邏輯計算的結果
CPU里面還有更多用來存儲數據和內存地址的寄存器
這樣的寄存器通常一類里面不止一個
通常根據存放的數據內容來給它們取名字,比如
常量寄存器
用來持有只讀的數值(例如0、1、圓周率等等)。由于“其中的值不可更改”這一特殊性質,這些寄存器未必會有實體的硬件電路相對應,例如將從零常數寄存器讀的操作實現為接通目標寄存器的下拉電阻。
一般而言,即使真正在硬件中放置常數寄存器也未必會是出于體系結構理論上的考慮,而很可能是由硬件描述語言為了簡化操作而自動生成的電路
整數寄存器
用來存儲整數數字(參考以下的浮點寄存器)。在某些簡單(或舊)的CPU,特別的數據寄存器是累加器,作為數學計算之用。
浮點數寄存器(FPRs)
用來存儲浮點數字。
向量寄存器
用來存儲由向量處理器運行SIMD指令所得到的數據。
地址寄存器
持有存儲器地址,以及用來訪問存儲器。在某些簡單/舊的CPU里,特別的地址寄存器是索引寄存器(可能出現一個或多個)。
有些寄存器既可以存放數據,又能存放地址,我們就叫它通用寄存器(GPRs)。
程序執行的時候,CPU會
根據PC寄存器里的地址
從內存里面把需要執行的指令讀取到指令寄存器里面執行
然后根據指令長度自增
開始順序讀取下一條指令
可以看到,一個程序的一條條指令,在內存里是連續保存的,也會一條條順序加載
而有些特殊指令,比如上一講我們講到J類指令,也就是跳轉指令,會修改PC寄存器里面的地址值
這樣,下一條要執行的指令就不是從內存里面順序加載的了
事實上,這些跳轉指令的存在,也是我們可以在寫程序的時候,使用
if…else條件語句
while/for循環語句
的原因
2 從if/else看程序的執行和跳轉我們現在就來看一個包含if…else的簡單程序。
test.c
用rand生成了一個隨機數r(0/1)
當r是0,我們把之前定義的變量a設成1
不然就設成2
我們把這個程序編譯成匯編代碼。你可以忽略前后無關的代碼,只關注于這里的if…else條件判斷語句
對應的匯編代碼是這樣的
對于r == 0的條件判斷,被編譯成了cmp和jne兩條指令。
cmp指令比較了前后兩個操作數的值
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下還有
進位標志(CF,Carry Flag)
最近的操作使最高位產生了進位。可以用來檢查無符號操作數據的溢出。
符號標志(SF,Sign Flag)
最近的操作得到的結果為負數。
溢出標志(OF,Overflow Flag)
最近的操作導致一個補碼溢出--正溢出或負溢出
用在不同的判斷條件下。
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相同的跳轉到特定指令位置的方式來實現的。
3 總結學習了程序里的多條指令,究竟是怎么樣一條一條被執行的
除了簡單地通過PC寄存器自增的方式順序執行外
條件碼寄存器會記錄下當前執行指令的條件判斷狀態
然后通過跳轉指令讀取對應的條件碼
修改PC寄存器內的下一條指令的地址
最終實現if…else以及for/while這樣的程序控制流程。
雖然我們可以用高級語言,可以用不同的語法,比如 if…else 這樣的條件分支,或者 while/for 這樣的循環方式,來實現不用的程序運行流程
但是回歸到計算機可以識別的機器指令級別,其實都只是一個簡單的地址跳轉而已,也就是一個類似于goto的語句。
想要在硬件層面實現這個goto語句,除了本身需要用來保存下一條指令地址,以及當前正要執行指令的PC寄存器、指令寄存器外
我們只需要再增加一個條件碼寄存器,來保留條件判斷的狀態。這樣簡簡單單的三個寄存器,就可以實現條件判斷和循環重復執行代碼的功能。
4 推薦閱讀《深入理解計算機系統》的第3章
詳細講解了C語言和Intel CPU的匯編語言以及指令的對應關系,以及Intel CPU的各種寄存器和指令集。
Intel指令集相對于之前的MIPS指令集要復雜一些
所有的指令是變長的
從1個字節到15個字節不等
即使是匯編代碼,還有很多針對操作數據的長度不同有不同的后綴
參考狀態寄存器
寄存器
條件碼
累加器
深入淺出計算機組成原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76124.html
摘要:而大寫字母,就是第個,也就是二進制的,對應的十六進制表示就是。在中文世界里,最典型的就是手持兩把錕斤拷,口中疾呼燙燙燙的典故。既然今天要徹底搞清楚編碼知識,我們就來弄清楚錕斤拷和燙燙燙的來龍去脈。參考深入淺出計算機組成原理 showImg(https://image-static.segmentfault.com/206/872/2068726052-5d5922b2effb9_art...
摘要:公元前年,在雅典附近的馬拉松海邊,發生了波斯和希臘之間的希波戰爭。因為電報員要熟記每一個字母對應的摩爾斯電碼,并且需要快速按鍵來進行輸入。 showImg(https://image-static.segmentfault.com/548/184/548184927-5d5962191cbce_articlex); 人用紙和筆來做運算,都是用十進制,直接用十進制和我們最熟悉的符號不是最...
摘要:匯編器是怎么把對應的匯編代碼,翻譯成為機器碼的。總結打孔卡,其實就是一種存儲程序型計算機。推薦閱讀了解的指令集參看計算機組成與設計軟硬件接口第版的小節參考深入淺出計算機組成原理 你在學寫程序的時候,有沒有想過,古老年代的計算機程序是怎么寫出來的?showImg(https://ask.qcloudimg.com/http-save/1752328/fpfs9776q8.png); 當...
摘要:計算機組成中的大量原理和設計,都對應著性能這個詞。時間的倒數性能計算機的性能,其實和體力勞動很像,好比是我們要搬東西。對于計算機的性能,我們需要有個標準來衡量。花的時間越少,自然性能就越好。 0 學習路線的知識點概括 showImg(https://segmentfault.com/img/remote/1460000020031616?w=3832&h=2540); 學習計算機組成原...
摘要:語義如何運用語義類標簽來呈現網頁通過網頁案例來學習語義類標簽最初的設計場景就是超文本,早期工作組的專家都是出版界書籍排版的專家。標記的部分有三個注記,它在文章中的作用就是額外的注釋,但是中并沒有相關的語義,這時可以使用標簽進行相關實現。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一...
閱讀 2770·2021-09-24 10:34
閱讀 1870·2021-09-22 10:02
閱讀 2258·2021-09-09 09:33
閱讀 1462·2021-08-13 15:02
閱讀 3273·2020-12-03 17:10
閱讀 1185·2019-08-30 15:44
閱讀 2151·2019-08-30 12:58
閱讀 3234·2019-08-26 13:40