摘要:匯編器是怎么把對(duì)應(yīng)的匯編代碼,翻譯成為機(jī)器碼的??偨Y(jié)打孔卡,其實(shí)就是一種存儲(chǔ)程序型計(jì)算機(jī)。推薦閱讀了解的指令集參看計(jì)算機(jī)組成與設(shè)計(jì)軟硬件接口第版的小節(jié)參考深入淺出計(jì)算機(jī)組成原理
你在學(xué)寫(xiě)程序的時(shí)候,有沒(méi)有想過(guò),古老年代的計(jì)算機(jī)程序是怎么寫(xiě)出來(lái)的?
當(dāng)年寫(xiě)程序,不像現(xiàn)在這樣,都是用一種古老的物理設(shè)備,叫作“打孔卡(Punched Card)”
用這種設(shè)備寫(xiě)程序,沒(méi)法像今天,掏出鍵盤(pán)就能打字,而是要先在腦海/紙寫(xiě)出程序,然后在紙帶/卡片上打洞
這樣,要寫(xiě)的程序、要處理的數(shù)據(jù),就變成一條條紙帶或者一張張卡片,之后再交給當(dāng)時(shí)的計(jì)算機(jī)去處理
上世紀(jì)60年代晚期或70年代初期,Arnold Reinold拍攝的FORTRAN計(jì)算程序的穿孔卡照片
人們?cè)谔囟ǖ奈恢蒙洗蚨椿蛘卟淮蚨?,?lái)代表“0”或者“1”。
為什么早期的計(jì)算機(jī)程序要使用打孔卡,而不能像我們現(xiàn)在一樣,用C或者Python這樣的高級(jí)語(yǔ)言來(lái)寫(xiě)呢?
因?yàn)橛?jì)算機(jī)或者說(shuō)CPU本身,并沒(méi)有能力理解這些高級(jí)語(yǔ)言
即使在2019年的今天,我們使用的現(xiàn)代個(gè)人計(jì)算機(jī),仍然只能處理所謂的“機(jī)器碼”,也就是一連串的“0”和“1”這樣的數(shù)字。
我們每天用高級(jí)語(yǔ)言的程序,最終是怎么變成一串串“0”和“1”的?這一串串“0”和“1”又是怎么在CPU中處理的?
1 在軟硬件接口中,CPU幫我們做的事CPU(Central Processing Unit,中央處理器)就是計(jì)算機(jī)的大腦
硬件的角度
一個(gè)超大規(guī)模集成電路,通過(guò)電路實(shí)現(xiàn)了加法、乘法乃至各種各樣的處理邏輯。
軟件工程師的角度
一個(gè)執(zhí)行各種計(jì)算機(jī)指令(Instruction Code)的邏輯機(jī)器
這里的計(jì)算機(jī)指令,就好比一門(mén)CPU能夠聽(tīng)得懂的語(yǔ)言,即機(jī)器語(yǔ)言(Machine Language)
不同的CPU能夠聽(tīng)懂的語(yǔ)言不太一樣
個(gè)人PC用的是Intel的CPU,iPhone用的是ARM的CPU,這兩者能聽(tīng)懂的語(yǔ)言就不太一樣
類似這樣兩種CPU各自支持的語(yǔ)言,就是兩組不同的計(jì)算機(jī)指令集(Instruction Set)
這里面的“Set”,其實(shí)就是數(shù)學(xué)上的集合,代表不同的單詞、語(yǔ)法
如果我們?cè)谧约弘娔X上寫(xiě)一個(gè)程序,然后把這個(gè)程序復(fù)制一下,裝到自己的手機(jī)上,肯定是沒(méi)辦法正常運(yùn)行的,因?yàn)檫@兩者語(yǔ)言不通
而一臺(tái)電腦上的程序,簡(jiǎn)單復(fù)制一下到另外一臺(tái)電腦上,通常就能正常運(yùn)行,因?yàn)檫@兩臺(tái)CPU有著相同的指令集,它們語(yǔ)言相通
存儲(chǔ)程序型計(jì)算機(jī)(Stored-program Computer)
計(jì)算機(jī)程序,不可能只有一條指令,而是成千上萬(wàn)條指令組成
但CPU不能一直放著所有指令,所以程序平時(shí)是存儲(chǔ)在存儲(chǔ)器
這種程序指令存儲(chǔ)在存儲(chǔ)器里面的計(jì)算機(jī),我們就叫作
Plugboard Computer
在沒(méi)有現(xiàn)代計(jì)算機(jī)之前,有著聰明才智的工程師們,早就發(fā)明了一種叫Plugboard Computer的計(jì)算設(shè)備
在一個(gè)布滿了各種插口和插座的板子上,工程師們用不同的電線來(lái)連接不同的插口和插座,從而來(lái)完成各種計(jì)算任務(wù)
IBM的Plugboard
2 編譯=>匯編 代碼=>機(jī)器碼代碼,到底是怎么變成一條條計(jì)算機(jī)指令,最后被CPU執(zhí)行的呢?
test.c
編譯(Compile)成匯編代碼
要讓這段程序在Linux跑起來(lái),需要把整個(gè)程序翻譯成匯編語(yǔ)言(ASM,Assembly Language)的程序
針對(duì)匯編代碼,可以再用匯編器(Assembler)翻譯成機(jī)器碼(Machine Code)
這些機(jī)器碼由“0”和“1”組成的機(jī)器語(yǔ)言表示,這一條條機(jī)器碼,就是一條條的計(jì)算機(jī)指令
這樣一串串的16進(jìn)制數(shù)字,就是我們CPU能夠真正認(rèn)識(shí)的計(jì)算機(jī)指令。
在Linux上,可使用gcc和objdump,把對(duì)應(yīng)的匯編代碼和機(jī)器碼都打印出來(lái)。
左側(cè)一堆數(shù)字,就是一條條機(jī)器碼
右邊一系列的push、mov、add、pop等,這些就是對(duì)應(yīng)的匯編代碼
一行C語(yǔ)言代碼,有時(shí)候只對(duì)應(yīng)一條機(jī)器碼和匯編代碼,有時(shí)候則是對(duì)應(yīng)兩條機(jī)器碼和匯編代碼
匯編代碼和機(jī)器碼之間是一一對(duì)應(yīng)的。
實(shí)際在用GCC(GUC編譯器套裝,GUI Compiler Collectipon)編譯器的時(shí)候,可直接把代碼編譯成機(jī)器碼,為什么還需要匯編代碼呢?
那一串?dāng)?shù)字表示的機(jī)器碼,摸不著頭腦
但即使你沒(méi)有學(xué)過(guò)匯編代碼,看的時(shí)候多少也能“猜”出一些這些代碼的含義。
匯編代碼就是“給程序員看的機(jī)器碼”
也正因?yàn)檫@樣,機(jī)器碼和匯編代碼是一一對(duì)應(yīng)的
很容易記住add、mov這些用英文表示的指令
而8b 45 f8這樣的指令,由于很難一下子看明白是在干什么,所以會(huì)非常難以記憶
從高級(jí)語(yǔ)言到匯編代碼,再到機(jī)器碼,就是一個(gè)日常開(kāi)發(fā)程序,最終變成了CPU可以執(zhí)行的計(jì)算機(jī)指令的過(guò)程。
3 解析指令和機(jī)器碼了解了這個(gè)過(guò)程,下面我們放大局部,來(lái)看看這一行行的匯編代碼和機(jī)器指令,到底是什么意思。
Intel CPU,有2000條左右的CPU指令,實(shí)在是太多了,沒(méi)法一一講解。不過(guò)一般來(lái)說(shuō),常見(jiàn)的指令可以分成五大類。
算術(shù)類指令加減乘除,在CPU層面,都會(huì)變成一條條算術(shù)類指令
數(shù)據(jù)傳輸類指令給變量賦值、在內(nèi)存里讀寫(xiě)數(shù)據(jù),用的都是數(shù)據(jù)傳輸類指令。
邏輯類指令邏輯上的與或非
條件分支類指令日常的“if/else”
無(wú)條件跳轉(zhuǎn)指令寫(xiě)一些大一點(diǎn)的程序,我們常常需要寫(xiě)一些函數(shù)或者方法
在調(diào)用函數(shù)的時(shí)候,其實(shí)就是發(fā)起了一個(gè)無(wú)條件跳轉(zhuǎn)指令。
匯編器是怎么把對(duì)應(yīng)的匯編代碼,翻譯成為機(jī)器碼的。
不同的CPU有不同的指令集,也就對(duì)應(yīng)著不同的匯編語(yǔ)言和不同的機(jī)器碼
為了方便你快速理解這個(gè)機(jī)器碼的計(jì)算方式,我們選用最簡(jiǎn)單的MIPS指令集,來(lái)看看機(jī)器碼是如何生成的。
MIPS是一組由MIPS技術(shù)公司在80年代中期設(shè)計(jì)出來(lái)的CPU指令集。就在最近,MIPS公司把整個(gè)指令集和芯片架構(gòu)都完全開(kāi)源了。想要深入研究CPU和指令集的同學(xué),推薦一些資料,可以自己了解下。
MIPS的指令是一個(gè)32位的整數(shù),高6位叫操作碼(Opcode)
也就是代表這條指令具體是一條什么樣的指令,剩下的26位有三種格式,分別是R、I和J。
R指令
一般用來(lái)做算術(shù)和邏輯操作,里面有讀取和寫(xiě)入數(shù)據(jù)的寄存器的地址
如果是邏輯位移操作,后面還有位移操作的位移量
而最后的功能碼,則是在前面的操作碼不夠的時(shí)候,擴(kuò)展操作碼表示對(duì)應(yīng)的具體指令的。
I指令
通常是用在數(shù)據(jù)傳輸、條件分支,以及在運(yùn)算的時(shí)候使用的并非變量還是常數(shù)的時(shí)候
這個(gè)時(shí)候,沒(méi)有了位移量和操作碼,也沒(méi)有了第三個(gè)寄存器,而是把這三部分直接合并成了一個(gè)地址值或者一個(gè)常數(shù)。
J指令
一個(gè)跳轉(zhuǎn)指令,高6位之外的26位都是一個(gè)跳轉(zhuǎn)后的地址
add $t0,$s2,$s1
下面都用十進(jìn)制來(lái)表示對(duì)應(yīng)的代碼。
對(duì)應(yīng)的MIPS指令里
opcode是0
rs代表第一個(gè)寄存器s1的地址是17
rt代表第二個(gè)寄存器s2的地址是18
rd代表目標(biāo)的臨時(shí)寄存器t0的地址是8
因?yàn)椴皇俏灰撇僮?,所以位移量?
把這些數(shù)字拼在一起,就變成了一個(gè)MIPS的加法指令。
為了讀起來(lái)方便,我們一般把對(duì)應(yīng)的二進(jìn)制數(shù),用16進(jìn)制表示出來(lái)
在這里,也就是0X02324020。這個(gè)數(shù)字也就是這條指令對(duì)應(yīng)的機(jī)器碼。
回到開(kāi)頭我們說(shuō)的打孔帶
打孔代表1
沒(méi)有打孔代表0
用4行8列代表一條指令來(lái)打一個(gè)穿孔紙帶,那么這條命令大概就長(zhǎng)這樣:
你應(yīng)該學(xué)會(huì)了怎么作為人肉編譯和匯編器,給紙帶打孔編程了,不用再對(duì)那些用過(guò)打孔卡的前輩們頂禮膜拜了。
4 總結(jié)打孔卡,其實(shí)就是一種存儲(chǔ)程序型計(jì)算機(jī)。
只是這整個(gè)程序的機(jī)器碼,不是通過(guò)計(jì)算機(jī)編譯出來(lái)的,而是由程序員的人腦“編譯”成一張張卡片的
對(duì)應(yīng)的程序,也不是存儲(chǔ)在設(shè)備里,而是存儲(chǔ)成一張打好孔的卡片
但是整個(gè)程序運(yùn)行的邏輯和其他CPU的機(jī)器語(yǔ)言沒(méi)有什么分別,也是處理一串“0”和“1”組成的機(jī)器碼而已。
我們看到了一個(gè)C語(yǔ)言程序,是怎么被編譯成為匯編語(yǔ)言,乃至通過(guò)匯編器再翻譯成機(jī)器碼的。
除了C這樣的編譯型的語(yǔ)言之外,不管是Python這樣的解釋型語(yǔ)言,還是Java這樣使用虛擬機(jī)的語(yǔ)言,其實(shí)最終都是由不同形式的程序,把我們寫(xiě)好的代碼,轉(zhuǎn)換成CPU能夠理解的機(jī)器碼來(lái)執(zhí)行的。
只是解釋型語(yǔ)言,是通過(guò)解釋器在程序運(yùn)行的時(shí)候逐句翻譯,而Java這樣使用虛擬機(jī)的語(yǔ)言,則是由虛擬機(jī)對(duì)編譯出來(lái)的中間代碼進(jìn)行解釋,或者即時(shí)編譯成為機(jī)器碼來(lái)最終執(zhí)行。
5 推薦閱讀了解Intel CPU的指令集參看
《計(jì)算機(jī)組成與設(shè)計(jì):軟/硬件接口》第5版的2.17小節(jié)
參考深入淺出計(jì)算機(jī)組成原理
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/76092.html
摘要:馮諾依曼體系結(jié)構(gòu)示意圖總結(jié)馮諾依曼體系結(jié)構(gòu)確立了我們現(xiàn)在每天使用的計(jì)算機(jī)硬件的基礎(chǔ)架構(gòu)。因此,學(xué)習(xí)計(jì)算機(jī)組成原理,其實(shí)就是學(xué)習(xí)和拆解馮諾依曼體系結(jié)構(gòu)。 showImg(https://ask.qcloudimg.com/http-save/1752328/g6cdrb45jg.png); 1 計(jì)算機(jī)的基本硬件組成 早期,DIY一臺(tái)計(jì)算機(jī),要先有三大件 CPU 內(nèi)存 主板 1.1 C...
摘要:在上一篇中我們談到過(guò)程序的執(zhí)行時(shí)間指令數(shù)要提升計(jì)算機(jī)的性能,可以從上面這三方面著手。在摩爾定律和并行計(jì)算之外,在整個(gè)計(jì)算機(jī)組成層面,還有這樣幾個(gè)原則性的性能提升方法。 showImg(https://ask.qcloudimg.com/http-save/1752328/uskvyzme4j.png); 在上一篇中,我們談到過(guò) 程序的CPU執(zhí)行時(shí)間 = 指令數(shù)×CPI×Clock Cy...
摘要:這個(gè)辦法,在現(xiàn)在計(jì)算機(jī)的內(nèi)存管理里面,就叫作內(nèi)存分頁(yè)和分段這樣分配一整段連續(xù)的空間給到程序相比分頁(yè)則是把整個(gè)物理內(nèi)存空間切成一段段固定尺寸的大小而對(duì)應(yīng)的程序所需要占用的虛擬內(nèi)存空間,也會(huì)同樣切成一段段固定尺寸的大小。 showImg(https://image-static.segmentfault.com/290/765/2907653835-5d580caf245fd_articl...
摘要:反對(duì)的意見(jiàn)主要是這樣可能會(huì)破壞掉無(wú)數(shù)個(gè)腳本,而且中已經(jīng)有太多的魔法了。除此之外,的命名本身也算是一種包袱。首字母大寫(xiě)的,譯作史努比,則是一只被很多人喜愛(ài)的漫畫(huà)小狗。 showImg(https://segmentfault.com/img/remote/1460000019559250); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授權(quán),請(qǐng)勿轉(zhuǎn)載。 原文地址:https://mp....
摘要:而大寫(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...
閱讀 1958·2021-11-22 09:34
閱讀 3209·2019-08-30 15:55
閱讀 772·2019-08-30 15:53
閱讀 2147·2019-08-30 15:52
閱讀 3075·2019-08-29 18:32
閱讀 2101·2019-08-29 17:15
閱讀 2478·2019-08-29 13:14
閱讀 3646·2019-08-28 18:05