今天由于時間不多,難有旁征博引的長文來跟大家解釋清楚計算機、存儲,及其對編程的影響。不過,有一點自己微小的見解與大家分享。雖然微小,但確實給自己減輕了很多在技術方面的理解成本和成長負擔。個人覺得也應是有些分享價值的。 1. 計算機組成現代電子計算機從二十世紀四十年代末發展至今,以EDVAC計算機為標志,到目前為止67年間計算機硬件體系的核心都未曾發生過變化,都是馮諾依曼體系的。注意這里說的是EDVAC而非第一臺通用計算機ENIAC。EDVAC于1949年交付,1951年正式運行,1961年完成使命退出歷史舞臺。 1.1 題外話:編程語言與軟件系統的發展和自舉本小節的目的是為了解決大家常有的疑惑:現代編程語言和軟件都是逐步迭代的方式演進,那最早最早的編程語言和操作系統是怎么來的? 越是追溯事物的起源,越是能發現其本質。 先提及一下EDVAC服役的這幾年(1949-1961)間出現過的,至今影響深遠的編程語言。在這12年間,與硬件平臺強相關的特定匯編于1949年最早出現,1951年各種匯編方言開始興起。Fortran于1954年開始開發,1957年正式由IBM發布。約翰·麥卡錫于1958年基于IPL語言發明了Lisp函數式編程語言,主要用于人工智能。同年, ALGOL也誕生了。1959年COBOL也問世。Fortran、Lisp、ALGOL,就是后來幾乎所有高級編程語言的鼻祖,它們至今自己也發揮著巨大的作用。 最早的都是用機器語言通過穿孔卡片的形式搞出來的,像Fortran早期都是穿孔卡片進行編程。后來的新語言和新系統,都是基于前輩們自舉而來。有了編程語言之后,程序,包括操作系統程序是如何誕生出來的就可想而知。 編程語言和軟件自舉的過程。純手工用穿孔紙的形式用機器語言編寫了早期的具有特定功能的程序,比如存粹為了計算導彈等軍事用途的程序。后來為了編程方便,各類匯編方言誕生,所以同樣純手工機器碼編程寫個匯編翻譯器,能把匯編源碼翻譯成機器語言,故而就能用匯編語言來寫各種各樣的特定功能的程序了。因為匯編語言翻譯之后得到的最終程序是機器可直接執行的機器碼,所以用匯編1.0寫個匯編2.0翻譯器也沒問題的,匯編2.0成熟后,就能完全拋棄匯編匯編1.0了。同樣的,直接用機器碼或者匯編語言,寫個A語言的編譯器也沒問題,A語言編譯器穩定之后,也可以用A語言來寫A語言自己的編譯器,用匯編寫的A語言編譯器就可以拋棄了。 如雷貫耳的C語言,就是按上述過程基于CPL和BCPL語言發展而來的,后來很多語言用C寫第一版編譯器也不足為奇了,比如Python官方實現就一直用的是C,而Pypy是用Python自舉的。 1.2 馮諾依曼體系精要首先要明確一點,馮諾依曼體系因其設計EDVAC時而系統提出來的,但是其中的核心思想和觀念,絕不是馮諾依曼一人靈光乍現的個人杰作。有很多前輩們的努力,馮諾依曼是那個有機會總結并在大型項目中實踐的幸運兒。 1.2.1 兩個核心原則:- 計算機程序和待處理的數據無差別存儲在存儲器中
這里隱含了兩點,運行時存在哪里?掉電后存在哪里?也就是主存和外存,馮諾依曼的報告中是有專門提及主存和外存的。更進一步說,程序亦是數據的一種。 - 使用二進制,而非十進制
ENIAC使用的就是十進制。
1.2.2 五個組成部分1.2.3 更簡化地理解在本人理解來看,計算機拋開外圍設備后,最核心的東西就是運算器和存儲器。 簡言之,運算與存儲。控制器是可以單獨列出來的,但是由于控制器本質就是根據不同的判定條件選擇不同的處理過程,和一般的運算沒有根本上的區別,我把它粗略地歸為運算類。這樣歸納也是有理由的,因為在早期的機電計算機中,有運算器和存儲器就完全可以工作了(后來有了晶體管的計算機稱為電子計算機)。 這樣的簡化的作用是為了減輕對計算機體系的理解成本,如何個減輕法,請繼續看后文。 1.3 計算機組成總結關鍵字:程序亦數據、二進制、運算器、存儲器。 其中進制不必多談,以后會發文專門談談為何非要是二進制在這種體系結構下最恰當,可不僅僅是高電平、低電平那么表面的解釋。程序亦是數據,這原本是指馮諾依曼體系中程序和數據無差別對待,一起存放在存儲器里就行。但我們可以從這條延伸出很多編程上的思想,程序就是指令集,指令等同于數據對待時,問題就會簡化很多,也會靈活很多。 函數式編程語言說函數是一等公民,而一個函數本就是一段程序,當有了“程序亦數據”的思想后,拿函數作為入參傳來傳去不是很自然地事嗎?不知道當初Lisp的誕生,有沒有受到這一點的啟發,也許Lisp純粹是按數學上對待函數的態度來的。不過應了我常說那句話:世界是圓的。 同理,面向對象編程的語言中,一個對象本就是一組數據和其操作的封裝,把對象當做參數傳來傳去也在情理之中。命令式語言把命令當做數據操作,這本就是它們的工作方式,由計算機硬件組成結構而來的工作方式。 2. 存儲曾經在”細學Python”QQ群里問群友,大家覺得存儲的本質是為了什么?回答五花八門,有的說是為了賺錢讓公司不倒閉,那為什么、憑什么能賺錢了?有的說是為了再利用,再利用來干什么?有的說是為了備份。有的說是為了能給程序處理,各種各種。這些回答都是有道理的,但是從一種“事物原本”的角度來講,不是很到位。 不論是外存、內存、數據庫軟件、瀏覽器端的Cookie、服務端的Session、磁盤上的文件等等,乃至是超越計算機體系之外,人腦中的記憶,各種各樣,五花八門的存儲形式,都有一個共同點:為了狀態的延續。不論是從運行原理上、業務邏輯上、商業目的上等各種層次各種維度來觀察,這個結論都能得到解釋。 瀏覽器端的Cookie和服務端的Session不必多說,為了使用戶的登錄狀態得以延續;程序運行時在內存中的數據存儲是為了程序的運行時狀態得以延續;電腦關機以后要將數據數據回寫磁盤,是為了下次開機后能夠延續上一次關機前的開機狀態;數據庫中存儲的數據,是為了讓業務系統的運行時狀態、業務邏輯狀態不斷延續;人腦能夠根據存儲的記憶觸景生情,是為了在若干時間后延續之前的那種情緒狀態,比如運動員若干年后看著自己曾經拿到的金牌能夠置身于當初得獎的喜悅感受中。還有很多例子拿來解釋。 3. 硬件體系和存儲的本對編程的意義在上一篇《編程到底是個什么玩意》中已經說了一條很重要的原則:事物是分層次的,軟件也是。層次思維明晰的話,就容易思考清楚很多軟件中的架構和設計。 3.1 運算器與存儲器對編程的意義編程語言各種各樣,之前我們已經談過了程序的共同本質,那么硬件體系對編程有什么影響呢?首先是硬件的可能性直接決定了軟件的可能性。其次,既然硬件體系可歸納為兩大類:運算器與存儲器。那么軟件體系也逃不脫這兩大類,做運算的與做存儲的。 運算與存儲,就好比是兩個基本元素,它們在硬件體系中存在,也在軟件體系中存在。它們還能夠按層次組織,大層次的元素可以囊括小層次的元素。舉個例子:MySQL數據庫從宏觀看是一個存儲程序,而要讓MySQL這個級別的程序正確工作,只有一個層次是不可能的,它必須包含運算類的模塊(按上文所述,控制類的也屬于此),也包含了存儲類的模塊,還有各種不同的層次逐級分下來,最終落實到MySQL的每一行源代碼,哪函數是是它所在的那個級別的存儲作用的,哪個函數是該級別的運算作用的;某個函數中,哪一行數和存取數據相關的,哪一行是與運算數據相關的……最終,指令而且還會落實到硬件上的運算器與存儲器上。 由于存儲到底還是存儲的是數據,故而明白了存儲的地位以后,就知道了計算機程序所操作的存儲器與存儲器中的數據的重要性。程序要操作的存儲器是內存,而數據在編程語言中的表現形式就是該語言支持的各種數據結構,包括基本數據結構和擴展數據結構,基本數據結構是就是整數、浮點數等,擴展數據結構包括結構體、對象等等。故而學習一門編程新語言,你首要關注的是如何進行內存的分配與使用,以及數據結構的操作。 還有一個很明確的編程指導思路:從程序功能講,你所要完成的項目應該歸為運算類還是存儲類?當要做模塊拆分時,這兩種分類又能指導你進行模塊劃分。為了更清晰,你可以按運算器、控制器、存儲器三種來分。再往下可以指導你的類、函數的劃分,再往下就能指導代碼的編寫,哪一行該操作哪個數據對象,它存在于哪里?要經過怎么樣的運算,要到哪里去?…… 當軟件需要優化時,有兩個大方向可以考慮,是運算需要優化,還是存儲需要優化?假如當運算需要優化時,哪個級別負責運算的程序需要優化?要優化這個級別的運算,那其子級別中的運算和存儲兩大類程序又是哪類更應該優化?當程序功能不足需要添加功能時,要添加充當運算角色的功能呢?還是要添加充當控制角色的?抑或是要充當存儲功能的?…… 有的同學可能會想,像WEB界面的內容,應該歸為哪類?宏觀看,WEB界面只是服務端輸出的結果,通過網絡I/O這種方式暫時存放到了客戶端,瀏覽器把這些數據處理渲染給了終端用戶。網絡I/O屬于我理解的計算機外圍設備,當然,非要劃分個歸類,那應當歸為存儲類。 總而言之,運算與存儲這兩個基本元素,構建起了馮諾依曼體系下計算機軟硬件的高樓大廈。 3.2 狀態延續性對編程的意義無需多言,將會指導我們定義程序中的數據種類、數據結構,優化數據的的核心指南。你希望程序這一刻是個什么狀態?經過運算后,下一刻又是一個什么狀態?要不要延續?那些基本數據齊活了以后才能使這個狀態得以延續?如果1個字節的數據就能讓狀態延續下去,用1KB數據來解決問題是不是有些浪費? 在有些程序里,磁盤IO速度不足,滿足不了CPU的處理速度,那怎么辦?那是CPU里的計算狀態延續不下去了,出現等待磁盤IO的斷檔,很明顯的優化思路就是增加一種程序來解決這個事情。增加運算類的程序能解決這個問題嗎?看樣子不太行,應為CPU作為運算器的存在它已經有強大優勢了。那就加存儲類程序唄,用內存來做存儲嘛,好像問題可以得解了。 如果隨著程序發展,又滿足不了了怎么辦?換性能更好的存儲器,一種存儲器解決不了的問題就兩種存儲器解決,兩種存儲器解決不了的就三種來解決…… 君不見,這就是馮諾依曼體系下的硬件發展與軟件優化趨勢,硬件中的多級緩存,從CPU到內存到磁盤,軟件中的多級緩存從應用程序到數據庫程序到操作系統程序,無處不在…… 以后再發文說說多級緩存的事。 另外,存儲是為了狀態延續這條指導規則,還可以發揮更多的作用,比如在數據災難恢復、程序不間斷服務而切換存儲介質或者切換數據庫上。大家可以發揮一下,不再多寫。 4. 總結本文也許有些同學看起來就是“假大空”,原因之一是確實篇幅有限,不能再多啰嗦;其二是偏理論經驗性的闡述,而缺少實例;其三是這部分同學可能編程經驗還不足。
|