機(jī)器指令到匯編再到高級(jí)編程語(yǔ)言!
Part 1. 機(jī)器指令
上一次 我們已經(jīng)了解了 二進(jìn)制和 CPU 的基本原理,知道了程序運(yùn)行時(shí),CPU 每秒數(shù)以億次、十億次、百億次地震蕩著時(shí)鐘,同步執(zhí)行著微小的 「電子操作」,例如:從內(nèi)存讀取一個(gè)字節(jié)的數(shù)據(jù)到 CPU 又或者判斷字節(jié)中的某一位是 0
還是 1
。
CPU 本身有一組 規(guī)定好的 可以執(zhí)行的 「基本動(dòng)作」(被稱為 機(jī)器指令):
-
讀取指令;2. 執(zhí)行指令;3. 寫寄存器;
這幾乎就是 CPU 工作的全部了。 這些動(dòng)作雖然每次只能執(zhí)行一次,但是每秒可以執(zhí)行數(shù)十億次,這個(gè)數(shù)量級(jí)的「小操作」累加成為一個(gè)大的「有用的操作」。
處理器所做的一切都是基于這些微小的操作!幸運(yùn)的是,我們已經(jīng)不再需要了解這些操作的詳細(xì)信息就可以編寫和使用各類程序。諸如 Java 這一類的 「高級(jí)語(yǔ)言」 的 目的 就是 將這些微小的電子操作組織成由人類可讀的「程序語(yǔ)言」表示的大型有用單元。
機(jī)器指令演示
一條 機(jī)器指令 一般由內(nèi)存中的幾個(gè)字節(jié)組成,它們告訴 CPU 應(yīng)該執(zhí)行一個(gè)什么樣的 「機(jī)器操作」(是取數(shù)據(jù)還是寫寄存器等..)。處理器依次查看 CPU 中的機(jī)器指令,并執(zhí)行每一條。內(nèi)存中的一組機(jī)器指令被稱為 「機(jī)器語(yǔ)言程序」,或稱為 「可執(zhí)行程序」。
下面我們來(lái)使用機(jī)器語(yǔ)言來(lái)演示一個(gè)控制燈泡亮度的機(jī)器語(yǔ)言程序。
先和硬件做好規(guī)定
假設(shè)燈泡由內(nèi)存中的某一個(gè)程序控制,該程序能夠完全打開(kāi)和關(guān)閉燈泡,可以使燈泡變亮或變暗,機(jī)器指令一個(gè)字節(jié)長(zhǎng)度,并且與機(jī)器操作對(duì)應(yīng)如下:
機(jī)器指令 | 機(jī)器操作 |
---|---|
00000000 | 停止程序 |
00000001 | 完全打開(kāi)燈泡 |
00000010 | 完全關(guān)閉燈泡 |
00000100 | 燈泡暗淡 10% |
00001000 | 將燈泡照亮 10% |
00010000 | 如果燈泡完全點(diǎn)亮,則跳過(guò)下一條說(shuō)明 |
00100000 | 如果燈泡完全熄滅,請(qǐng)?zhí)^(guò)下一條說(shuō)明 |
01000000 | 轉(zhuǎn)到程序的開(kāi)始(地址 0) |
Demo 程序 && 演示
根據(jù)上方作出的規(guī)定,我們寫下如下的程序:(為了方便理解,我把對(duì)應(yīng)的機(jī)器操作也寫在了后面,實(shí)際的程序只包含機(jī)器指令)
地址 | 機(jī)器指令 | 機(jī)器操作 |
---|---|---|
0 | 00000001 | 完全打開(kāi)燈泡 |
1 | 00000010 | 完全關(guān)閉燈泡 |
2 | 00000001 | 完全打開(kāi)燈泡 |
3 | 00000100 | 燈泡暗淡10% |
4 | 00000100 | 燈泡暗淡10% |
5 | 00000000 | 停止程序 |
所以這樣的一段程序執(zhí)行效果就如下圖:
您可以嘗試自己利用 01000000
(跳轉(zhuǎn)到程序開(kāi)始) 來(lái)改寫程序來(lái)達(dá)到讓「燈逐漸變亮又逐漸變暗」的目的。
小結(jié)
上面演示的程序 核心思想 是:
-
機(jī)器語(yǔ)言程序是內(nèi)存中一系列機(jī)器指令的集合; -
機(jī)器指令由一個(gè)或多個(gè)字節(jié)組成(在此示例中,僅一個(gè)字節(jié)); -
處理器一次運(yùn)行一條機(jī)器指令的程序; -
所有的小機(jī)器操作加起來(lái)都是有用的;
在實(shí)際的 CPU 中,擁有更多的機(jī)器指令,而且更詳細(xì),并且不同的 CPU,指令集是不同的。典型的 CPU 擁有一千或更多的機(jī)器指令。
Part 2. 匯編語(yǔ)言
-
圖片來(lái)源:http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
機(jī)器語(yǔ)言太 "反人類”
我們已經(jīng)可以開(kāi)始寫一些程序使用了,但是使用 機(jī)器語(yǔ)言編寫代碼會(huì)十分辛苦,比如:
00000001 00000010 00000001
00000100 00000100 00000000
即使你剛看過(guò)你也會(huì)對(duì)這一段就在 上方的實(shí)例代碼 沒(méi)有什么感知,這是因?yàn)闄C(jī)器語(yǔ)言是設(shè)計(jì)給機(jī)器的,人類記憶和使用起來(lái)就會(huì)顯得十分麻煩。
如此你就會(huì)感知到 上個(gè)世紀(jì) 的程序員使用 打孔卡片:
使用 紙帶:
甚至是 直接插拔線路 or 按下開(kāi)關(guān):
是一件多么硬核的事情...
“如果你對(duì)它們?nèi)绾喂ぷ饕约岸嗝从埠烁信d趣,可以參考一下下方的鏈接:
開(kāi)發(fā)語(yǔ)言小傳之一:最早的編程語(yǔ)言——機(jī)器語(yǔ)言 - https://blog.csdn.net/killer080414/article/details/42219091 50年前的登月程序和程序員有多硬核 - https://coolshell.cn/articles/19612.html、 再附帶一個(gè)寶藏網(wǎng)站(哥倫比亞大學(xué)出版的計(jì)算機(jī)歷史,非常詳細(xì)),有條件的同學(xué) 非常推薦 進(jìn)去瀏覽一下:
http://www.columbia.edu/cu/computinghistory/index.html
匯編語(yǔ)言誕生
CPU 的指令都是 二進(jìn)制 的,這顯然對(duì)于人類來(lái)說(shuō)是 不可讀 的。為了解決二進(jìn)制指令的可讀性問(wèn)題,工程師將那些指令寫成了 八進(jìn)制。二進(jìn)制轉(zhuǎn)八進(jìn)制是輕而易舉的,但是八進(jìn)制的可讀性也不行。
很自然地,最后還是用文字表達(dá),加法指令寫成 ADD
。內(nèi)存地址也不再直接引用,而是 用標(biāo)簽 表示。
這樣的話,就多出一個(gè)步驟,要把這些文字指令翻譯成二進(jìn)制,這個(gè)步驟就稱為 assembling
,完成這個(gè)步驟的程序就叫做 assembler
。它處理的文本,自然就叫做 aseembly code
。標(biāo)準(zhǔn)化以后,稱為 assembly language
,縮寫為 asm
,中文譯為 匯編語(yǔ)言。
-
圖片來(lái)源:http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
理解匯編語(yǔ)言
每一種 CPU 的機(jī)器指令都是不一樣的,因此對(duì)應(yīng)的匯編語(yǔ)言也不一樣。本文介紹的是目前最常見(jiàn)的 x86
匯編語(yǔ)言,即 Intel
公司的 CPU 使用的那一種。
寄存器
要學(xué)習(xí)匯編語(yǔ)言,首先必須了解兩個(gè)知識(shí)點(diǎn):寄存器 和 內(nèi)存模型。
先來(lái)看寄存器。CPU 本身只負(fù)責(zé)運(yùn)算,不負(fù)責(zé)儲(chǔ)存數(shù)據(jù)。數(shù)據(jù)一般都儲(chǔ)存在內(nèi)存之中,CPU 要用的時(shí)候就去內(nèi)存讀寫數(shù)據(jù)。但是,CPU 的運(yùn)算速度遠(yuǎn)高于內(nèi)存的讀寫速度,為了避免被拖慢,CPU 都自帶一級(jí)緩存和二級(jí)緩存。基本上,CPU 緩存可以看作是讀寫速度較快的內(nèi)存。
但是,CPU 緩存還是不夠快,另外數(shù)據(jù)在緩存里面的地址是不固定的,CPU 每次讀寫都要尋址也會(huì)拖慢速度。因此,除了緩存之外,CPU 還自帶了寄存器(register),用來(lái)儲(chǔ)存最常用的數(shù)據(jù)。也就是說(shuō),那些最頻繁讀寫的數(shù)據(jù)(比如循環(huán)變量),都會(huì)放在寄存器里面,CPU 優(yōu)先讀寫寄存器,再由寄存器跟內(nèi)存交換數(shù)據(jù)。
寄存器不依靠地址區(qū)分?jǐn)?shù)據(jù),而依靠名稱。每一個(gè)寄存器都有自己的名稱,我們告訴 CPU 去具體的哪一個(gè)寄存器拿數(shù)據(jù),這樣的速度是最快的。有人比喻寄存器是 CPU 的零級(jí)緩存。
寄存器的種類
早期的 x86
CPU 只有 8
個(gè)寄存器,而且每個(gè)都有不同的用途?,F(xiàn)在的寄存器已經(jīng)有 100
多個(gè)了,都變成通用寄存器,不特別指定用途了,但是早期寄存器的名字都被保存了下來(lái)。
“
EAX EBX ECX EDX EDI ESI EBP ESP
上面這 8
個(gè)寄存器之中,前面七個(gè)都是通用的。ESP 寄存器有特定用途,保存當(dāng)前 Stack 的地址(詳見(jiàn)下一節(jié))。
我們常常看到 32
位 CPU、64
位 CPU 這樣的名稱,其實(shí)指的就是寄存器的大小。32
位 CPU 的寄存器大小就是 4
個(gè)字節(jié)。
內(nèi)存模型:Heap(堆)
寄存器只能存放很少量的數(shù)據(jù),大多數(shù)時(shí)候,CPU 要指揮寄存器,直接跟內(nèi)存交換數(shù)據(jù)。所以,除了寄存器,還必須了解內(nèi)存怎么儲(chǔ)存數(shù)據(jù)。
程序運(yùn)行的時(shí)候,操作系統(tǒng)會(huì)給它分配一段內(nèi)存,用來(lái)儲(chǔ)存程序和運(yùn)行產(chǎn)生的數(shù)據(jù)。這段內(nèi)存有起始地址和結(jié)束地址,比如從 0x1000
到 0x8000
,起始地址是較小的那個(gè)地址,結(jié)束地址是較大的那個(gè)地址。
程序運(yùn)行過(guò)程中,對(duì)于動(dòng)態(tài)的內(nèi)存占用請(qǐng)求(比如新建對(duì)象,或者使用 malloc
命令),系統(tǒng)就會(huì)從預(yù)先分配好的那段內(nèi)存之中,劃出一部分給用戶,具體規(guī)則是從起始地址開(kāi)始劃分(實(shí)際上,起始地址會(huì)有一段靜態(tài)數(shù)據(jù),這里忽略)。舉例來(lái)說(shuō),用戶要求得到 10
個(gè)字節(jié)內(nèi)存,那么從起始地址 0x1000
開(kāi)始給他分配,一直分配到地址 0x100A
,如果再要求得到 22
個(gè)字節(jié),那么就分配到 0x1020
。
這種因?yàn)橛脩糁鲃?dòng)請(qǐng)求而劃分出來(lái)的內(nèi)存區(qū)域,叫做 Heap(堆)。它由起始地址開(kāi)始,從低位(地址)向高位(地址)增長(zhǎng)。Heap 的一個(gè)重要特點(diǎn)就是不會(huì)自動(dòng)消失,必須手動(dòng)釋放,或者由垃圾回收機(jī)制來(lái)回收。
內(nèi)存模型:Stack(棧)
除了 Heap 以外,其他的內(nèi)存占用叫做 Stack(棧)。簡(jiǎn)單說(shuō),Stack 是由于 函數(shù)運(yùn)行 而 臨時(shí)占用 的內(nèi)存區(qū)域。
例如我們?cè)趫?zhí)行一個(gè)叫 main
的函數(shù)時(shí),會(huì)為它在內(nèi)存里面創(chuàng)建一個(gè) 幀,用來(lái)保存所有 main
中使用的內(nèi)部變量。main
函數(shù)執(zhí)行結(jié)束后,該幀就會(huì)被回收,釋放所有的內(nèi)部變量,不再占用空間。
如果在 main
函數(shù) 內(nèi)部調(diào)用了其他函數(shù),例如 add_a_and_b
函數(shù),那么執(zhí)行到這一行的時(shí)候,系統(tǒng)也會(huì)為 add_a_and_b
新建一個(gè)幀,用來(lái)儲(chǔ)存它的內(nèi)部變量。也就是說(shuō),此時(shí)同時(shí)存在兩個(gè)幀:main
和 add_a_and_b
。一般來(lái)說(shuō),調(diào)用棧有多少層,就有多少幀。
等到 add_a_and_b
運(yùn)行結(jié)束,它的幀就會(huì)被回收,系統(tǒng)會(huì)回到函數(shù) main
剛才中斷執(zhí)行的地方,繼續(xù)往下執(zhí)行。通過(guò)這種機(jī)制,就實(shí)現(xiàn)了函數(shù)的 層層調(diào)用,并且 每一層都能使用自己的本地變量。
我們可以把棧理解為一個(gè)下方密封,而上方打開(kāi)的「桶」。
生成的新幀放入我們稱之為 「入?!?/strong>,而釋放幀我們稱之為 「出棧」。棧的特點(diǎn) 就是,最晚入棧的幀最早出棧(因?yàn)樽顑?nèi)層的函數(shù)調(diào)用,最先結(jié)束運(yùn)行),這就叫做 "后進(jìn)先出" 的數(shù)據(jù)結(jié)構(gòu)。每一次函數(shù)執(zhí)行結(jié)束,就自動(dòng)釋放一個(gè)幀,所有函數(shù)執(zhí)行結(jié)束,整個(gè)棧就都釋放了。
匯編語(yǔ)言演示
舉個(gè)簡(jiǎn)單的例子,我們需要計(jì)算:
(1 + 4) * 2 + 3
我們按照 「后綴表示法」 進(jìn)行一下轉(zhuǎn)換:
1,4,+,2,*,3,+
我們平常使用的方法是 「中綴表示法」,也就是把計(jì)算符號(hào)放中間,例如 1 + 3
,后綴則是把符號(hào)放最后,例如 1, 3, +
。
這樣做的好處是沒(méi)有先乘除后加減的影響,也沒(méi)有括號(hào),直接運(yùn)算就行了。(例如 1, 3, +
,先把 1
和 3
保存起來(lái)碰到 +
知道是加法則直接相加)
OK,我們從頭開(kāi)始使用匯編語(yǔ)言來(lái)編寫一下程序,首先第一步:把 1
保存起來(lái)(放入寄存器):
MOV 1
之后是 4, +
,那就直接加一下:
ADD 4
然后是 2, *
,那就直接乘一下(SHL
是向左移動(dòng)一位的意思,二進(jìn)制中左移一個(gè)單位就相當(dāng)于乘以 2
,例如 01
表示 1
,而 10
則表示 2
):
SHL 0
最后是 3, +
,再加一下:
ADD 3
完整程序如下:
MOV 1
ADD 4
SHL 0
ADD 3
這似乎看起來(lái)比 00001111
這樣的二進(jìn)制要好上太多了!程序員們感動(dòng)到落淚:
Part 3. 高級(jí)編程語(yǔ)言
擺脫了 二進(jìn)制,我們有了更可讀的 匯編語(yǔ)言,但仍然十分繁瑣和復(fù)雜,每一條匯編指令代表一個(gè)基本操作,例如:「從內(nèi)存 x 位置獲取一個(gè)數(shù)字并放入寄存器 A」、「將寄存器 A 中的數(shù)字添加到寄存器 B 的數(shù)字上」。這樣的編程風(fēng)格既費(fèi)時(shí)又容易出錯(cuò),并且一旦出錯(cuò)還很難發(fā)現(xiàn)。
例如,我們來(lái)看一看 「1969 年阿波羅 11號(hào)登月計(jì)劃」 用來(lái) 防止登月艙計(jì)算機(jī)耗盡自身資源 的 BAILOUT 代碼:
POODOO INHINT
CA Q
TS ALMCADR
TC BANKCALL
CADR VAC5STOR # STORE ERASABLES FOR DEBUGGING PURPOSES.
INDEX ALMCADR
CAF 0
ABORT2 TC BORTENT
OCT77770 OCT 77770 # DONT MOVE
CA V37FLBIT # IS AVERAGE G ON
MASK FLAGWRD7
CCS A
TC WHIMPER -1 # YES. DONT DO POODOO. DO BAILOUT.
TC DOWNFLAG
ADRES STATEFLG
TC DOWNFLAG
ADRES REINTFLG
TC DOWNFLAG
ADRES NODOFLAG
TC BANKCALL
CADR MR.KLEAN
TC WHIMPER
-
出處:改變世界的代碼行 - https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX
似乎不太容易讀的樣子...
“阿波羅登月計(jì)劃的源代碼在 Github 上已經(jīng)公開(kāi),有興趣的可以去下方鏈接膜拜一下(可以去感受一下當(dāng)時(shí)程序員的工程能力):
https://github.com/chrislgarry/Apollo-11 另外附一下當(dāng)時(shí)代碼的設(shè)計(jì)負(fù)責(zé)人 Margaret Heafield Hamilton(女程序員)和完成的堆起來(lái)跟人一樣高的代碼量:
第一個(gè)高級(jí)語(yǔ)言:FORTRAN
當(dāng) John Backus 在 1950
年以一名科學(xué)程序員的身份加入 IBM 時(shí),已經(jīng)可以使用諸如 ADD
之類的助記詞代替數(shù)字代碼來(lái)編寫程序,也就是我們的匯編語(yǔ)言。這使編程變得容易一些,但是即使是一個(gè)簡(jiǎn)單的程序也需要數(shù)十次操作,并且仍然很難找到錯(cuò)誤。
巴克斯認(rèn)為,應(yīng)該有可能創(chuàng)建一種編程語(yǔ)言,使一系列計(jì)算可以用類似于數(shù)學(xué)符號(hào)的形式來(lái)表達(dá)。然后,使用特定的翻譯程序(以今天的術(shù)語(yǔ)來(lái)說(shuō)是編譯器)可以將其轉(zhuǎn)換為計(jì)算機(jī)可以理解的數(shù)字代碼。
Backus 在 1953
年向他的經(jīng)理提出了這個(gè)想法。他得到了預(yù)算,并被鼓勵(lì)雇用一個(gè)小團(tuán)隊(duì)來(lái)測(cè)試該想法的可行性。三年后,該團(tuán)隊(duì)發(fā)布了一本手冊(cè),其中描述了 IBM Mathematical Formula Translating System(簡(jiǎn)稱 FORTRAN)。不久之后, IBM 向 IBM 704 的用戶提供了第一個(gè) FORTRAN 編譯器。
Backus 和他的團(tuán)隊(duì)創(chuàng)造了世界上第一種高級(jí)編程語(yǔ)言。科學(xué)家和工程師將不再需要將其程序編寫為數(shù)字代碼或冗長(zhǎng)的助記符。
FORTRAN 代碼演示
下面演示計(jì)算并輸出 8 * 6
的代碼實(shí)例:
program VF0944
implicit none
integer a, b, c
a= 8
b= 6
c= a*b
print *, 'Hello World, a, b, c= ', a, b, c
end program VF0944
對(duì)比匯編代碼,是不是看上去要清晰(人類可讀)多了呢?
FORTRAN 的意義
FORTRAN 的問(wèn)世在計(jì)算機(jī)史上具有劃時(shí)代的意義,它使計(jì)算機(jī)語(yǔ)言從原始的低級(jí)匯編語(yǔ)言走出來(lái),進(jìn)入了更高的境界,使得 計(jì)算機(jī)語(yǔ)言不再是計(jì)算機(jī)專家的專利,使廣大的工程技術(shù)人員有了進(jìn)行計(jì)算機(jī)編程的手段。
由此計(jì)算機(jī)更快地深入到了社會(huì)之中,它在工業(yè)部門中初露頭角,更是在火箭、導(dǎo)彈、人造地球衛(wèi)星的設(shè)計(jì)中大顯身手,因此有人稱 FORTRAN 語(yǔ)言使計(jì)算機(jī)的工業(yè)應(yīng)用成了可能,是推動(dòng)第二次世界大戰(zhàn)以后西方工業(yè)經(jīng)濟(jì)復(fù)蘇和進(jìn)入第二次工業(yè)革命的無(wú)形力量,是 "看不見(jiàn)的蒸汽機(jī)"。
FORTRAN 后時(shí)代
FORTRAN 高級(jí)程序設(shè)計(jì)語(yǔ)言的出現(xiàn)孕育了計(jì)算機(jī)軟件業(yè),繼其之后,計(jì)算機(jī)高級(jí)程序語(yǔ)言的開(kāi)發(fā)進(jìn)入到了一個(gè)蓬勃發(fā)展的時(shí)代。
1959
Grace Hopper 發(fā)明了第一個(gè)面向企業(yè)業(yè)務(wù)的編程語(yǔ)言,又稱 “面向商業(yè)的通用語(yǔ)言”,也常常簡(jiǎn)稱 COBOL。
1964
美國(guó)達(dá)特茅斯學(xué)院約翰·凱梅尼和托馬斯·卡茨認(rèn)為,像 FORTRAN 那樣的語(yǔ)言太過(guò)專業(yè),編程非常困難。于是他們簡(jiǎn)化了 FORTRAN,并設(shè)計(jì)出了更適合初學(xué)者的 BASIC 語(yǔ)言。
1970
尼古拉斯·沃斯非常癡迷于編程語(yǔ)言,他率先提出了結(jié)構(gòu)化程序設(shè)計(jì)思想并發(fā)明了 Pascal 語(yǔ)言。
此外他還提出了 Wirth 定律,意為 “軟件變慢的速度比硬件變快的速度更快”,這讓摩爾定律變得充滿諷刺。之后的 Electron.js 也確實(shí)證明了這一點(diǎn)。
1972
丹尼斯·里奇在貝爾實(shí)驗(yàn)室工作期間發(fā)明了 C 語(yǔ)言,開(kāi)啟了現(xiàn)代程序語(yǔ)言的革命。之后,他又添加了段錯(cuò)誤和其他一些幫助開(kāi)發(fā)人員的實(shí)用功能,大大提升了編程效率。
除了 C 語(yǔ)言之外, 他和貝爾實(shí)驗(yàn)室的同事還創(chuàng)造了偉大的 Unix 操作系統(tǒng)。
1980
Alan Kay 發(fā)明了面向?qū)ο蟮木幊陶Z(yǔ)言 Smalltalk,在 Smalltalk 中,一切皆對(duì)象。
1987
拉里·沃爾發(fā)明了 Perl 語(yǔ)言。
1983
Jean Ichbiah 發(fā)現(xiàn) Ada Lovelace 的程序從未運(yùn)行成功過(guò),因此決定用她的名字創(chuàng)建一種語(yǔ)言,于是 Ada 語(yǔ)言誕生了。
1986
Brac Box 和 Tol Move 通過(guò)融合 C 語(yǔ)言和 Smalltalk 的特征,發(fā)明了 Objective-C。但由于其語(yǔ)法晦澀,不太容易理解。
1983
Bjarne Stroustrup 在 C 語(yǔ)言的基礎(chǔ)上引入并擴(kuò)充了面向?qū)ο蟮母拍?,發(fā)明了—種新的程序語(yǔ)言并將其命名為 C++。
C++ 大大提升了應(yīng)用程序的編程效率。
1991
Guido van Rossum 討厭帶有大括號(hào)的編程語(yǔ)言,于是他參考 Monty Python 和 Flying Circus 語(yǔ)法,并發(fā)明了 Python。
1993
Roberto Ierusalimschy 和其朋友創(chuàng)造了一門巴西本地的腳本語(yǔ)言。在本地化過(guò)程中,由于一個(gè)小的錯(cuò)誤使得索引從1開(kāi)始,而不是0。這門語(yǔ)言就是 Lua。
1994
Rasmus Lerdorf 為他個(gè)人主頁(yè)的 CGI 腳本制作了一個(gè)模板引擎,用來(lái)統(tǒng)計(jì)他自己網(wǎng)站的訪問(wèn)量。
這個(gè)文件被上傳到網(wǎng)上之后用它的人越來(lái)越多。后來(lái)又用 C 語(yǔ)言重新編寫,還添加了數(shù)據(jù)庫(kù)訪問(wèn)功能。這門語(yǔ)言就是 PHP。
1995
松本行弘發(fā)明了 Ruby 語(yǔ)言。
1995
Brendan Eich 利用周末時(shí)間設(shè)計(jì)了一種語(yǔ)言,用于為世界各地的網(wǎng)頁(yè)瀏覽器提供支持,并最終推出了 Skynet。他最初去了 Netscape,并將這門語(yǔ)言命名為 LiveScript,后來(lái)在代碼審查期間 Java 逐漸開(kāi)始風(fēng)靡,因此他們決定將其改名為 JavaScript。
后來(lái) Java 使其陷入了商標(biāo)麻煩,于是 JavaScript 被更名為 ECMAScript。但是人們還是習(xí)慣稱之為 JavaScript。
1996
James Gosling 發(fā)明了 Java,這是 第一個(gè)真正意義上面向?qū)ο蟮镁幊陶Z(yǔ)言,其中設(shè)計(jì)模式在實(shí)用主義中占統(tǒng)治地位。
More...
“對(duì)于這一段計(jì)算機(jī)歷史感興趣的同學(xué)可以拜讀一下「IT 通史 12.2 節(jié) - 高級(jí)計(jì)算機(jī)程序設(shè)計(jì)語(yǔ)言」的內(nèi)容,在線預(yù)覽鏈接如下:
https://books.google.com.hk/books?id=ZrAol3RzcNkC&printsec=frontcover&hl=zh-CN#v=onepage&q&f=false
高級(jí)語(yǔ)言分類
CPU 終究只認(rèn)識(shí)二進(jìn)制指令,在我們發(fā)明高級(jí)語(yǔ)言之后,仍然無(wú)可避免的需要進(jìn)行 「翻譯」 工作。按照翻譯方式的不同,我們又把高級(jí)語(yǔ)言分為了 「編譯型」 和 「解釋型」。
編譯型
編譯型專業(yè)解釋為:
使用 專門的編譯器,針對(duì) 特定的平臺(tái),將高級(jí)語(yǔ)言源代碼 一次性 的編譯成可被該平臺(tái)硬件執(zhí)行的機(jī)器碼,并包裝成該平臺(tái)所能識(shí)別的可執(zhí)行性程序的格式,并且只需要編譯一次,以后再也不用編譯。其實(shí)可以簡(jiǎn)單理解成谷歌/ 百度翻譯,我們把要翻譯的文字全部放進(jìn)去,一次翻譯,下次使用直接使用上一次翻譯好的結(jié)果。
-
優(yōu)點(diǎn)(較解釋型):執(zhí)行效率高(有解釋器省去很多翻譯的過(guò)程) -
缺點(diǎn)(較解釋型):開(kāi)發(fā)效率低(寫完所有的代碼才能檢查 bug
,得多恐怖呀???)
解釋型
解釋型專業(yè)解釋為:
使用 專門的解釋器 對(duì)源程序逐行解釋成 特定平臺(tái) 的機(jī)器碼并 立即執(zhí)行,它不需要事先編譯,直接將代碼解釋稱機(jī)器碼直接運(yùn)行,也就是說(shuō)只要某一平臺(tái)提供了相應(yīng)的解釋器即可運(yùn)行代碼。其實(shí)可以理解成同聲傳譯,我們需要翻譯的時(shí)候,找一個(gè)翻譯員,對(duì)方說(shuō)一句翻譯員翻譯一句,下次翻譯還是需要一個(gè)翻譯員一句一句的翻譯。
-
缺點(diǎn)(較編譯型):執(zhí)行效率低(寫一次翻譯一次) -
優(yōu)點(diǎn)(較編譯型):開(kāi)發(fā)效率高(寫一行翻譯一行,錯(cuò)了馬上就知道,媽媽再也不用擔(dān)心我找不到 bug 了)
半解釋半編譯的 Java
不同廠商、不同時(shí)間開(kāi)發(fā)的 CPU 的指令集是不一樣的,這就是上方為什么提到要使用 專門的解釋器,要用于 特定的平臺(tái) 的原因。
所以 Java 為了實(shí)現(xiàn) 「一次編譯,到處運(yùn)行」 的目的,采用了一種特別的方案:先 編譯 為 與任何具體及其環(huán)境及操作系統(tǒng)環(huán)境無(wú)關(guān)的中間代碼(也就是 .class
字節(jié)碼文件),然后交由各個(gè)平臺(tái)特定的 Java 解釋器(也就是 JVM)來(lái)負(fù)責(zé) 解釋 運(yùn)行。
編程人員和計(jì)算機(jī)都無(wú)法直接讀懂字節(jié)碼文件,它必須由專用的 Java 解釋器來(lái)解釋執(zhí)行,因此 Java 是一種在 編譯基礎(chǔ)上進(jìn)行解釋運(yùn)行 的語(yǔ)言。(Java 程序運(yùn)行流程如下)
Java 解釋器 負(fù)責(zé)將字節(jié)碼文件翻譯成具體硬件環(huán)境和操作系統(tǒng)平臺(tái)下的機(jī)器代碼,以便執(zhí)行。因此 Java 程序不能直接運(yùn)行在現(xiàn)有的操作系統(tǒng)平臺(tái)上,它必須運(yùn)行在被稱為 Java 虛擬機(jī)的軟件平臺(tái)之上。
Java 虛擬機(jī)(JVM) 是運(yùn)行 Java 程序的軟件環(huán)境(我們后面會(huì)詳細(xì)說(shuō)到,這是學(xué)習(xí) Java 繞不過(guò)的題),Java 解釋器是 Java 虛擬機(jī)的一部分。在運(yùn)行 Java 程序時(shí),首先會(huì)啟動(dòng) JVM,然后由它來(lái)負(fù)責(zé)解釋執(zhí)行 Java 的字節(jié)碼程序,并且 Java 字節(jié)碼程序只能運(yùn)行于 JVM 之上。這樣利用 JVM 就可以把 Java 字節(jié)碼程序和具體的硬件平臺(tái)以及操作系統(tǒng)環(huán)境分隔開(kāi)來(lái),只要在不同的計(jì)算機(jī)上安裝了針對(duì)特定平臺(tái)的 JVM,Java 程序就可以運(yùn)行,而不用考慮當(dāng)前具體的硬件平臺(tái)及操作系統(tǒng)環(huán)境,也不用考慮字節(jié)碼文件是在何種平臺(tái)上生成的。
JVM 把這種不同軟、硬件平臺(tái)的具體差別隱藏起來(lái),從而 實(shí)現(xiàn)了真正的二進(jìn)制代碼級(jí)的跨平臺(tái)移植。JVM 是 Java 平臺(tái)架構(gòu)的基礎(chǔ),Java 的跨平臺(tái)特性正是通過(guò)在 JVM 中運(yùn)行 Java 程序?qū)崿F(xiàn)的。Java 的這種運(yùn)行機(jī)制可以通過(guò)下圖來(lái)說(shuō)明:
Java 語(yǔ)言這種「一次編寫,到處運(yùn)行」的方式,有效地解決了目前大多數(shù)高級(jí)程序設(shè)計(jì)語(yǔ)言需要針對(duì)不同系統(tǒng)來(lái)編譯產(chǎn)生不同機(jī)器代碼的問(wèn)題,即硬件環(huán)境和操作平臺(tái)的異構(gòu)問(wèn)題,大大降低了程序開(kāi)發(fā)、維護(hù)和管理的開(kāi)銷。
-
提示: Java 程序通過(guò) JVM 可以實(shí)現(xiàn)跨平臺(tái)特性,但 JVM 是不跨平臺(tái)的。也就是說(shuō), 不同操作系統(tǒng)之上的 JVM 是不同的,Windows 平臺(tái)之上的 JVM 不能用在 Linux 平臺(tái),反之亦然。
參考資料
-
Introduction to Computer Science using Java | CHAPTER 4 - http://programmedlessons.org/Java9/chap04/ch04_01.html -
匯編語(yǔ)言入門教程 - http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html -
CPU 是怎么認(rèn)識(shí)代碼的?| 知乎@Zign - https://www.zhihu.com/question/348237008/answer/843382847 -
改變世界的代碼行 - https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX -
The History of FORTRAN - https://www.obliquity.com/computer/fortran/history.html -
《IT 通史》 | @李彥 -
A Brief Totally Accurate History Of Programming Languages - https://medium.com/commitlog/a-brief-totally-accurate-history-of-programming-languages-cd93ec806124 -
編程語(yǔ)言分類 - https://www.cnblogs.com/nickchen121/p/10722720.html
特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:
長(zhǎng)按訂閱更多精彩▼
如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!