摘要:在中央處理器的控制部件中,包含的寄存器有指令寄存器和程序計(jì)數(shù)器。這條指令的第一個(gè)操作數(shù),代表累加寄存器在中央處理器中,累加器是一種寄存器,用來(lái)儲(chǔ)存計(jì)算產(chǎn)生的中間結(jié)果。第二個(gè)操作數(shù)則是進(jìn)制的的表示。
CPU執(zhí)行的也不只是一條指令,一般一個(gè)程序包含很多條指令
因?yàn)橛衖f…else、for這樣的條件和循環(huán)存在,這些指令也不會(huì)一路平直執(zhí)行下去。
一個(gè)計(jì)算機(jī)程序是怎么被分解成一條條指令來(lái)執(zhí)行的呢
1 CPU如何執(zhí)行指令CPU里差不多幾百億個(gè)晶體管
實(shí)際上,一條條計(jì)算機(jī)指令執(zhí)行起來(lái)非常復(fù)雜
好在CPU在軟件層面已經(jīng)為我們做好了封裝
對(duì)于程序員來(lái)說(shuō),我們只要知道,寫(xiě)好的代碼變成了指令之后,是一條一條順序執(zhí)行
不管幾百億的晶體管的背后是怎么通過(guò)電路運(yùn)轉(zhuǎn)起來(lái)的
邏輯上,我們可以認(rèn)為,CPU其實(shí)就是由一堆寄存器組成的
而寄存器就是CPU內(nèi)部,由多個(gè)觸發(fā)器(Flip-Flop)或者鎖存器(Latches)組成的簡(jiǎn)單電路。
觸發(fā)器和鎖存器,其實(shí)就是兩種不同原理的數(shù)字電路組成的邏輯門(mén)
如果想要深入學(xué)習(xí)的話,可以學(xué)習(xí)數(shù)字電路的相關(guān)課程
N個(gè)觸發(fā)器或者鎖存器,就可以組成一個(gè)N位(Bit)的寄存器,能夠保存N位的數(shù)據(jù)
比方說(shuō),我們用的64位Intel服務(wù)器,寄存器就是64位的
CPU里有很多種不同功能的
1.1 寄存器寄存器(Register),是中央處理器內(nèi)的其中組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來(lái)暫存指令、數(shù)據(jù)和地址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計(jì)數(shù)器。在中央處理器的算術(shù)及邏輯部件中,包含的寄存器有累加器。
在計(jì)算機(jī)體系結(jié)構(gòu)里,處理器中的寄存器是少量且速度快的計(jì)算機(jī)存儲(chǔ)器,借由提供快速共同地訪問(wèn)數(shù)值來(lái)加速計(jì)算機(jī)程序的運(yùn)行:典型地說(shuō)就是在已知時(shí)間點(diǎn)所作的之計(jì)算中間的數(shù)值。
寄存器是存儲(chǔ)器層次結(jié)構(gòu)中的最頂端,也是系統(tǒng)操作數(shù)據(jù)的最快速途徑。寄存器通常都是以他們可以保存的比特?cái)?shù)量來(lái)估量,舉例來(lái)說(shuō),一個(gè)8位寄存器或32位寄存器。寄存器現(xiàn)在都以寄存器數(shù)組的方式來(lái)實(shí)現(xiàn),但是他們也可能使用多帶帶的觸發(fā)器、高速的核心存儲(chǔ)器、薄膜存儲(chǔ)器以及在數(shù)種機(jī)器上的其他方式來(lái)實(shí)現(xiàn)出來(lái)。
這個(gè)名詞通常都用來(lái)意指由一個(gè)指令之輸出或輸入可以直接索引到的寄存器組群。更適當(dāng)?shù)氖欠Q他們?yōu)椤凹軜?gòu)寄存器”。例如,x86指令集定義八個(gè)32位寄存器的集合,但一個(gè)實(shí)現(xiàn)x86指令集的CPU可以包含比八個(gè)更多的寄存器。
1.1.1 PC寄存器(Program Counter Register)亦稱指令地址寄存器(Instruction Address Register)
存放下一條需要執(zhí)行的計(jì)算機(jī)指令的內(nèi)存地址
1.1.2 指令寄存器(Instruction Register)存放當(dāng)前正在執(zhí)行的指令
1.1.3 條件碼寄存器(Status Register)用里面的一個(gè)一個(gè)標(biāo)記位(Flag),存放CPU進(jìn)行算術(shù)或者邏輯計(jì)算的結(jié)果
CPU里面還有更多用來(lái)存儲(chǔ)數(shù)據(jù)和內(nèi)存地址的寄存器
這樣的寄存器通常一類里面不止一個(gè)
通常根據(jù)存放的數(shù)據(jù)內(nèi)容來(lái)給它們?nèi)∶?,比?/p>
常量寄存器
用來(lái)持有只讀的數(shù)值(例如0、1、圓周率等等)。由于“其中的值不可更改”這一特殊性質(zhì),這些寄存器未必會(huì)有實(shí)體的硬件電路相對(duì)應(yīng),例如將從零常數(shù)寄存器讀的操作實(shí)現(xiàn)為接通目標(biāo)寄存器的下拉電阻。
一般而言,即使真正在硬件中放置常數(shù)寄存器也未必會(huì)是出于體系結(jié)構(gòu)理論上的考慮,而很可能是由硬件描述語(yǔ)言為了簡(jiǎn)化操作而自動(dòng)生成的電路
整數(shù)寄存器
用來(lái)存儲(chǔ)整數(shù)數(shù)字(參考以下的浮點(diǎn)寄存器)。在某些簡(jiǎn)單(或舊)的CPU,特別的數(shù)據(jù)寄存器是累加器,作為數(shù)學(xué)計(jì)算之用。
浮點(diǎn)數(shù)寄存器(FPRs)
用來(lái)存儲(chǔ)浮點(diǎn)數(shù)字。
向量寄存器
用來(lái)存儲(chǔ)由向量處理器運(yùn)行SIMD指令所得到的數(shù)據(jù)。
地址寄存器
持有存儲(chǔ)器地址,以及用來(lái)訪問(wèn)存儲(chǔ)器。在某些簡(jiǎn)單/舊的CPU里,特別的地址寄存器是索引寄存器(可能出現(xiàn)一個(gè)或多個(gè))。
有些寄存器既可以存放數(shù)據(jù),又能存放地址,我們就叫它通用寄存器(GPRs)。
程序執(zhí)行的時(shí)候,CPU會(huì)
根據(jù)PC寄存器里的地址
從內(nèi)存里面把需要執(zhí)行的指令讀取到指令寄存器里面執(zhí)行
然后根據(jù)指令長(zhǎng)度自增
開(kāi)始順序讀取下一條指令
可以看到,一個(gè)程序的一條條指令,在內(nèi)存里是連續(xù)保存的,也會(huì)一條條順序加載
而有些特殊指令,比如上一講我們講到J類指令,也就是跳轉(zhuǎn)指令,會(huì)修改PC寄存器里面的地址值
這樣,下一條要執(zhí)行的指令就不是從內(nèi)存里面順序加載的了
事實(shí)上,這些跳轉(zhuǎn)指令的存在,也是我們可以在寫(xiě)程序的時(shí)候,使用
if…else條件語(yǔ)句
while/for循環(huán)語(yǔ)句
的原因
2 從if/else看程序的執(zhí)行和跳轉(zhuǎn)我們現(xiàn)在就來(lái)看一個(gè)包含if…else的簡(jiǎn)單程序。
test.c
用rand生成了一個(gè)隨機(jī)數(shù)r(0/1)
當(dāng)r是0,我們把之前定義的變量a設(shè)成1
不然就設(shè)成2
我們把這個(gè)程序編譯成匯編代碼。你可以忽略前后無(wú)關(guān)的代碼,只關(guān)注于這里的if…else條件判斷語(yǔ)句
對(duì)應(yīng)的匯編代碼是這樣的
對(duì)于r == 0的條件判斷,被編譯成了cmp和jne兩條指令。
cmp指令比較了前后兩個(gè)操作數(shù)的值
DWORD PTR 代表操作的數(shù)據(jù)類型是32位的整數(shù)
rbp-0x4則是一個(gè)寄存器的地址
第一個(gè)操作數(shù)就是從寄存器里拿到的變量r的值
第二個(gè)操作數(shù)0x0就是我們?cè)O(shè)定的常量0的16進(jìn)制表示
cmp指令的比較結(jié)果,會(huì)存入到條件碼寄存器
狀態(tài)寄存器又名條件碼寄存器,它是計(jì)算機(jī)系統(tǒng)的核心部件——運(yùn)算器的一部分
狀態(tài)寄存器用來(lái)存放兩類信息:一類是體現(xiàn)當(dāng)前指令執(zhí)行結(jié)果的各種狀態(tài)信息(條件碼),如有無(wú)進(jìn)位(CF位)、有無(wú)溢出(OF位)、結(jié)果正負(fù)(SF位)、結(jié)果是否為零(ZF位)、奇偶標(biāo)志位(P位)等另一類是存放控制信息(PSW:程序狀態(tài)字寄存器),如允許中斷(IF位)、跟蹤標(biāo)志(TF位)等
有些機(jī)器中將PSW稱為標(biāo)志寄存器FR(Flag Register)。
如果比較結(jié)果 True,即 r == 0,就把零標(biāo)志條件碼(對(duì)應(yīng)的條件碼是ZF,Zero Flag)設(shè)置為1
條件碼是CPU根據(jù)運(yùn)算結(jié)果由硬件設(shè)置的位,體現(xiàn)當(dāng)前指令執(zhí)行結(jié)果的各種狀態(tài)信息
例如:算術(shù)運(yùn)算產(chǎn)生的正、負(fù)、零或溢出等的結(jié)果。條件碼可被測(cè)試,作為分支運(yùn)算的依據(jù),此外,有些條件碼可被設(shè)置,例如對(duì)于最高位進(jìn)位標(biāo)志C,可用指令對(duì)它置位和復(fù)位。
Intel的CPU下還有
進(jìn)位標(biāo)志(CF,Carry Flag)
最近的操作使最高位產(chǎn)生了進(jìn)位。可以用來(lái)檢查無(wú)符號(hào)操作數(shù)據(jù)的溢出。
符號(hào)標(biāo)志(SF,Sign Flag)
最近的操作得到的結(jié)果為負(fù)數(shù)。
溢出標(biāo)志(OF,Overflow Flag)
最近的操作導(dǎo)致一個(gè)補(bǔ)碼溢出--正溢出或負(fù)溢出
用在不同的判斷條件下。
cmp指令執(zhí)行完成之后,PC寄存器會(huì)自增,開(kāi)始執(zhí)行下一條jne的指令
跟著的jne指令(jump if not equal),它會(huì)查看對(duì)應(yīng)的零標(biāo)志位
如果為0,會(huì)跳轉(zhuǎn)到后面跟著的操作數(shù)4a的位置
4a,對(duì)應(yīng)匯編代碼的行號(hào),也就是else條件里的第一條指令
當(dāng)跳轉(zhuǎn)發(fā)生,PC寄存器不再是自增變成下一條指令的地址,而被直接設(shè)置4a這個(gè)地址
這個(gè)時(shí)候,CPU再把4a地址里的指令加載到指令寄存器執(zhí)行。
跳轉(zhuǎn)到執(zhí)行地址為4a的指令,實(shí)際是一條mov指令
第一個(gè)操作數(shù)和前面的cmp指令一樣,是另一個(gè)32位整型的寄存器地址,以及對(duì)應(yīng)的2的16進(jìn)制值0x2
mov指令把2設(shè)置到對(duì)應(yīng)的寄存器里去,相當(dāng)于一個(gè)賦值操作
然后,PC寄存器里的值繼續(xù)自增,執(zhí)行下一條mov指令。
這條mov指令的第一個(gè)操作數(shù)eax,代表累加寄存器
在中央處理器中,累加器 (accumulator) 是一種寄存器,用來(lái)儲(chǔ)存計(jì)算產(chǎn)生的中間結(jié)果。如果沒(méi)有像累加器這樣的寄存器,那么在每次計(jì)算 (加法,乘法,移位等等) 后就必須要把結(jié)果寫(xiě)回到 內(nèi)存,也許馬上就得讀回來(lái)。然而存取主存的速度是比從算術(shù)邏輯單元到有直接路徑的累加器存取更慢。
第二個(gè)操作數(shù)0x0則是16進(jìn)制的0的表示。這條指令其實(shí)沒(méi)有實(shí)際的作用,它的作用是一個(gè)占位符
if條件如果滿足,在賦值的mov指令執(zhí)行完成之后,有一個(gè)jmp的無(wú)條件跳轉(zhuǎn)指令
跳轉(zhuǎn)的地址就是這一行的地址51
我們的main函數(shù)沒(méi)有設(shè)定返回值,而mov eax, 0x0 其實(shí)就是給main函數(shù)生成了一個(gè)默認(rèn)的為0的返回值到累加器里面
if條件里面的內(nèi)容執(zhí)行完成之后也會(huì)跳轉(zhuǎn)到這里,和else里的內(nèi)容結(jié)束之后的位置是一樣的。
上一講我們講打孔卡的時(shí)候說(shuō)到,讀取打孔卡的機(jī)器會(huì)順序地一段一段地讀取指令,然后執(zhí)行。
執(zhí)行完一條指令,它會(huì)自動(dòng)地順序讀取下一條指令
如果執(zhí)行的當(dāng)前指令帶有跳轉(zhuǎn)的地址,比如往后跳10個(gè)指令,那么機(jī)器會(huì)自動(dòng)將卡片帶往后移動(dòng)10個(gè)指令的位置,再來(lái)執(zhí)行指令
同樣的,機(jī)器也能向前移動(dòng),去讀取之前已經(jīng)執(zhí)行過(guò)的指令
這也就是我們的while/for循環(huán)實(shí)現(xiàn)的原理。
如何通過(guò)if…else和goto來(lái)實(shí)現(xiàn)循環(huán)?
我們?cè)倏匆欢魏?jiǎn)單的利用for循環(huán)的程序。我們循環(huán)自增變量i三次,三次之后,i>=3,就會(huì)跳出循環(huán)。整個(gè)程序,對(duì)應(yīng)的Intel匯編代碼就是這樣的:
可以看到,對(duì)應(yīng)的循環(huán)也是用1e這個(gè)地址上的cmp比較指令
和緊接著的jle條件跳轉(zhuǎn)指令來(lái)實(shí)現(xiàn)的
主要的差別在于,這里的jle跳轉(zhuǎn)的地址,在這條指令之前的地址14,而非if…else編譯出來(lái)的跳轉(zhuǎn)指令之后
往前跳轉(zhuǎn)使得條件滿足的時(shí)候,PC寄存器會(huì)把指令地址設(shè)置到之前執(zhí)行過(guò)的指令位置,重新執(zhí)行之前執(zhí)行過(guò)的指令,直到條件不滿足,順序往下執(zhí)行jle之后的指令,整個(gè)循環(huán)才結(jié)束。
如果你看一長(zhǎng)條打孔卡的話,就會(huì)看到卡片往后移動(dòng)一段,執(zhí)行了之后,又反向移動(dòng),去重新執(zhí)行前面的指令。
jle和jmp指令,有點(diǎn)像程序語(yǔ)言里面的goto命令,直接指定了一個(gè)特定條件下的跳轉(zhuǎn)位置
雖然我們?cè)谟酶呒?jí)語(yǔ)言開(kāi)發(fā)程序的時(shí)候反對(duì)使用goto,但是實(shí)際在機(jī)器指令層面,無(wú)論是if…else…也好,還是for/while也好,都是用和goto相同的跳轉(zhuǎn)到特定指令位置的方式來(lái)實(shí)現(xiàn)的。
3 總結(jié)學(xué)習(xí)了程序里的多條指令,究竟是怎么樣一條一條被執(zhí)行的
除了簡(jiǎn)單地通過(guò)PC寄存器自增的方式順序執(zhí)行外
條件碼寄存器會(huì)記錄下當(dāng)前執(zhí)行指令的條件判斷狀態(tài)
然后通過(guò)跳轉(zhuǎn)指令讀取對(duì)應(yīng)的條件碼
修改PC寄存器內(nèi)的下一條指令的地址
最終實(shí)現(xiàn)if…else以及for/while這樣的程序控制流程。
雖然我們可以用高級(jí)語(yǔ)言,可以用不同的語(yǔ)法,比如 if…else 這樣的條件分支,或者 while/for 這樣的循環(huán)方式,來(lái)實(shí)現(xiàn)不用的程序運(yùn)行流程
但是回歸到計(jì)算機(jī)可以識(shí)別的機(jī)器指令級(jí)別,其實(shí)都只是一個(gè)簡(jiǎn)單的地址跳轉(zhuǎn)而已,也就是一個(gè)類似于goto的語(yǔ)句。
想要在硬件層面實(shí)現(xiàn)這個(gè)goto語(yǔ)句,除了本身需要用來(lái)保存下一條指令地址,以及當(dāng)前正要執(zhí)行指令的PC寄存器、指令寄存器外
我們只需要再增加一個(gè)條件碼寄存器,來(lái)保留條件判斷的狀態(tài)。這樣簡(jiǎn)簡(jiǎn)單單的三個(gè)寄存器,就可以實(shí)現(xiàn)條件判斷和循環(huán)重復(fù)執(zhí)行代碼的功能。
4 推薦閱讀《深入理解計(jì)算機(jī)系統(tǒng)》的第3章
詳細(xì)講解了C語(yǔ)言和Intel CPU的匯編語(yǔ)言以及指令的對(duì)應(yīng)關(guān)系,以及Intel CPU的各種寄存器和指令集。
Intel指令集相對(duì)于之前的MIPS指令集要復(fù)雜一些
所有的指令是變長(zhǎng)的
從1個(gè)字節(jié)到15個(gè)字節(jié)不等
即使是匯編代碼,還有很多針對(duì)操作數(shù)據(jù)的長(zhǎng)度不同有不同的后綴
參考狀態(tài)寄存器
寄存器
條件碼
累加器
深入淺出計(jì)算機(jī)組成原理
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76124.html
摘要:而大寫(xiě)字母,就是第個(gè),也就是二進(jìn)制的,對(duì)應(yīng)的十六進(jìn)制表示就是。在中文世界里,最典型的就是手持兩把錕斤拷,口中疾呼燙燙燙的典故。既然今天要徹底搞清楚編碼知識(shí),我們就來(lái)弄清楚錕斤拷和燙燙燙的來(lái)龍去脈。參考深入淺出計(jì)算機(jī)組成原理 showImg(https://image-static.segmentfault.com/206/872/2068726052-5d5922b2effb9_art...
摘要:公元前年,在雅典附近的馬拉松海邊,發(fā)生了波斯和希臘之間的希波戰(zhàn)爭(zhēng)。因?yàn)殡妶?bào)員要熟記每一個(gè)字母對(duì)應(yīng)的摩爾斯電碼,并且需要快速按鍵來(lái)進(jìn)行輸入。 showImg(https://image-static.segmentfault.com/548/184/548184927-5d5962191cbce_articlex); 人用紙和筆來(lái)做運(yùn)算,都是用十進(jìn)制,直接用十進(jìn)制和我們最熟悉的符號(hào)不是最...
摘要:匯編器是怎么把對(duì)應(yīng)的匯編代碼,翻譯成為機(jī)器碼的。總結(jié)打孔卡,其實(shí)就是一種存儲(chǔ)程序型計(jì)算機(jī)。推薦閱讀了解的指令集參看計(jì)算機(jī)組成與設(shè)計(jì)軟硬件接口第版的小節(jié)參考深入淺出計(jì)算機(jī)組成原理 你在學(xué)寫(xiě)程序的時(shí)候,有沒(méi)有想過(guò),古老年代的計(jì)算機(jī)程序是怎么寫(xiě)出來(lái)的?showImg(https://ask.qcloudimg.com/http-save/1752328/fpfs9776q8.png); 當(dāng)...
摘要:計(jì)算機(jī)組成中的大量原理和設(shè)計(jì),都對(duì)應(yīng)著性能這個(gè)詞。時(shí)間的倒數(shù)性能計(jì)算機(jī)的性能,其實(shí)和體力勞動(dòng)很像,好比是我們要搬東西。對(duì)于計(jì)算機(jī)的性能,我們需要有個(gè)標(biāo)準(zhǔn)來(lái)衡量?;ǖ臅r(shí)間越少,自然性能就越好。 0 學(xué)習(xí)路線的知識(shí)點(diǎn)概括 showImg(https://segmentfault.com/img/remote/1460000020031616?w=3832&h=2540); 學(xué)習(xí)計(jì)算機(jī)組成原...
摘要:語(yǔ)義如何運(yùn)用語(yǔ)義類標(biāo)簽來(lái)呈現(xiàn)網(wǎng)頁(yè)通過(guò)網(wǎng)頁(yè)案例來(lái)學(xué)習(xí)語(yǔ)義類標(biāo)簽最初的設(shè)計(jì)場(chǎng)景就是超文本,早期工作組的專家都是出版界書(shū)籍排版的專家。標(biāo)記的部分有三個(gè)注記,它在文章中的作用就是額外的注釋,但是中并沒(méi)有相關(guān)的語(yǔ)義,這時(shí)可以使用標(biāo)簽進(jìn)行相關(guān)實(shí)現(xiàn)。 筆記說(shuō)明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開(kāi)的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過(guò)程的一...
閱讀 2774·2021-09-24 10:34
閱讀 1876·2021-09-22 10:02
閱讀 2265·2021-09-09 09:33
閱讀 1469·2021-08-13 15:02
閱讀 3279·2020-12-03 17:10
閱讀 1193·2019-08-30 15:44
閱讀 2156·2019-08-30 12:58
閱讀 3238·2019-08-26 13:40