D. 高級語言怎么來的
本文關鍵詞:高級語言,由筆耕文化傳播整理發(fā)布。
作者:徐宥
原文:
我主要想八卦一下高級語言的設計思想和各種范式的來龍去脈編程語言為什么會發(fā)生成現(xiàn)在這個樣子哩包括虛擬機的設計棧和寄存器兩大流派的來龍去脈等等, 也算是完成年初給大家許下的諾言. 僅電路與連線天地開, 始有FORTRAN, LISP. ALGOL是基于遞歸函數(shù)的現(xiàn)在的都比較像不像可是很少有人知道, 最初, FORTRAN所有高級語言里面的遞歸調(diào)用那里學來的值得八卦一番. 之外不是階乘就是菲波拉契數(shù)列基本上都是因為函數(shù)的遞歸調(diào)用才顯得簡單漂亮人民非常想念您第一版的就居然居然不支持遞歸不支持遞歸的語言能圖靈完全么但是沒遞歸調(diào)用的程序會很難寫那么, FORTRAN年. 至于計算能力嘛, 卻比你的手機還弱. 那時候計算機所做的最多的事情, 不是發(fā)郵件打游戲, 而是作計算. 作計算嘛, 自然需要一種和數(shù)學語言比較接近的編程語言. 于是, 1960年, IBM 就搗鼓出了用行話說 用現(xiàn)在的眼光看所以數(shù)學公式加上一個數(shù)組, 基本上就能完成所有的科學計算了. IBM 語言規(guī)范, 并且在自家的大型機上實現(xiàn)了這個語言. 編譯器那時候的編譯器都是用機器語言或者很低級的匯編語言寫成的這些工程師覺得因為在科學計算中用到的變量等就是可以知道大小的也就相當于沒有的或者沒有++一個程序要多少內(nèi)存這個主意看上去很聰明的工程師比你想得更加聰明既然一個程序或者子程序要多少內(nèi)存在編譯的時候都知道了子程序中參數(shù)大小都定好是的在沒有操作系統(tǒng)管理的情況下如果內(nèi)存放的整整齊齊的這樣也是一件非常好的事情. (再次強調(diào)操作系統(tǒng)要等到才有人月神話>). 這樣靜態(tài)的搞內(nèi)存分配為啥呢我有個函數(shù)個菲波拉契數(shù)返回一個整數(shù), FORTRAN我運行起來存在某個專門給輸入?yún)?shù)的位置里面遞歸的調(diào)用了Fib(4), FORTRAN不還是么我存新的參數(shù)給覆蓋掉了也把原來的返回值給覆蓋掉了這么一搞這下了怎么遞歸啊? 不是不知道這個問題你丫科學計算遞歸什么呀展不開是你數(shù)學沒學好你就不要用那時候乃是老大老大發(fā)話, 下面的消費者只能聽他的. 所以就壓根沒有任何棧支持計算機發(fā)展史上我們現(xiàn)在也很難猜測的軟件工程師因為的硬件工程師沒有在硬件上設計出堆棧所以沒有能在里面設計出遞歸調(diào)用呢的硬件工程師覺得既然軟件沒要求不管怎么樣, 我們看到的是, 1960熟悉里面一個叫做用來標記下一條要執(zhí)行的指令的位置如果沒有后者因為需要程序員手工維護一個棧而當年壓根就不支持遞歸的硬件如果一個程序員想要遞歸調(diào)用就是讓程序員借用一個通用寄存器作為棧指針而且不能用 FORTRAN. 按照自然規(guī)律于是, 很快的, LISP這兩個新語言就出道了它的創(chuàng)始人是 是學院派人物演算所以, LISP因為遞歸就是 但是有兩大問題擺在 一是他的二是他的 的指令而這個按照 Paul Graham里面的說法指令的一個叫 Steve Russell的工程師宣稱要實現(xiàn)的時候?qū)嶋H是實際可是, Russell叫 就是寫了一個解釋器在這個解釋器里面跑讓傳統(tǒng)上編譯-> 運行 的高級語言流程解釋執(zhí)行的流程流程相當于在用來解釋所有的 這個創(chuàng)舉從理論走到了實踐. 就可以怎么遞歸上面我也說了寫有了解釋器因為現(xiàn)在所有的空間分配都可以由解釋器管理了運行時環(huán)境允許你動態(tài)的分配空間隨之就帶來了一項新技術這個技術出現(xiàn)在 是解釋器的自然要求和歸宿上本來被繞過的問題里面用全新的方法被解決了. LISP比如抽象語法樹垃圾收集都很早的出現(xiàn)在了加上本身規(guī)則很少所以特別是和解釋器和運行時相關的一項新技術出現(xiàn)“這玩意兒里早就有了”是有一定道理的. 這一派為日后所有虛擬機理論鋪開了一條新路. 這一派在70, 80年代迅速崛起的興起又迅速的隕落, 讓人唏噓不已. 高級語言編程領域也發(fā)生了一件大事的提出. ALGOL全是 注意到了 于是從一開始但是, 處理遞歸需要很小心的安排每個函數(shù)每次調(diào)用的地址和所謂的活動窗口(Active Frame),難免會出點小問題和小這時候出場了叫做 “是男人就得負不能給各位讀者把這個男人測試的關竅講清楚我知道乃是看 ALGOL 60真的男人要能得到正確答案當然所以自己猜了一個后來正確答案是可見也不是男人編譯器…虛擬機的前世今生
因為的原因, 發(fā)展出了運行時環(huán)境這樣一個概念。基于這個概念,日后發(fā)展出了虛擬機技術。但這段歷史并不是平鋪直敘的,實際上,這里面還經(jīng)歷了一個非常漫長而曲折的過 程, 說起來也是非常有意思的。 這一節(jié)我們就著重解釋虛擬機的歷史。
我們世紀的程序員,凡要是懂一點編程技術的,基本上都知道虛擬機和字節(jié)碼這樣兩個重要的概念。 所謂的字節(jié)碼 (bytecode), 是一種非常類似于機器碼的指令格式。這種指令格式是以二進制字節(jié)為單位定義的(不會有一個指令只用到一個字節(jié)的前四位),所以叫做字節(jié)碼。所謂的虛擬機, 就是說不是一臺真的計算機,而是一個環(huán)境,其他程序能在這個環(huán)境中運行,而不是在真的機器上運行,F(xiàn)在主流高級語言如 Java, Python, PHP, C#,編譯后的代碼都是以字節(jié)碼的形式存在的, 這些字節(jié)碼程序, 最后都是在虛擬機上運行的。
1. 虛擬機的安全性和跨平臺性
虛擬機的好處大家都知道,最容易想到的是安全性和跨平臺性。安全性是因為現(xiàn)在可執(zhí)行程序被放在虛擬機環(huán)境中運行,虛擬機可以隨時對程序的危險行為, 比如緩沖區(qū)溢出,數(shù)組訪問過界等等進行控制。跨平臺性是因為只要不同平臺上都裝上了支持同一個字節(jié)碼標準的虛擬機,程序就可以在不同的平臺上不加修改而運 行,因為虛擬機架構在各種不同的平臺之上,用虛擬機把下層平臺間的差異性給抹平了。我們最熟悉的例子就是了。Java 語言號稱 一次編寫,到處運行(Write Once, Run Anywhere),就是因為各個平臺上的虛擬機都統(tǒng)一支持字節(jié)碼,所以用戶感覺不到虛擬機下層平臺的差異。
虛擬機是個好東西,但是它的出現(xiàn),不是完全由安全性和跨平臺性驅(qū)使的。
2. 跨平臺需求的出現(xiàn)
我們知道,在計算機還是鎖在機房里面的昂貴的龐然大物的時候,系統(tǒng)軟件都是硬件廠商附送的東西(是比爾蓋茨這一代人的出現(xiàn),才有了和硬件產(chǎn)業(yè)分庭抗礼的軟件產(chǎn)業(yè)), 一個系統(tǒng)程序員可能一輩子只和一個產(chǎn)品線的計算機打交道,壓根沒有跨平臺的需求。應用程序員更加不要說了,因為計算機很稀有,寫程序都是為某一臺計算機專 門寫的,所以一段時間可能只和一臺龐然大物打交道,更加不要說什么跨平臺了。 真的有跨平臺需求,是從微型計算機開始真的普及開始的。因為只有計算機普及了,各種平臺都被廣泛采用了,相互又不互相兼容軟件,才會有軟件跨平臺的需求。 微機普及的歷史,比普及的歷史要早發(fā)展史是并行重疊的。
熟悉發(fā)展史的讀者都知道,真正普及開來,是因為其全部都用版本的出生沒多久,就迅速從原始的實現(xiàn),移植到了等平臺上,產(chǎn)生了無數(shù)衍生版本。隨著跨平臺的的普及, 微型計算機也更多的普及開來,因為只需要掌握基本的知識,就可以順利操作微型計算機了。所以,微機和這兩樣東西都在年在美國政府,大學,科研機構,公司,金融機構等各種信息化前沿部門間真正的普及開來了。這些歷史都是人所共知耳熟能詳?shù)摹?/p>
既然是跨平臺的,那么,本節(jié)所有的故事都和無關,因為本身就不是一個跨平臺的操作系統(tǒng))。的跨平臺性,一般是以各平臺廠商提供編譯器的方式實現(xiàn)的,而最終編譯生成的可執(zhí)行程序,其實不是跨平臺的。所以,跨平臺是源代碼級別的跨平臺,而不是可執(zhí)行程序?qū)用娴?/span>。 而除了標準了語言外,UNIX 上有一派生機勃勃的跨平臺語言,就是腳本語言。(注:腳本語言和普通的編程語言相比,在能完成的任務上并沒有什么的巨大差異。腳本語言往往是針對特定類型的問題提出的,,語法更加簡單,功能更加高層,常常幾百行C語言要做的事情,幾行簡單的腳本就能完成)
3. 解釋和執(zhí)行
腳本語言美妙的地方在于,它們的源代碼本身就是可執(zhí)行程序,所以在兩個層面上都是跨平臺的。不難看出,腳本語言既要能被直接執(zhí)行,又要跨平臺的話,就必然要有一個“東西”,橫亙在語言源代碼和平臺之間,往上,在源代碼層面,分析源代碼的語法,結構和邏輯,也就是所謂的“解釋”;往下,要隱藏平臺差異,使得源代碼中的邏輯,能在具體的平臺上以正確的方式執(zhí)行,也就是所謂的“執(zhí)行”。
雖說我們知道一定要這么一個東西,能夠?qū)ι虾蛢蓚模塊畢竟是相互獨立的,因此就很自然的會出現(xiàn)兩個流派:把解釋和執(zhí)行設計到一起 和 把解釋和執(zhí)行單獨分開來 這樣兩個設計思路,需要讀者注意的是,現(xiàn)在這兩個都是跨平臺的,安全的設計,而在后者中字節(jié)碼作為了解釋和執(zhí)行之間的溝通橋梁,前者并沒有字節(jié)碼作為橋梁。
4. 解釋和執(zhí)行在一起的方案
我們先說前者,前者的優(yōu)點是設計簡單,不需要搞什么字節(jié)碼規(guī)范,所以上早期的腳本語言,都是采用前者的設計方法。 我們以上大名鼎鼎的和兩個腳本語言的解釋器為例說明。和都是上極為常用的,圖靈完全的語言,其中在任何系統(tǒng)中都是作為標準配置的,甚至入選 IEEE POSIX盧浮宮的唯一同類語言品牌,其地位絕對不是下其他腳本語言能夠比的。這兩個語言是怎么實現(xiàn)解釋和運行的呢? 我從的標準實現(xiàn)中摘一段代碼您一看就清楚了:
argv[]) { ... syminit(); yyparse(); ... run(winner); } ... }其中, run 的原型是
run(Node *a) /* execution of parse tree starts here */
而 winner 的定義是:
Node *winner ; /* root of parse tree */
熟悉的讀者應該能夠立即看出解析源代碼,生成了一棵語法樹。按照 winner 的定義, winner 是這棵語法樹的根節(jié)點。 在變成了 0),將 run 作用到這棵語法樹的根節(jié)點上。 不難想像,這個的基本邏輯先用解釋器(這里解釋器),生成一棵語法樹,然后,從樹的根節(jié)點開始,往下用這個函數(shù),遇山開山,遇水搭橋,一路遞歸下去,最后把整個語法樹遍歷完,程序就執(zhí)行完畢了。(這里附送一個小八卦,抽象語法樹這個概念是先提出的,因為是最早像這樣做的,的源代碼也是類似的邏輯解釋執(zhí)行的,我就不一一舉例了。
5. 三大缺點
現(xiàn)在我們看看這個方法的優(yōu)缺點。 優(yōu)點是顯而易見的,因為通過抽象語法樹在兩個模塊之間通信,避免了設計復雜的字節(jié)碼規(guī)范,設計簡單。但是缺點也非常明顯。最核心的缺點就是性能差,需要資源多,具體來說,就是如下三個缺點。
缺點1,因為解釋和運行放在了一起,每次運行都需要經(jīng)過解釋這個過程。假如我們有一個腳本,寫好了就不修改了,只需要重復的運行,那么在一般應用下尚可以忍受每次零點幾秒的重復冗余的解釋過程,在高性能的場合就不能適用了。
缺點2,因為運行是采用遞歸的方式的,效率會比較低。 我們都知道,因為遞歸涉及到棧操作和狀態(tài)保存和恢復等,代價通常比較高,所以能不用遞歸就不用遞歸。在高性能的場合使用遞歸去執(zhí)行語法樹,不值得。
缺點3,因為一切程序的起點都是源代碼,而抽象語法樹不能作為通用的結構在機器之間互傳,所以不得不在所有的機器上都布置一個解釋+運行的模塊。 在資源充裕的系統(tǒng)上布置一個這樣的系統(tǒng)沒什么,可在資源受限的系統(tǒng)上就要慎重了,比如嵌入式系統(tǒng)上。 鑒于有些語言本身語法結構復雜,布置一個解釋模塊的代價是非常高昂的。本來一個遞歸執(zhí)行模塊就很吃資源了,再加一個解釋器,嵌入式系統(tǒng)就沒法做了。所以, 這種設計在嵌入式系統(tǒng)上是行不通的。
當然,還有一些其他的小缺點,比如有程序員不喜歡開放源代碼,但這種設計中,一切都從源代碼開始,要發(fā)布可執(zhí)行程序,就等于發(fā)布源代碼,所以不愿意 公布源代碼的商業(yè)公司很不喜歡這些語言等等。但是上面的三個缺點,是最致命的,這三個缺點,決定了有些場合,就是不能用這種設計。
6. 分開解釋和執(zhí)行
前面的三個主要缺點,恰好全部被第二個設計所克服了。在第二種設計中, 我們可以只解釋一次語法結構,生成一個結構更加簡單緊湊的字節(jié)碼文件。這樣,以后每次要運行腳本的時候, 只需要把字節(jié)碼文件送給一個簡單的解釋字節(jié)碼的模塊就行了。因為字節(jié)碼比源程序要簡單多了,所以解釋字節(jié)碼的模塊比原來解釋源程序的模塊要小很多;同時, 脫離了語法樹,我們完全可以用更加高性能的方式設計運行時,避免遞歸遍歷語法樹這種低效的執(zhí)行方式;同時,在嵌入式系統(tǒng)上,我們可以只部署運行時,不部署 編譯器。 這三個解決方案,預示了在運行次數(shù)遠大于編譯次數(shù)的場合,或在性能要求高的場合,或在嵌入式系統(tǒng)里,想要跨平臺和安全性,就非得用第二種設計,也就是字節(jié) 碼+虛擬機的設計。
講到了這里,相信對對或者對歷史稍微了解的讀者都會一拍腦袋頓悟了: 原來這些牛逼的虛擬機都不是天才拍腦袋想出來的,而是被需求和現(xiàn)實給召喚出來的。
我們先以為例,說說在嵌入式場合的應用。語言,最初不是為桌面和服務器應用開發(fā)的,而是為機頂盒開發(fā)的。的唯一目的,就是為了參加機頂盒項目的競標。嵌入式系統(tǒng)的資源受限程度不必細說了,自然不會允許上面放一個解釋器和一個運行時。所以,不管虛擬機設計得直白無比,簡單無比,手機上,智能卡上都能放上一個運行時(當然是精簡版本的)。 這就是字節(jié)碼和虛擬機的威力了。
正好對繪圖支持非常好,在一統(tǒng)江湖之前,憑借跨平臺性能,以的名義一舉走紅。然后,又因為這種設計先天性的能克服性能問題,在性能上大作文章,憑借技術,充分發(fā)揮上面說到的優(yōu)點2,再加上安全性,一舉拿下了企業(yè)服務器市場的半壁江山,這都是后話了。
再說的歷史就包含了從第一種設計轉(zhuǎn)化到第二種設計以用來優(yōu)化運行時性能的歷史。是一般用來生成服務器網(wǎng)頁的腳本語言。一個大站點上的PHP腳本, 一旦寫好了,每天能訪問千百萬次,所以,如果全靠每次都解釋,每次都遞歸執(zhí)行,性能上是必然要打折扣的。 所以,從開始,腳本解釋引擎,就開始將解釋成字節(jié)碼,以支持這種一次解釋,多次運行的框架。 在此之前,和還有還算平分秋色的樣子,基本上服務器上三類網(wǎng)頁的數(shù)量都差不多,三者語法也很類似,但是到了出現(xiàn)之后,其他兩個基于第一種設計方案的頁面就慢慢消逝了, 全部讓位給博客,也是基于技術的,底層也是引擎的。 著名的里面的那個 P, 原始上也是年出現(xiàn)之后的事情。
第二種設計的優(yōu)點正好滿足了實際需求的事情,其實不勝枚舉。比如說 在和等宿主語言上也都表現(xiàn)的淋漓盡致。像這樣的小型語言,本來就是讓運行時為了嵌入其他語言的,所以運行時越小越好,自然的,就走了和嵌入式系統(tǒng)一樣的設計道路。
7. 結語
其實第二種設計也不是鐵板一塊,里面也有很多流派,各派有很多優(yōu)缺點,也有很多細致的考量,下一節(jié),如果不出意外,我將介紹我最喜歡的一個內(nèi)容: 下一代虛擬機:寄存器還是棧。
說了這么多,最后就是一句話,有時候我們看上去覺得一種設計好像是天外飛仙,橫空出世,其實其后都有現(xiàn)實,需求等等的諸多考量。虛擬機技術就是這樣,在各種需求的引導下,逐漸的演化成了現(xiàn)在的樣子。
本文關鍵詞:高級語言,由筆耕文化傳播整理發(fā)布。
本文編號:375497
本文鏈接:http://www.sikaile.net/wenshubaike/dxkc/375497.html