簡單介紹一下編程方式的歷史演變。前端
在計算機誕生不久的早期年代,硬件很是昂貴,而程序員比較廉價。這些廉價程序員甚至都沒有「程序員」這個頭銜,而且經常是由數學家或者電氣工程師來充當這個角色的。早期的計算機被用來快速解決複雜的數學問題,因此數學家自然就適合「編程」工做。python
首先來看一點背景知識。計算機本身是作不了任何事情的,它們的任何行爲都須要程序來引導。你能夠把程序當作是很是精確的菜譜,這種菜譜讀取一個輸入,而後生成對應的輸出。菜譜裏的各個步驟由操做數據的指令構成。聽上去有點兒複雜,不過你或許知道下面這個語句是什麼意思:linux
1 + 2 = 3
複製代碼
其中的加號是「指令」,而數字 1 和 2 是數據。數學上的等號意味着等式兩邊的部分是「等價」的,不過在大部分編程語言中對變量使用等號是「賦值」的意思。若是計算機執行上面這個語句,它會把這個加法的結果(也就是「3」)儲存在內存中的某個地方。git
計算機知道如何使用數字進行數學運算,以及如何在內存結構中移動數據。在這裏就不對內存進行展開了,你只須要知道內存通常分爲兩大類:「速度快/空間小」和「速度慢/空間大」。CPU 寄存器的讀寫速度很是快,可是空間很是小,至關於一個速記便籤。主存儲器一般有很大的空間,可是讀寫速度就比寄存器差遠了。在程序運行的時候,CPU 不斷將它所須要用到的數據從主存儲器挪動到寄存器,而後再把結果放回到主存儲器。程序員
當時的計算機很貴,而人力比較便宜。程序員須要耗費不少時間把手寫的數學表達式翻譯成計算機能夠執行的指令。最初的計算機只有很是糟糕的用戶界面,有些甚至只有前面板上的撥動開關。這些開關就表明一個內存「單元」裏的一個個 「0」 和 「1」。程序員須要配置一個內存單元,選擇好儲存位置,而後把這個單元提交到內存裏。這是一個既耗時又容易出錯的過程。github
程序員Betty Jean Jennings (左) 和 Fran Bilas (右) 在操做 ENIAC 的主控制面板編程
後來有一名 電氣工程師 認爲本身的時間很寶貴,就寫了一個程序,可以把人們能夠讀懂的「菜譜」同樣的輸入轉換成計算機能夠讀懂的版本。這就是最初的「彙編器」,在當時引發了不小的爭議。這些昂貴機器的主人不但願把計算資源浪費在人們已經能作的任務上(雖然又慢又容易出錯)。不過隨着時間的推移,人們逐漸發現使用匯編器在速度和準確性上都勝於人工編寫機器語言,而且計算機完成的「實際工做量」增長了。bash
儘管彙編器相比在機器面板上切換比特的狀態已是很大的進步了,這種編程方式仍然很是專業。上面加法的例子在彙編語言中看起來差很少是這樣的:架構
01 MOV R0, 1
02 MOV R1, 2
03 ADD R0, R1, R2
04 MOV 64, R0
05 STO R2, R0
複製代碼
每一行都是一個計算機指令,前面是一個指令的簡寫,後面是指令所操做的數據。這個小小的程序首先會將數值 1 「移動」到寄存器 R0,而後把 2 移動到寄存器 R1。03 行把 R0 和 R1 兩個寄存器裏的數值相加,而後將結果儲存在 R2 寄存器裏。最後,04 行和 05 行決定結果應該被放在主存儲器裏的什麼位置(在這裏是地址 64)。管理內存中存儲數據的位置是編程過程當中最耗時也最容易出錯的部分之一。編程語言
彙編器已經比手寫計算機指令要好太多了,不過早期的程序員仍是渴望可以按照他們所習慣的方式,像書寫數學公式同樣地去寫程序。這種需求推進了高級編譯語言的發展,其中有一些已經成爲歷史,另外一些現在還在使用。好比 ALGO 就已經成爲歷史了,可是像 Fortran 和 C 這樣的語言仍然在不斷解決實際問題。
ALGO 和 Fortran 編程語言的譜系樹
這些「高級」語言使得程序員能夠用更簡單的方式編寫程序。在 C 語言中,咱們的加法程序就變成了這樣:
int x;
x = 1 + 2;
複製代碼
第一個語句描述了該程序將要使用的一塊內存。在這個例子中,這塊內存應該佔一個整數的大小,名字是 x
。第二個語句是加法,雖然是倒着寫的。一個 C 語言的程序員會說這是 「X 被賦值爲 1 加 2 的結果」。須要注意的是,程序員並不須要決定在內存的什麼位置儲存 x
,這個任務交給編譯器了。
這種被稱爲「編譯器」的新程序能夠把用高級語言寫的程序轉換成彙編語言,再使用匯編器把彙編語言轉換成機器可讀的程序。這種程序組合經常被稱爲「工具鏈」,由於一個程序的輸出就直接成爲另外一個程序的輸入。
編譯語言相比彙編語言的優點體如今從一臺計算機遷移到不一樣型號或者品牌的另外一臺計算機上的時候。在計算機的早期歲月裏,包括 IBM、DEC、德州儀器、UNIVAC 以及惠普在內的不少公司都在製造除了大量不一樣類型的計算機硬件。這些計算機除了都須要鏈接電源以外就沒有太多共同點了。它們在內存和 CPU 架構上的差別至關大,當時常常須要人們花費數年來將一臺計算機的程序翻譯成另外一臺計算機的程序。
有了高級語言,咱們只須要把編譯器工具鏈遷移到新的平臺就好了。只要有可用的編譯器,高級語言寫的程序最多隻須要通過小幅修改就能夠在新的計算機上被從新編譯。高級語言的編譯是一個真正的革命性成果。
1983 發佈的 IBM PC XT 是硬件價格降低的早期例子。
程序員們的生活獲得了很好的改善。相比之下,經過高級語言表達他們想要解決的問題讓事情變得輕鬆不少。因爲半導體技術的進步以及集成芯片的發明,計算機硬件的價格急劇降低。計算機的速度愈來愈快,能力也愈來愈強,而且還便宜了不少。從某個時間點日後(也許是 80 年代末期吧),事情發生了反轉,程序員變得比他們所使用的硬件更值錢了。
隨着時間的推移,一種新的編程方式興起了。一種被稱爲「解釋器」的特殊程序能夠直接讀取一個程序將其轉換成計算機指令以當即執行。和編譯器差很少,解釋器讀取程序並將它轉換成一箇中間形態。但和編譯器不一樣的是,解釋器直接執行程序的這個中間形態。解釋型語言在每一次執行的時候都要經歷這個過程;而編譯程序只須要編譯一次,以後計算機每次只須要執行編譯好的機器指令就能夠了。
順便說一句,這個特性就是致使人們感受解釋型程序運行得比較慢的緣由。不過現代計算機的性能出奇地強大,以致於大多數人沒法區分編譯型程序和解釋型程序。
解釋型程序(有時也被成爲「腳本」)甚至更容易被移植到不一樣的硬件平臺上。由於腳本並不包含任何機器特有的指令,同一個版本的程序能夠不通過任何修改就直接在不少不一樣的計算機上運行。不過固然了,解釋器必須得先移植到新的機器上才行。
一個很流行的解釋型語言是 perl。用 perl 完整地表達咱們的加法問題會是這樣的:
$x = 1 + 2
複製代碼
雖然這個程序看起來和 C 語言的版本差很少,運行上也沒有太大區別,但卻缺乏了初始化變量的語句。其實還有一些其它的區別(超出這篇文章的範圍了),但你應該已經注意到,咱們寫計算機程序的方式已經和數學家用紙筆手寫數學表達式很是接近了。
最新潮的編程方式要數虛擬機(常常簡稱 VM)了。虛擬機分爲兩大類:系統虛擬機和進程虛擬機。這兩種虛擬機都提供一種對「真實的」計算硬件的不一樣級別的抽象,不過它們的做用域不一樣。系統虛擬機是一個提供物理硬件的替代品的軟件,而進程虛擬機則被設計用來以一種「系統獨立」的方式執行程序。因此在這個例子裏,進程虛擬機(日後我所說的虛擬機都是指這個類型)的做用域和解釋器的比較相似,由於也是先將程序編譯成一箇中間形態,而後虛擬機再執行這個中間形態。
虛擬機和解釋器的主要區別在於,虛擬機創造了一個虛擬的 CPU,以及一套虛擬的指令集。有了這層抽象,咱們就能夠編寫前端工具來把不一樣語言的程序編譯成虛擬機能夠接受的程序了。也許最流行也最知名的虛擬機就是 Java 虛擬機(JVM)了。JVM 最初在 1990 年代只支持 Java 語言,可是現在卻能夠運行 許多 流行的編程語言,包括 Scala、Jython、JRuby、Clojure,以及 Kotlin 等等。還有其它一些不太常見的例子,在這裏就不說了。我也是最近才知道,我最喜歡的語言 Python 並非一個解釋型語言,而是一個 運行在虛擬機上的語言!
虛擬機仍然在延續這樣一個歷史趨勢:讓程序員在使用特定領域的編程語言解決問題的時候,所須要的對特定計算平臺的瞭解變得愈來愈少了。
但願你喜歡這篇簡單介紹軟件背後運行原理的短文。有什麼其它話題是你想讓我接下來討論的嗎?在評論裏告訴我吧。
via: opensource.com/article/19/…
做者:Erik O'Shaughnessy 選題:lujun9972 譯者:chen-ni 校對:wxy