天天看點

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

你在學寫程式的時候,有沒有想過,古老年代的計算機程式是怎麼寫出來的?

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

當年寫程式,不像現在這樣,都是用一種古老的實體裝置,叫作“打孔卡(Punched Card)”

用這種裝置寫程式,沒法像今天,掏出鍵盤就能打字,而是要先在腦海/紙寫出程式,然後在紙帶/卡片上打洞

這樣,要寫的程式、要處理的資料,就變成一條條紙帶或者一張張卡片,之後再交給當時的計算機去處理

上世紀60年代晚期或70年代初期,Arnold Reinold拍攝的FORTRAN計算程式的穿孔卡照片

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

人們在特定的位置上打洞或者不打洞,來代表“0”或者“1”。

為什麼早期的計算機程式要使用打孔卡,而不能像我們現在一樣,用C或者Python這樣的進階語言來寫呢?

因為計算機或者說CPU本身,并沒有能力了解這些進階語言

即使在2019年的今天,我們使用的現代個人計算機,仍然隻能處理所謂的“機器碼”,也就是一連串的“0”和“1”這樣的數字。

我們每天用進階語言的程式,最終是怎麼變成一串串“0”和“1”的?這一串串“0”和“1”又是怎麼在CPU中處理的?

1 在軟硬體接口中,CPU幫我們做的事

CPU(Central Processing Unit,中央處理器)就是計算機的大腦

  • 硬體的角度

    一個超大規模內建電路,通過電路實作了加法、乘法乃至各種各樣的處理邏輯。

  • 軟體工程師的角度

    一個執行各種計算機指令(Instruction Code)的邏輯機器

    這裡的計算機指令,就好比一門CPU能夠聽得懂的語言,即機器語言(Machine Language)

不同的CPU能夠聽懂的語言不太一樣

個人PC用的是Intel的CPU,iPhone用的是ARM的CPU,這兩者能聽懂的語言就不太一樣

類似這樣兩種CPU各自支援的語言,就是兩組不同的計算機指令集(Instruction Set)

這裡面的“Set”,其實就是數學上的集合,代表不同的單詞、文法

如果我們在自己電腦上寫一個程式,然後把這個程式複制一下,裝到自己的手機上,肯定是沒辦法正常運作的,因為這兩者語言不通

而一台電腦上的程式,簡單複制一下到另外一台電腦上,通常就能正常運作,因為這兩台CPU有着相同的指令集,它們語言相通

存儲程式型計算機(Stored-program Computer)

計算機程式,不可能隻有一條指令,而是成千上萬條指令組成

但CPU不能一直放着所有指令,是以程式平時是存儲在存儲器

這種程式指令存儲在存儲器裡面的計算機,我們就叫作

Plugboard Computer

在沒有現代計算機之前,有着聰明才智的工程師們,早就發明了一種叫Plugboard Computer的計算裝置

在一個布滿了各種插口和插座的闆子上,工程師們用不同的電線來連接配接不同的插口和插座,進而來完成各種計算任務

IBM的Plugboard

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

2 編譯=>彙編 代碼=>機器碼

代碼,到底是怎麼變成一條條計算機指令,最後被CPU執行的呢?

test.c

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼
  • 編譯(Compile)成彙編代碼

    要讓這段程式在Linux跑起來,需要把整個程式翻譯成彙編語言(ASM,Assembly Language)的程式

針對彙編代碼,可以再用彙編器(Assembler)翻譯成機器碼(Machine Code)

這些機器碼由“0”和“1”組成的機器語言表示,這一條條機器碼,就是一條條的計算機指令

這樣一串串的16進制數字,就是我們CPU能夠真正認識的計算機指令。

在Linux上,可使用gcc和objdump,把對應的彙編代碼和機器碼都列印出來。

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

左側一堆數字,就是一條條機器碼

右邊一系列的push、mov、add、pop等,這些就是對應的彙編代碼

一行C語言代碼,有時候隻對應一條機器碼和彙編代碼,有時候則是對應兩條機器碼和彙編代碼

彙編代碼和機器碼之間是一一對應的。

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

實際在用GCC(GUC編譯器套裝,GUI Compiler Collectipon)編譯器的時候,可直接把代碼編譯成機器碼,為什麼還需要彙編代碼呢?

那一串數字表示的機器碼,摸不着頭腦

但即使你沒有學過彙編代碼,看的時候多少也能“猜”出一些這些代碼的含義。

彙編代碼就是“給程式員看的機器碼”

也正因為這樣,機器碼和彙編代碼是一一對應的

很容易記住add、mov這些用英文表示的指令

而8b 45 f8這樣的指令,由于很難一下子看明白是在幹什麼,是以會非常難以記憶

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

從進階語言到彙編代碼,再到機器碼,就是一個日常開發程式,最終變成了CPU可以執行的計算機指令的過程。

3 解析指令和機器碼

了解了這個過程,下面我們放大局部,來看看這一行行的彙編代碼和機器指令,到底是什麼意思。

Intel CPU,有2000條左右的CPU指令,實在是太多了,沒法一一講解。不過一般來說,常見的指令可以分成五大類。

算術類指令

加減乘除,在CPU層面,都會變成一條條算術類指令

資料傳輸類指令

給變量指派、在記憶體裡讀寫資料,用的都是資料傳輸類指令。

邏輯類指令

邏輯上的與或非

條件分支類指令

日常的“if/else”

無條件跳轉指令

寫一些大一點的程式,我們常常需要寫一些函數或者方法

在調用函數的時候,其實就是發起了一個無條件跳轉指令。

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

彙編器是怎麼把對應的彙編代碼,翻譯成為機器碼的。

不同的CPU有不同的指令集,也就對應着不同的彙編語言和不同的機器碼

為了友善你快速了解這個機器碼的計算方式,我們選用最簡單的MIPS指令集,來看看機器碼是如何生成的。

MIPS是一組由MIPS技術公司在80年代中期設計出來的CPU指令集。就在最近,MIPS公司把整個指令集和晶片架構都完全開源了。想要深入研究CPU和指令集的同學,推薦一些資料,可以自己了解下。

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

MIPS的指令是一個32位的整數,高6位叫操作碼(Opcode)

也就是代表這條指令具體是一條什麼樣的指令,剩下的26位有三種格式,分别是R、I和J。

R指令

一般用來做算術和邏輯操作,裡面有讀取和寫入資料的寄存器的位址

如果是邏輯位移操作,後面還有位移操作的位移量

而最後的功能碼,則是在前面的操作碼不夠的時候,擴充操作碼表示對應的具體指令的。

I指令

通常是用在資料傳輸、條件分支,以及在運算的時候使用的并非變量還是常數的時候

這個時候,沒有了位移量和操作碼,也沒有了第三個寄存器,而是把這三部分直接合并成了一個位址值或者一個常數。

J指令

一個跳轉指令,高6位之外的26位都是一個跳轉後的位址

add $t0,$s2,$s1      

下面都用十進制來表示對應的代碼。

對應的MIPS指令裡

  • opcode是0
  • rs代表第一個寄存器s1的位址是17
  • rt代表第二個寄存器s2的位址是18
  • rd代表目标的臨時寄存器t0的位址是8
  • 因為不是位移操作,是以位移量是0

把這些數字拼在一起,就變成了一個MIPS的加法指令。

為了讀起來友善,我們一般把對應的二進制數,用16進制表示出來

在這裡,也就是0X02324020。這個數字也就是這條指令對應的機器碼。

重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

回到開頭我們說的打孔帶

  • 打孔代表1
  • 沒有打孔代表0
  • 用4行8列代表一條指令來打一個穿孔紙帶,那麼這條指令大概就長這樣:
重學計算機組成原理(四)- 進擊,更強的性能!(上)1 在軟硬體接口中,CPU幫我們做的事2 編譯=>彙編 代碼=>機器碼3 解析指令和機器碼

你應該學會了怎麼作為人肉編譯和彙編器,給紙帶打孔程式設計了,不用再對那些用過打孔卡的前輩們頂禮膜拜了。