電腦系統由硬體系統和軟體系統組成,它們共同協作來完成執行程式的任務。作為20世紀(世界上第一台電腦ENIAC與1946年誕生於美國的賓夕法尼亞大學)最偉大的發明之一,電腦系統可以說是人類迄今為止創造的最複雜最精密的工具。今天,藉助電腦界的聖經教材《深入理解電腦系統》,我來帶領大家探索一下電腦系統的秘密。
1 資訊就是位 + 上下文
電腦是資訊學科的產物,其主要左右就是協助人們處理各種各樣的資訊。而資訊本身是一個比較抽象的概念,從百度百科給出的定義中我們可以知道,資訊是用來傳輸和處理的。而且資訊一定是有一個載體,也就是有一個表現形式。那麼再電腦中,資訊是如何儲存並傳輸的呢。這就要說到我們這一節的主題了:資訊就是位 + 上下文。
電腦中所有資訊都是以二進位位的形式儲存的,理解這一點可以協助你搞明白很多問題。比如很多新手搞不懂的文字檔和二進位檔案去區別。其實,無論什麼檔案,在電腦中全部都是...01101100...這樣的二進位碼。所以所有檔案都可以說是二進位檔案,而文字檔不過是一種特殊的二進位檔案罷了。特殊在哪兒呢。就特殊在電腦對其的“翻譯”上。我們人類是不太能處理...01101100...這種東西的,所以電腦要將這些以二進位表示的資訊翻譯成我們人類所熟悉的文字、圖片、聲音等這些多媒體資訊。當然,不同的多媒體資訊與二級制之間的翻譯規則是不同的,其中二進位與文字之間的一種翻譯規則就稱為ASCII編碼。通過ASCII編碼可以實現8位二進位位和各個字元之間的一一對應。而將文字資訊利用ASCII編碼轉換成二進位在電腦系統中儲存起來的那個檔案就叫做文字檔。鑒於此,我們就可以將二進位檔案分為文字檔、圖片檔案、音效檔等等。總之,在電腦中,我們熟悉的各種資訊被編碼成二進位的形式儲存起來並進行傳輸。
就像上文提到的,不同類型的資訊和二進位之間的轉換規則是不同的。你不能把你喜歡的歌曲用圖片的形式開啟,也不能把你的照片轉化成文字(當然強制轉換是可以的,但結果肯定不是你想要的,回想一下,有沒有經曆過滿屏亂碼的經曆)。也就是說,二進位資訊也是有類型的,這些類型就表示在資訊所在那個檔案裡,也就是那片二進位碼中。這樣就形成了前後相互關聯的關係,一個個二進位位在特定的環境中被賦予了不同的含義。同樣,我們要理解或者說翻譯它們,肯定也要根據其前後的環境來理解,這就是我們所說的上下文。
資訊被編碼成一片相互關聯的二進位代碼儲存在電腦系統中,它們以二進位的形式儲存、傳遞、處理。只有在與人類互動時,比如顯示在螢幕上或者列印輸出時,才會被翻譯成人們熟悉的形式展現出來。
2. 程式被其他程式翻譯成不同的格式
前面我們說了電腦的主要作用是處理資訊,也就是處理...01101100...這種二進位碼。那顯然就有一個問題,電腦作為一個沒有生命沒有智力的東西,它是怎麼知道該怎麼處理資訊的呢。當然,那就是我們的軟體或者說是程式了。軟體系統只所以成為電腦系統不可缺少的部分,就是因為資訊的處理過程必須由軟體來完成,而一個不能處理資訊的電腦系統是沒有任何存在意義的。於是,人們發明了軟體,賦予了電腦系統生命。
如何告訴電腦改怎麼處理資訊呢。既然電腦內只能儲存二進位碼,於是人們自然想到了用二進位碼去控制電腦系統,沒錯,這就是機器語言的誕生。二進位碼也變成了二進位代碼。但前面我們也提到了,人類是不適應處理...01101100...這種東西的。所以在當時,人們和電腦的“交流”成本是非常大的,而且效率低下。人們為了克服這一困難,發明了另一種交流手段,即先用機器碼寫一個翻譯軟體,然後我們用一些助記符來表達想要執行的操作,再用前面那個翻譯軟體翻譯成二進位機器碼,最後讓電腦系統去執行這些代碼。藉助這種“師夷長技以制夷”的策略,軟體界向前邁出了一大步,而人們將這些助記符組成的語言稱為組合語言。接下來的事情就順理成章了,人們又發現組合語言還是太難用(人們總是感覺現有的東西難用,這也是人類文明向前發展的重要動力),於是便又發明了C語言,C語言更貼近自然語言,編碼風格也更加靈活,深的使用者喜愛。不過人類是不會滿足的,後來又發明了C++、Java等各種語言。這些語言更加貼近現實世界,而遠離底層那些煩人的二進位代碼。
回到這一小節的標題,既然現在程式員寫程式用的是所謂的進階語言,那搞明白電腦是如何把我們寫的代碼翻譯成二級制代碼的就很有必要了。這裡我們以編譯型語言為例,來說明一下這其中經曆的幾個步驟:首先是我們寫的文字檔(為什麼是文字檔。因為我們對文字更熟悉),這些檔案被前置處理器“物理”的處理了一下,產生一個中間產物(還是文字檔),然後是我們的重頭戲,編譯器把文字檔“智能”地翻譯成彙編檔案,接著是彙編器,它把彙編檔案翻譯成機器熟悉的二進位檔案,最後是連接器,將多個檔案連結起來,讓電腦系統執行。
這裡要著重說一下的是編譯器的作用,前面的前置處理器和後面的彙編器以及連結器做的都是固定的一對一的翻譯工作,只有編譯器是“智能”的,這種智能體現在最佳化能力上。首先要說明一下,在特定的硬體平台上,彙編代碼和機器碼是一一對應的,從彙編代碼可以完全得到電腦系統的運行過程,所以程式調試的最底層是組合語言而不是機器碼(當然如果你喜歡看機器碼也是可以的)。於此想對應的是編譯器翻譯的兩端:原始碼和彙編代碼,並不是一一對應的,同樣的原始碼在不同的情況下(跟編譯器的類型和最佳化層級有關)會被編譯器翻譯成不同的組合語言。所以編譯器是一個非常複雜且重要的軟體系統,程式員的一生都在和它打交道。
這一小節講的是我們平時使用的程式設計語言是為何以及如何在電腦中被轉換的。編譯系統為我們的代碼和機器控制之間搭建了橋樑。
3. 瞭解編譯系統如何工作是大有益處的
編譯系統是一個智能的翻譯系統,但其智能程度也很有限,它要求程式員必須按照既定的格式安排組織代碼,否則,它可能會“誤解”我們的意思。這些既定的規則就是某種語言的文法,我們必須嚴格按照文法來編寫程式,否則編譯器會報錯說無法完成翻譯。但這僅僅是個開始,寫程式去控制電腦是一個複雜的過程,在這個過程中會出現各種各樣的錯誤或者瑕疵影響我們的控制效果。要想更好的完成工作,我們有必要去瞭解編譯器是如果工作的。
瞭解編譯器的工作原理,至少是瞭解關鍵的幾點,對程式員尤其是C程式員是非常有必要的,它可以讓你和編譯器合作的更加融洽。具體來說,有這樣幾個好處:最佳化程式效能,你可以從語言層面上提高程式的運行效率;理解連結時錯誤,相信大部分C/C++程式員都經曆過無法解析的外部符號吧,等你理解了編譯器的連結過程,你就能明白這到底是什麼意思了;避免安全性漏洞,緩衝區溢位是一個臭名昭著的程式bug,函數調用棧的知識有助於你理解並避免這種錯誤。
學習編譯器的工作原理並不像學習其他技術一樣,會對編程水平有很快的提高。這些知識更像武俠小說中的內功心法,是程式員進一步提高的基礎。
4. 處理器讀並解釋儲存在儲存空間中的命令
我們編寫的原始碼經過編譯器的各種處理,最終轉變成了二進位代碼儲存到了計算中記憶體中。記憶體可以看做是一個資料倉儲,裡面以二進位的形式儲存著大量的資料。其中就有我們的程式碼,這些代碼通過一個叫處理器的物理組件來執行,從而控制整個電腦系統的運行。
電腦系統整個運行過程就可以看做是處理器從記憶體讀指令(代碼的一個新名字),執行指令的過程。其效果就是處理器根據指令控制外圍組件完成各種任務。這裡就要講到馮諾依曼的電腦體繫結構了:儲存空間,就是我們的記憶體了,所有資料都放在這裡;控制器,這是處理器的一部分,它利用時鐘訊號產生的“脈搏”來控制其他部分的運行;運算器,這也是處理器的一部分,用來完成簡單的算術/邏輯運算,這些簡單的運算構成了系統處理資料的基礎;輸入輸出裝置,這些東西是用來完成人機互動的,比如滑鼠、鍵盤、顯示器等等。
在整個系統運行過程中,資料被反覆從一個位置移動到另一個位置。可以認為系統的運行過程就是搬運資料的過程。所以資料的傳輸效率是決定系統整體效率的關鍵因素,為了提高資料轉送效率,電腦系統內採用了很多技術,瞭解這些技術併合理利用可以很大程度上提高程式效能。下面就來介紹一些這方面的知識。
5. 快取至關重要
對於特定的儲存空間件來說,其存取速度和容量是成反相關的。也就是說,其儲存量越大,其存取速度越慢。在電腦系統中儲存速度最快的儲存空間件是寄存器,而其個數只有幾個或者十幾個。而儲存量最大的硬碟,存取速度是最慢的。一個機器的硬碟容量可能比其記憶體容量大1000倍,但其存取速度可能要慢1000萬倍,為了調節如此大的速度差距,電腦系統採用多級緩衝的方法。
緩衝的作用其實很簡單,它只是把處理器可能要用到的資料提前從容量比較大的讀取速度比較慢的儲存空間件中讀出,以備處理器使用。為了調節效果,現在的新型系統中都採用三級緩衝技術,即從記憶體到寄存器之間插入三層儲存空間件,它們容量越來越小,速度越來越快。
6. 存放裝置形成階層
正如上面提到的,電腦系統中各種儲存空間件根據容量或者存取速度有一個排序,這樣就形成了一個階層。最上面的是寄存器,下面是各級快取,然後是記憶體,再下面是硬碟,最後可能還有網路資源。
儲存空間之所以是分結構,就是因為物理特性決定了其儲存量和讀取速度一定是成反比的。為了使快的裝置和慢的裝置更好的交換資料,就需要在中間插入一個中介層。相比於具體的實現細節,這種設計思想是更值得我們學習的。
7. 作業系統管理硬體
我們寫的程式主要是邏輯的表達,但真正在電腦系統中運行,卻要時時刻刻和具體的硬體打交道,我們不大可能每次寫程式都花費心思就管理儲存空間的每個位元組,也不應該思考螢幕每個像素點該怎麼控制。為了使硬體系統對普通程式員透明化,電腦系統中採用作業系統來統一管理硬體,我們的應用程式只需要和作業系統交流就可以了。
有了作業系統的協助,我們就可以把各種硬體裝置抽象成一個統一的模型來管理使用了,而不用去理會具體的實現細節。具體來說,I/O裝置被抽象成檔案,記憶體和I/O裝置被抽象成虛擬記憶體,處理器、記憶體和I/O裝置一起被抽象成進程。讓我們從大到小,分別做一個簡單介紹。
進程,當一個程式被執行時,我們可以認為這個程式獨佔整個電腦系統,這種一個程式獨自佔用CPU、記憶體、I/O裝置的“假象”是通過進程的概念實現的。作業系統控制整個電腦的過程就可以看做是管理進程表的過程,作業系統按照某種規則再不同的進程中間來回切換,從而完成並行的任務。我們執行一個程式,在電腦系統中就多了一個進程。
虛擬儲存空間,是對記憶體地區和I/O裝置的抽象。程式員平時說的全域變數區、堆區、棧區等等都是虛擬記憶體中抽象的概念,而真實的記憶體是不分這些東西的,它們的硬體基礎是完全相同的。之所以做這種抽象,就是為了程式員使用記憶體方便,而將程式員看到的記憶體和硬體記憶體相對應是一個相當複雜的過程。
檔案,檔案對於每個使用過電腦的人來說都不陌生,其實檔案的本質就是一些位元組序列。所有可讀寫的硬體裝置都被抽象成檔案,程式員只需要去讀檔案或者寫檔案就可以了,而不用關係具體硬體是怎麼完成讀寫的。
對硬體的模型化的目的就是為了方便人們使用它們。正是有了種種抽象概念,程式員才得以從繁瑣的硬體細節中解脫出來。這種解決問題的方法在整個電腦乃至資訊領域都非常常見,是人類聰明才智發揮的完美體現。
8. 系統之間利用網路通訊
電腦系統之所以如此強大,是因為它不僅自身可以處理資料,而且還可以進行跨越空間進行通訊。電腦網路是比電腦系統本身跟重要的產物,正是有了這個網路,人類社會才發生了翻天覆地的變化。
雖然網路的概念非常重要,但這裡並不打算介紹太多網路知識,對於一個電腦系統來說,網路上就相當於一個檔案,我們可以從中讀資料也可以往裡面寫資料,瞭解這一點對於程式員來說就可以了。
9. 重要主題
並發和並行是當前電腦系統速度提高的關鍵概念,他們都涉及同時處理多個事務的能力。就像人不能一心二用一樣,電腦系統的並發也比較困難,幾十年的研究現狀才剛剛進入實踐。這應該是以後電腦方面的研究熱門之一。
抽象是人類思維的進階體現,在電腦系統中,人們用了很多抽象來協助我們完成任務。而在其他領域,抽象的意義也非常重要,這在程式設計語言中也有很多體現,比如介面的概念,就是對一組功能的抽象。
10. 小結
電腦系統是被用來處理資訊的,這些資訊以二進位的形式儲存在系統之中。為了實現電腦系統的控制,我們必須寫程式碼完成。編譯器是解釋翻譯我們代碼的工具,理解翻譯官的工作原理有助於我們寫出更加安全高效的代碼。處理器是執行代碼的器件,在處理資料的過程中,資料轉送在系統中非常常見,為了提高傳輸效率,採用快速緩衝的思想來調節不同硬體的存取速度差別。抽象是非常重要的概念,無論是電腦系統中還是在其他領域,學習抽象思維比學習具體的技術更加重要。