為解決各種問(wèn)題,人們發(fā)明了不計(jì)其數(shù)的機(jī)器。嵌入式設(shè)備種類(lèi)繁多,從嵌入火星漫游機(jī)器人的計(jì)算機(jī)到為操縱核潛艇導(dǎo)航系統(tǒng)的系統(tǒng),不一而足。馮? 諾伊曼在1945 年提出第一種計(jì)算模型,無(wú)論筆記本電腦還是電話(huà),幾乎所有計(jì)算機(jī)都遵循與這種模型相同的工作原理。那么你們了解計(jì)算機(jī)是如何工作的嗎?
本文將討論這些內(nèi)容:◎ 理解計(jì)算機(jī)體系結(jié)構(gòu)的基礎(chǔ)知識(shí)◎ 選擇編譯器將代碼轉(zhuǎn)換為計(jì)算機(jī)可以執(zhí)行的指令◎ 根據(jù)存儲(chǔ)器層次結(jié)構(gòu)提高數(shù)據(jù)的存儲(chǔ)速度畢竟,在非程序員看來(lái),編程要像魔法一樣神奇,我們程序員不會(huì)這么看。
體系結(jié)構(gòu)
計(jì)算機(jī)是一種根據(jù)指令操作數(shù)據(jù)的機(jī)器,主要由處理器與存儲(chǔ)器兩部分組成。存儲(chǔ)器又稱(chēng)RAM(隨機(jī)存取存儲(chǔ)器),用于存儲(chǔ)指令以及需要操作的數(shù)據(jù)。處理器又稱(chēng)CPU(中央處理器),它從存儲(chǔ)器獲取指令與數(shù)據(jù),并執(zhí)行相應(yīng)的計(jì)算。接下來(lái),我們將討論這兩部分的工作原理。
存儲(chǔ)器
存儲(chǔ)器被劃分為許多單元,每個(gè)單元存儲(chǔ)少量數(shù)據(jù),通過(guò)一個(gè)數(shù)字地址加以標(biāo)識(shí)。在存儲(chǔ)器中讀取或?qū)懭霐?shù)據(jù)時(shí),每次對(duì)一個(gè)單元進(jìn)行操作。為讀寫(xiě)特定的存儲(chǔ)單元,必須找到該單元的數(shù)字地址。由于存儲(chǔ)器是一種電氣元件,單元地址作為二進(jìn)制數(shù)通過(guò)信號(hào)線(xiàn)傳輸。
二進(jìn)制數(shù)以 2 為基數(shù)表示,其工作原理如下:
每條信號(hào)線(xiàn)傳輸一個(gè)比特,以高電壓表示信號(hào)“1”,低電壓表示信號(hào)“0”,如圖7-1 所示。
對(duì)于某個(gè)給定的單元地址,存儲(chǔ)器可以進(jìn)行兩種操作:獲取其值或存儲(chǔ)新值,如圖7-2 所示。存儲(chǔ)器包括一條用于設(shè)置操作模式的特殊信號(hào)線(xiàn)。
每個(gè)存儲(chǔ)單元通常存儲(chǔ)一個(gè) 8 位二進(jìn)制數(shù),它稱(chēng)為字節(jié)。設(shè)置為“讀”模式時(shí),存儲(chǔ)器檢索保存在單元中的字節(jié),并通過(guò)8 條數(shù)據(jù)傳輸線(xiàn)輸出,如圖7-3 所示。
設(shè)置為“寫(xiě)”模式時(shí),存儲(chǔ)器從數(shù)據(jù)傳輸線(xiàn)獲取一個(gè)字節(jié),并將其寫(xiě)入相應(yīng)的單元,如圖7-4 所示。
傳輸相同數(shù)據(jù)的一組信號(hào)線(xiàn)稱(chēng)為總線(xiàn)。用于傳輸?shù)刂返? 條信號(hào)線(xiàn)構(gòu)成地址總線(xiàn),用于在存儲(chǔ)單元之間傳輸數(shù)據(jù)的另外8 條信號(hào)線(xiàn)構(gòu)成數(shù)據(jù)總線(xiàn)。地址總線(xiàn)是單向的(僅用于接收數(shù)據(jù)),而數(shù)據(jù)總線(xiàn)是雙向的(用于發(fā)送和接收數(shù)據(jù))。在所有計(jì)算機(jī)中,CPU 與RAM 無(wú)時(shí)無(wú)刻不在交換數(shù)據(jù):CPU 不斷從RAM 獲取指令與數(shù)據(jù),偶爾也會(huì)將輸出與部分計(jì)算存儲(chǔ)在RAM 中,如圖7-5 所示。
CPU
CPU 包括若干稱(chēng)為寄存器的內(nèi)部存儲(chǔ)單元,它能對(duì)存儲(chǔ)在這些寄存器中的數(shù)字執(zhí)行簡(jiǎn)單的數(shù)學(xué)運(yùn)算,也能在RAM 與寄存器之間傳輸數(shù)據(jù)。可以指示CPU 執(zhí)行以下典型的操作:◎ 將數(shù)據(jù)從存儲(chǔ)位置 220 復(fù)制到寄存器 3;◎ 將寄存器 3 與寄存器 1 中的數(shù)字相加。CPU 可以執(zhí)行的所有操作的集合稱(chēng)為指令集,指令集中的每項(xiàng)操作被分配一個(gè)數(shù)字。計(jì)算機(jī)代碼本質(zhì)上是表示CPU 操作的數(shù)字序列,這些操作以數(shù)字的形式存儲(chǔ)在RAM 中。輸入/ 輸出數(shù)據(jù)、部分計(jì)算以及計(jì)算機(jī)代碼都存儲(chǔ)在RAM 中。
通過(guò)在RAM 中包含重寫(xiě)部分代碼的指令,代碼甚至可以對(duì)自身修改,這是計(jì)算機(jī)病毒逃避反病毒軟件檢測(cè)的慣用手法。與之類(lèi)似,生物病毒通過(guò)改變自身的DNA以躲避宿主免疫系統(tǒng)的打擊。
圖7-6 取自Intel 4004 操作手冊(cè),顯示了部分CPU 指令映射為數(shù)字的方法。隨著制造工藝的發(fā)展,CPU 支持的操作越來(lái)越多。現(xiàn)代CPU 的指令集極為龐大,但最重要的指令在幾十年前就已存在。
CPU 的運(yùn)行永無(wú)休止,它不斷從存儲(chǔ)器獲取并執(zhí)行指令。這個(gè)周期的核心是PC 寄存器,PC (program counter)是“程序計(jì)數(shù)器”的簡(jiǎn)稱(chēng)。PC 是一種特殊的寄存器,用于保存下一條待執(zhí)行指令的存儲(chǔ)地址。CPU 的工作流程如下:(1) 從PC 指定的存儲(chǔ)地址獲取指令;(2) PC 自增;(3) 執(zhí)行指令;(4) 返回步驟1。PC 在CPU 上電時(shí)復(fù)位為默認(rèn)值,它是計(jì)算機(jī)中第一條待執(zhí)行指令的地址。這條指令通常是一種不可變的內(nèi)置程序,用于加載計(jì)算機(jī)的基本功能。
在許多個(gè)人計(jì)算機(jī)中,這種程序稱(chēng)為BIOS(基本輸入輸出系統(tǒng))。
CPU 上電后將繼續(xù)執(zhí)行這種“獲取- 執(zhí)行”周期直至關(guān)機(jī)。然而,如果CPU 只能遵循有序、順序的操作列表,那么它與一個(gè)花哨的計(jì)算器并無(wú)二致。CPU 的神奇之處在于可以指示它向PC 中寫(xiě)入新值,從而實(shí)現(xiàn)執(zhí)行過(guò)程的分支,或“跳轉(zhuǎn)”到存儲(chǔ)器的其他位置。這種分支可以是有條件的。以下面這條CPU 指令為例:“如果寄存器1 等于0,將PC設(shè)置為地址200”。該指令相當(dāng)于:
if x = 0 compute_this() else compute_that()僅此而已。無(wú)論是打開(kāi)網(wǎng)站、玩計(jì)算機(jī)游戲抑或編輯電子表格,所涉及的計(jì)算并無(wú)區(qū)別,都是一系列只能對(duì)存儲(chǔ)器中的數(shù)據(jù)求和、比較或移動(dòng)的簡(jiǎn)單操作。大量簡(jiǎn)單的操作組合在一起,就能表達(dá)復(fù)雜的過(guò)程。以經(jīng)典的《太空侵略者》游戲?yàn)槔浯a包括大約3000 條機(jī)器指令。CPU 時(shí)鐘 早在20 世紀(jì)80 年代,《太空侵略者》就已風(fēng)靡一時(shí)。這個(gè)游戲在配備2 MHz CPU 的街機(jī)上運(yùn)行。“2 MHz”表示CPU 的時(shí)鐘,即CPU 每秒可以執(zhí)行的基本操作數(shù)。時(shí)鐘頻率為200 萬(wàn)赫茲(2 MHz)的CPU 每秒大約可以執(zhí)行200 萬(wàn)次基本操作。完成一條機(jī)器指令需要5到10 次基本操作,因此老式街機(jī)每秒能運(yùn)行數(shù)十萬(wàn)條機(jī)器指令。隨著現(xiàn)代科技的進(jìn)步,普通的臺(tái)式計(jì)算機(jī)與智能手機(jī)通常配備2 GHzCPU,每秒可以執(zhí)行數(shù)億條機(jī)器指令。時(shí)至今日,多核CPU 已投入大規(guī)模應(yīng)用,如四核2 GHz CPU 每秒能執(zhí)行近10 億條機(jī)器指令。展望未來(lái),CPU 配備的核心數(shù)量或許會(huì)越來(lái)越多。CPU 體系結(jié)構(gòu) 讀者是否思考過(guò),PlayStation 的游戲CD 為何無(wú)法在臺(tái)式計(jì)算機(jī)中運(yùn)行?iPhone 應(yīng)用為何無(wú)法在Mac 中運(yùn)行?原因很簡(jiǎn)單,因?yàn)樗鼈兊腃PU 體系結(jié)構(gòu)不同。x86 體系結(jié)構(gòu)如今已成為行業(yè)標(biāo)準(zhǔn),因此相同的代碼可以在大部分個(gè)人計(jì)算機(jī)中執(zhí)行。但考慮到節(jié)電的要求,手機(jī)采用的CPU 體系結(jié)構(gòu)有所不同。不同的CPU 體系結(jié)構(gòu)意味著不同的CPU 指令集,也意味著將指令編碼為數(shù)字的方式各不相同。臺(tái)式計(jì)算機(jī)CPU 的指令并非手機(jī)CPU的有效指令,反之亦然。32 位與64 位體系結(jié)構(gòu) 第一種CPU 是Intel 4004,它采用4 位體系架構(gòu)。換言之,這種CPU 在一條機(jī)器指令中可以對(duì)最多4 位二進(jìn)制數(shù)執(zhí)行求和、比較與移動(dòng)操作。Intel 4004 的數(shù)據(jù)總線(xiàn)與地址總線(xiàn)均只有4 條。不久之后,8 位CPU 開(kāi)始廣為流行,這種CPU 用于運(yùn)行DOS 的早期個(gè)人計(jì)算機(jī)。20 世紀(jì)八九十年代,著名的便攜式游戲機(jī)Game Boy 就采用8 位處理器。這種CPU 可以在一條指令中對(duì)8 位二進(jìn)制數(shù)進(jìn)行操作。技術(shù)的快速發(fā)展使16 位以及之后的32 位體系結(jié)構(gòu)成為主導(dǎo)。CPU 寄存器隨之增大,以容納32 位數(shù)字。更大的寄存器自然催生出更大的數(shù)據(jù)總線(xiàn)與地址總線(xiàn):具有32 條信號(hào)線(xiàn)的地址總線(xiàn)可以對(duì)232 字節(jié)(4 GB)的內(nèi)存進(jìn)行尋址。人們對(duì)計(jì)算能力的渴求從未停止。計(jì)算機(jī)程序越來(lái)越復(fù)雜,消耗的內(nèi)存越來(lái)越多,4 GB 內(nèi)存已無(wú)法滿(mǎn)足需要。使用適合32 位寄存器的數(shù)字地址對(duì)超過(guò)4 GB 內(nèi)存進(jìn)行尋址頗為棘手,這成為64 位體系結(jié)構(gòu)興起的動(dòng)因,這種體系結(jié)構(gòu)如今占據(jù)主導(dǎo)地位。64 位CPU 可以在一條指令中對(duì)極大的數(shù)字進(jìn)行操作,而64 位寄存器將地址存儲(chǔ)在海量的存儲(chǔ)空間中:264 字節(jié)相當(dāng)于超過(guò)170 億吉字節(jié)(GB)。大端序與小端序 一些計(jì)算機(jī)設(shè)計(jì)師認(rèn)為,應(yīng)按從左至右的順序在RAM 與CPU 中存儲(chǔ)數(shù)字,這種模式稱(chēng)為小端序。另一些計(jì)算機(jī)設(shè)計(jì)師則傾向于按從右至左的順序在存儲(chǔ)器中寫(xiě)入數(shù)據(jù),這種模式稱(chēng)為大端序。因此,根據(jù)“字節(jié)序”的不同,二進(jìn)制序列1-0-0-0-0-0-1-1 表示的數(shù)字也有所不同。◎ 大端序:27 + 21 + 20 = 131◎ 小端序:20 + 26 + 27 = 193目前的大部分CPU 采用小端序模式,但同樣存在許多采用大端序模式的計(jì)算機(jī)。如果大端序CPU 需要解釋由小端序CPU 產(chǎn)生的數(shù)據(jù),則必須采取措施以免出現(xiàn)字節(jié)序不匹配。程序員直接對(duì)二進(jìn)制數(shù)進(jìn)行操作,在解析來(lái)自網(wǎng)絡(luò)交換機(jī)的數(shù)據(jù)時(shí)尤其需要注意這個(gè)問(wèn)題。雖然目前多數(shù)計(jì)算機(jī)采用小端序模式,但由于大部分早期的網(wǎng)絡(luò)路由器使用大端序CPU,所以因特網(wǎng)流量仍然以大端序?yàn)榛A(chǔ)進(jìn)行標(biāo)準(zhǔn)化。以小端序模式讀取大端序數(shù)據(jù)時(shí)將出現(xiàn)亂碼,反之亦然。模擬器 某些情況下,需要在計(jì)算機(jī)上運(yùn)行某些為不同CPU 設(shè)計(jì)的代碼,以便在沒(méi)有iPhone 的情況下測(cè)試iPhone 應(yīng)用,或玩膾炙人口的老式超級(jí)任天堂游戲。這是通過(guò)稱(chēng)為模擬器的軟件來(lái)實(shí)現(xiàn)的。模擬器用于模仿目標(biāo)機(jī)器,它假定與其擁有相同的CPU、RAM 以及其他硬件。模擬器程序?qū)χ噶钸M(jìn)行解碼,并在模擬機(jī)器中執(zhí)行。可以想見(jiàn),如果兩臺(tái)機(jī)器的體系結(jié)構(gòu)不同,那么在一臺(tái)機(jī)器內(nèi)部模擬另一臺(tái)機(jī)器絕非易事。好在現(xiàn)代計(jì)算機(jī)的速度遠(yuǎn)遠(yuǎn)超過(guò)之前的機(jī)器,因此模擬并非無(wú)法實(shí)現(xiàn)。我們可以利用Game Boy 模擬器在計(jì)算機(jī)中創(chuàng)建一個(gè)虛擬的Game Boy,然后就能像使用實(shí)際的Game Boy 那樣玩游戲。
編譯器
通過(guò)對(duì)計(jì)算機(jī)進(jìn)行編程,可以完成核磁共振成像、聲音識(shí)別、行星探索以及其他許多復(fù)雜的任務(wù)。值得注意的是,計(jì)算機(jī)執(zhí)行的所有操作最終都要通過(guò)簡(jiǎn)單的CPU 指令完成,即歸結(jié)為對(duì)數(shù)字的求和與比較。而Web 瀏覽器等復(fù)雜的計(jì)算機(jī)程序需要數(shù)百萬(wàn)乃至數(shù)十億條這樣的機(jī)器指令。但我們很少會(huì)直接使用CPU 指令來(lái)編寫(xiě)程序,也無(wú)法采用這種方式開(kāi)發(fā)一個(gè)逼真的三維計(jì)算機(jī)游戲。為了以一種更“自然”且更緊湊的方式表達(dá)命令,人們創(chuàng)造了編程語(yǔ)言。我們使用這些語(yǔ)言編寫(xiě)代碼,然后通過(guò)一種稱(chēng)為編譯器的程序?qū)⒚钷D(zhuǎn)換為CPU 可以執(zhí)行的機(jī)器指令。我們用一個(gè)簡(jiǎn)單的數(shù)學(xué)類(lèi)比來(lái)解釋編譯器的用途。假設(shè)我們向某人提問(wèn),要求他計(jì)算5 的階乘。5! = ?但如果回答者不了解什么是階乘,則這樣提問(wèn)并無(wú)意義。我們必須采用更簡(jiǎn)單的操作來(lái)重新表述問(wèn)題。5×4×3×2×1 = ?不過(guò),如果回答者只會(huì)做加法怎么辦?我們必須進(jìn)一步簡(jiǎn)化問(wèn)題的表述。5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 +5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 = ?可以看到,表達(dá)計(jì)算的形式越簡(jiǎn)單,所需的操作數(shù)量越多。計(jì)算機(jī)代碼同樣如此。編譯器將編程語(yǔ)言中的復(fù)雜指令轉(zhuǎn)換為等效的CPU 指令。結(jié)合功能強(qiáng)大的外部庫(kù),就能通過(guò)相對(duì)較少的幾行代碼表示包含數(shù)十億條CPU 指令的復(fù)雜程序,而這些代碼易于理解和修改。計(jì)算機(jī)之父艾倫? 圖靈發(fā)現(xiàn),簡(jiǎn)單的機(jī)器有能力計(jì)算任何可計(jì)算的事物。如果機(jī)器具有通用的計(jì)算能力,那么它必須能遵循包含指令的程序,以便:◎ 對(duì)存儲(chǔ)器中的數(shù)據(jù)進(jìn)行讀寫(xiě);◎ 執(zhí)行條件分支:如果存儲(chǔ)地址具有給定的值,則跳轉(zhuǎn)到程序的另一個(gè)點(diǎn)。我們稱(chēng)具有這種通用計(jì)算能力的機(jī)器是圖靈完備的。無(wú)論計(jì)算的復(fù)雜性或難度如何,都可以采用簡(jiǎn)單的讀取/ 寫(xiě)入/ 分支指令來(lái)表達(dá)。只要分配足夠的時(shí)間與存儲(chǔ)空間,這些指令就能計(jì)算任何事物。人們最近發(fā)現(xiàn),一種稱(chēng)為MOV(數(shù)據(jù)傳送)的CPU 指令是圖靈完備的。這意味著僅能執(zhí)行MOV 指令的CPU 與完整的CPU 在功能上并無(wú)不同:換言之,通過(guò)MOV 指令可以嚴(yán)格地表達(dá)任何類(lèi)型的代碼。這個(gè)重要概念在于,無(wú)論簡(jiǎn)單與否,如果程序能采用編程語(yǔ)言進(jìn)行編碼,就可以重寫(xiě)后在任何圖靈完備的機(jī)器中運(yùn)行。編譯器是一種神奇的程序,能自動(dòng)將代碼從復(fù)雜的語(yǔ)言轉(zhuǎn)換為簡(jiǎn)單的語(yǔ)言。
從本質(zhì)上講,編譯后的計(jì)算機(jī)程序是CPU 指令的序列。如前所述,為臺(tái)式計(jì)算機(jī)編譯的代碼無(wú)法在智能手機(jī)中運(yùn)行,因?yàn)槎卟捎貌煌腃PU體系結(jié)構(gòu)。不過(guò),由于程序必須與計(jì)算機(jī)的操作系統(tǒng)通信才能運(yùn)行,編譯后的程序也可能無(wú)法在共享相同CPU 架構(gòu)的兩臺(tái)計(jì)算機(jī)中使用。為實(shí)現(xiàn)與外界的通信,程序必須進(jìn)行輸入與輸出操作,如打開(kāi)文件、在屏幕上顯示消息、打開(kāi)網(wǎng)絡(luò)連接等。但不同的計(jì)算機(jī)采用不同的硬件,因此程序不可能直接支持所有不同類(lèi)型的屏幕、聲卡或網(wǎng)卡。這就是程序依賴(lài)于操作系統(tǒng)執(zhí)行的原因所在。借助操作系統(tǒng)的幫助,程序可以毫不費(fèi)力地使用不同的硬件。程序創(chuàng)建特殊的系統(tǒng)調(diào)用,請(qǐng)求操作系統(tǒng)執(zhí)行所需的輸入/ 輸出操作。編譯器負(fù)責(zé)將輸入/ 輸出命令轉(zhuǎn)換為合適的系統(tǒng)調(diào)用。然而,不同的操作系統(tǒng)往往使用互不兼容的系統(tǒng)調(diào)用。例如,與macOS或Linux 相比,Windows 在屏幕上打印信息所用的系統(tǒng)調(diào)用有所不同。因此,在使用x86 處理器的Windows 中編譯的程序,無(wú)法在使用x86處理器的Mac 中運(yùn)行。除針對(duì)特定的CPU 體系結(jié)構(gòu)外,編譯后的代碼還會(huì)針對(duì)特定的操作系統(tǒng)。
編譯優(yōu)化
優(yōu)秀的編譯器致力于優(yōu)化它們生成的機(jī)器碼。如果編譯器認(rèn)為可以通過(guò)修改部分代碼來(lái)提高執(zhí)行效率,則會(huì)處理。在生成二進(jìn)制輸出之前,編譯器可能?chē)L試應(yīng)用數(shù)百條優(yōu)化規(guī)則。因此,應(yīng)使代碼易于閱讀以利于進(jìn)行微優(yōu)化。編譯器最終將完成所有細(xì)微的優(yōu)化。例如,一些人對(duì)以下代碼頗有微詞。
function factorial(n)
if n 》 1
return factorial(n - 1) * n
else
return 1
他們認(rèn)為應(yīng)該進(jìn)行以下修改:
function factorial(n)
result ← 1
while n 》 1
result ← result * n
n ← n - 1
return result
誠(chéng)然,在不使用遞歸的情況下執(zhí)行factorial 函數(shù)將消耗較少的計(jì)算資源,但仍然沒(méi)有理由因此而改變代碼。現(xiàn)代編譯器將自動(dòng)重寫(xiě)簡(jiǎn)單的遞歸函數(shù),舉例如下。
i ← x + y + 1
j ← x + y
為避免進(jìn)行兩次x+y 計(jì)算,編譯器將上述代碼重寫(xiě)為:
t1 ← x + y
i ← t1 + 1
j ← t1
應(yīng)專(zhuān)注于編寫(xiě)清晰且自解釋的代碼。如果性能出現(xiàn)問(wèn)題,可以利用分析工具尋找代碼中的瓶頸,并嘗試改用更好的方法計(jì)算存在問(wèn)題的代碼。此外,避免在不必要的微操作上浪費(fèi)太多時(shí)間。但在某些情況下,我們希望跳過(guò)編譯,接下來(lái)將對(duì)此進(jìn)行討論。
腳本語(yǔ)言
某些語(yǔ)言在執(zhí)行時(shí)并未被直接編譯為機(jī)器碼,這些語(yǔ)言稱(chēng)為腳本語(yǔ)言,包括JavaScript、Python 以及Ruby。在腳本語(yǔ)言中,代碼由解釋器而非CPU 執(zhí)行,解釋器必須安裝在運(yùn)行代碼的機(jī)器中。解釋器實(shí)時(shí)轉(zhuǎn)譯并執(zhí)行代碼,因此其運(yùn)行速度通常比編譯后的代碼慢得多。但另一方面,程序員隨時(shí)都能立即運(yùn)行代碼而無(wú)須等待編譯過(guò)程。對(duì)于規(guī)模極大的項(xiàng)目,編譯可能耗時(shí)數(shù)小時(shí)之久。Google 工程師必須不斷編譯大量代碼,導(dǎo)致程序員“損失”了很多時(shí)間(圖7-9)。由于需要保證編譯后的二進(jìn)制文件有更好的性能,Google 無(wú)法切換到腳本語(yǔ)言。公司為此開(kāi)發(fā)了Go 語(yǔ)言,它的編譯速度極快,同時(shí)仍然保持很高的性能。
反匯編與逆向工程
給定一個(gè)已編譯的計(jì)算機(jī)程序,無(wú)法在編譯之前恢復(fù)其源代碼。但我們可以對(duì)二進(jìn)制程序解碼,將用于編碼CPU 指令的數(shù)字轉(zhuǎn)換為人類(lèi)可讀的指令序列。這個(gè)過(guò)程稱(chēng)為反匯編。接下來(lái),可以查看這些CPU 指令,并嘗試分析它們的用途,這就是所謂的逆向工程。某些反匯編程序?qū)@一過(guò)程大有裨益,它們能自動(dòng)檢測(cè)并注釋系統(tǒng)調(diào)用與常用函數(shù)。借由反匯編工具,黑客對(duì)二進(jìn)制代碼的各個(gè)環(huán)節(jié)了如指掌。我相信,許多頂尖的IT 公司都設(shè)有秘密的逆向工程實(shí)驗(yàn)室,以便研究競(jìng)爭(zhēng)對(duì)手的軟件。地下黑客經(jīng)常分析Windows、Photoshop、《俠盜獵車(chē)手》等授權(quán)程序中的二進(jìn)制代碼,以確定哪部分代碼負(fù)責(zé)驗(yàn)證軟件許可證。黑客將二進(jìn)制代碼修改,在其中加入一條指令,直接跳轉(zhuǎn)到驗(yàn)證許可證后執(zhí)行的代碼部分。運(yùn)行修改后的二進(jìn)制代碼時(shí),它在檢查許可證前獲取注入的JUMP 命令,從而可以在沒(méi)有付費(fèi)的情況下運(yùn)行非法的盜版副本。在秘密的政府情報(bào)機(jī)構(gòu)中,同樣設(shè)有供安全研究人員與工程師研究iOS、Windows、IE 瀏覽器等流行消費(fèi)者軟件的實(shí)驗(yàn)室。他們尋找這些程序中可能存在的安全漏洞,以防御網(wǎng)絡(luò)攻擊或?qū)Ω邇r(jià)值目標(biāo)的入侵。在這類(lèi)攻擊中,最知名的當(dāng)屬“震網(wǎng)”病毒,它是美國(guó)與以色列情報(bào)機(jī)構(gòu)研制的一種網(wǎng)絡(luò)武器。通過(guò)感染控制地下聚變反應(yīng)堆的計(jì)算機(jī),“震網(wǎng)”延緩了伊朗核計(jì)劃。
開(kāi)源軟件
如前所述,我們可以根據(jù)二進(jìn)制可執(zhí)行文件分析有關(guān)程序的原始指令,但無(wú)法恢復(fù)用于生成二進(jìn)制文件的原始源代碼。在沒(méi)有原始源代碼的情況下,即使可以稍許修改二進(jìn)制文件以便以較小的方式破解,實(shí)際上也無(wú)法對(duì)程序進(jìn)行任何重大更改(如添加新功能)。一些人推崇協(xié)作構(gòu)建代碼的方式,因此將自己的源代碼開(kāi)放供他人修改。“開(kāi)源”的主要概念就在于此:所有人都能自由使用與修改的軟件。基于Linux 的操作系統(tǒng)(如Ubuntu、Fedora 與Debian)是開(kāi)源的,而Windows 與macOS 是閉源的。開(kāi)源操作系統(tǒng)的一個(gè)有趣之處在于,任何人都可以檢查源代碼以尋找安全漏洞。現(xiàn)已證實(shí),政府機(jī)構(gòu)通過(guò)日常消費(fèi)者軟件中未修補(bǔ)的安全漏洞,對(duì)數(shù)百萬(wàn)平民進(jìn)行利用和監(jiān)視。但對(duì)開(kāi)源軟件而言,代碼受到的關(guān)注度更高,因此惡意的第三方與政府機(jī)構(gòu)很難植入監(jiān)控后門(mén)程序。使用macOS 或Windows 時(shí),用戶(hù)必須相信Apple 或Microsoft 對(duì)自己的安全不會(huì)構(gòu)成危害,并盡最大努力防止任何嚴(yán)重的安全漏洞。而開(kāi)源系統(tǒng)置于公眾的監(jiān)督之下,因此安全漏洞被忽視的可能性大為降低。
存儲(chǔ)器層次結(jié)構(gòu)
我們知道,計(jì)算機(jī)的操作可以歸結(jié)為使CPU 執(zhí)行簡(jiǎn)單的指令,這些指令只能對(duì)存儲(chǔ)在CPU 寄存器中的數(shù)據(jù)操作。但寄存器的存儲(chǔ)空間通常被限制在1000 字節(jié)以?xún)?nèi),這意味著CPU 寄存器與RAM 之間必須不斷進(jìn)行數(shù)據(jù)傳輸。如果存儲(chǔ)器訪(fǎng)問(wèn)速度過(guò)慢,CPU 將被迫處于空閑狀態(tài),以等待RAM 完成數(shù)據(jù)傳輸。CPU 讀寫(xiě)存儲(chǔ)器中數(shù)據(jù)所需的時(shí)間與計(jì)算機(jī)性能直接相關(guān)。提高存儲(chǔ)器速度有助于加快計(jì)算機(jī)運(yùn)行,也可以提高CPU 訪(fǎng)問(wèn)數(shù)據(jù)的速度。CPU 能以近乎實(shí)時(shí)的速度(一個(gè)周期以?xún)?nèi))訪(fǎng)問(wèn)存儲(chǔ)在寄存器中的數(shù)據(jù),但訪(fǎng)問(wèn)RAM 則慢得多。
對(duì)于時(shí)鐘頻率為1 GHz 的CPU,一個(gè)周期的持續(xù)時(shí)間約為十億分之一秒,這是光線(xiàn)從本書(shū)進(jìn)入讀者眼中所需的時(shí)間。
處理器與存儲(chǔ)器之間的鴻溝
近年來(lái)的技術(shù)發(fā)展使得CPU 速度成倍增長(zhǎng)。雖然存儲(chǔ)器速度同樣有所提高,但卻慢得多。CPU 與RAM 之間的這種性能差距稱(chēng)為“處理器與存儲(chǔ)器之間的鴻溝”。我們可以執(zhí)行大量CPU 指令,因此它們很“廉價(jià)”;而從RAM 獲取數(shù)據(jù)所需的時(shí)間較長(zhǎng),因此它們很“昂貴”。隨著兩者之間的差距逐漸增大,提高存儲(chǔ)器訪(fǎng)問(wèn)效率的重要性越發(fā)明顯。現(xiàn)代計(jì)算機(jī)需要大約1000 個(gè)CPU 周期(1 微秒左右) 從RAM 獲取數(shù)據(jù)。這種速度已很驚人,但與訪(fǎng)問(wèn)CPU 寄存器的時(shí)間相比仍然較慢。減少計(jì)算所需的RAM 操作次數(shù),是計(jì)算機(jī)科學(xué)家追求的目標(biāo)。
在兩個(gè)面對(duì)面的人之間,聲波傳播需要大約10 微秒。
時(shí)間局部性與空間局部性
在嘗試盡量減少對(duì)RAM 的訪(fǎng)問(wèn)時(shí),計(jì)算機(jī)科學(xué)家開(kāi)始注意到兩個(gè)事實(shí)。◎ 時(shí)間局部性:訪(fǎng)問(wèn)某個(gè)存儲(chǔ)地址時(shí),可能很快會(huì)再次訪(fǎng)問(wèn)該地址。◎ 空間局部性:訪(fǎng)問(wèn)某個(gè)存儲(chǔ)地址時(shí),可能很快會(huì)訪(fǎng)問(wèn)與之相鄰的地址。因此,將這些存儲(chǔ)地址保存在CPU 寄存器中,有助于避免大部分對(duì)RAM的“昂貴”操作。不過(guò)在設(shè)計(jì)CPU 芯片時(shí),工業(yè)工程師并未找到可行的方法來(lái)容納足夠多的內(nèi)部寄存器,但他們?nèi)匀话l(fā)現(xiàn)了如何有效地利用時(shí)間局部性與空間局部性。接下來(lái)將對(duì)此進(jìn)行討論。
一級(jí)緩存
可以構(gòu)建一種集成在CPU 內(nèi)部且速度極快的輔助存儲(chǔ)器,這就是一級(jí)緩存。將數(shù)據(jù)從一級(jí)緩存讀入寄存器,僅比直接從寄存器獲取數(shù)據(jù)稍慢。利用一級(jí)緩存,我們將可能訪(fǎng)問(wèn)的存儲(chǔ)地址中的內(nèi)容復(fù)制到CPU 寄存器附近,借此以極快的速度將數(shù)據(jù)載入CPU 寄存器。將數(shù)據(jù)從一級(jí)緩存讀入寄存器僅需大約10 個(gè)CPU 周期,速度是從RAM 獲取數(shù)據(jù)的近百倍。借由10 KB 左右的一級(jí)緩存,并合理利用時(shí)間局部性與空間局部性,超過(guò)一半的RAM 訪(fǎng)問(wèn)調(diào)用僅通過(guò)緩存就能實(shí)現(xiàn)。這一創(chuàng)新使計(jì)算技術(shù)發(fā)生了翻天覆地的變化。一級(jí)緩存可以極大縮短CPU 的等待時(shí)間,使CPU 將更多時(shí)間用于實(shí)際計(jì)算而非處于空閑狀態(tài)。
二級(jí)緩存
提高一級(jí)緩存的容量有助于減少?gòu)腞AM 獲取數(shù)據(jù)的操作,進(jìn)而縮短CPU 的等待時(shí)間。但是,增大一級(jí)緩存的同時(shí)也會(huì)降低它的速度。在一級(jí)緩存達(dá)到50 KB 左右時(shí),繼續(xù)增加其容量就要付出極高的成本。更好的方案是構(gòu)建一種稱(chēng)為二級(jí)緩存的緩存。二級(jí)緩存的速度稍慢,但容量比一級(jí)緩存大得多。現(xiàn)代CPU 配備的二級(jí)緩存約為200 KB,將數(shù)據(jù)從二級(jí)緩存讀入CPU 寄存器需要大約100 個(gè)CPU 周期。我們將最有可能訪(fǎng)問(wèn)的地址復(fù)制到一級(jí)緩存,較有可能訪(fǎng)問(wèn)的地址復(fù)制到二級(jí)緩存。如果CPU 沒(méi)有在一級(jí)緩存中找到某個(gè)存儲(chǔ)地址,仍然可以嘗試在二級(jí)緩存中搜索。僅當(dāng)該地址既不在一級(jí)緩存、也不在二級(jí)緩存中時(shí),CPU 才需要訪(fǎng)問(wèn)RAM。目前,不少制造商推出了配備三級(jí)緩存的處理器。三級(jí)緩存的容量比二級(jí)緩存大,雖然速度不及二級(jí)緩存,但仍然比RAM 快得多。一級(jí)/ 二級(jí)/ 三級(jí)緩存非常重要,它們占據(jù)了CPU 芯片內(nèi)部的大部分硅片空間。見(jiàn)圖7-11。使用一級(jí)/ 二級(jí)/ 三級(jí)緩存能顯著提高計(jì)算機(jī)的性能。在配備200 KB的二級(jí)緩存后,CPU 發(fā)出的存儲(chǔ)請(qǐng)求中僅有不到10% 必須直接從RAM獲取。讀者今后購(gòu)買(mǎi)計(jì)算機(jī)時(shí),對(duì)于所挑選的CPU,請(qǐng)記住比較一級(jí)/ 二級(jí)/三級(jí)緩存的容量。CPU 越好,緩存越大。一般來(lái)說(shuō),建議選擇一款時(shí)鐘頻率稍低但緩存容量較大的CPU。
第一級(jí)存儲(chǔ)器與第二級(jí)存儲(chǔ)器
如前所述,計(jì)算機(jī)配有不同類(lèi)型的存儲(chǔ)器,它們按層次結(jié)構(gòu)排列。性能最好的存儲(chǔ)器容量有限且成本極高。沿層次結(jié)構(gòu)向下,可用的存儲(chǔ)空間越來(lái)越多,但訪(fǎng)問(wèn)速度越來(lái)越慢。在存儲(chǔ)器層次結(jié)構(gòu)中,位于CPU 寄存器與緩存之下的是RAM,它負(fù)責(zé)存儲(chǔ)當(dāng)前運(yùn)行的所有進(jìn)程的數(shù)據(jù)與代碼。截至2017 年,計(jì)算機(jī)配備的RAM 容量通常為1 GB 到10 GB。但在許多情況下,RAM 可能無(wú)法滿(mǎn)足操作系統(tǒng)以及所有運(yùn)行程序的需要。因此,我們必須深入探究存儲(chǔ)器層次結(jié)構(gòu),使用位于RAM 之下的硬盤(pán)。截至2017 年,計(jì)算機(jī)配備的硬盤(pán)容量通常為數(shù)百吉字節(jié),足以容納當(dāng)前運(yùn)行的所有程序數(shù)據(jù)。如果RAM 已滿(mǎn),當(dāng)前的空閑數(shù)據(jù)將被移至硬盤(pán)以釋放部分內(nèi)存空間。問(wèn)題在于,硬盤(pán)的速度非常慢,它一般需要100 萬(wàn)個(gè)CPU 周期(1 毫秒)a 在磁盤(pán)與RAM 之間傳輸數(shù)據(jù)。從磁盤(pán)訪(fǎng)問(wèn)數(shù)據(jù)看似很快,但不要忘記,訪(fǎng)問(wèn)RAM 僅需1000 個(gè)周期,而訪(fǎng)問(wèn)磁盤(pán)需要100 萬(wàn)個(gè)周期。RAM 通常稱(chēng)為第一級(jí)存儲(chǔ)器,而存儲(chǔ)程序與數(shù)據(jù)的磁盤(pán)稱(chēng)為第二級(jí)存儲(chǔ)器。
標(biāo)準(zhǔn)照片在大約4 毫秒內(nèi)捕捉光線(xiàn)。
CPU 無(wú)法直接訪(fǎng)問(wèn)第二級(jí)存儲(chǔ)器。執(zhí)行保存在第二級(jí)存儲(chǔ)器中的程序之前,必須將其復(fù)制到第一級(jí)存儲(chǔ)器。實(shí)際上,每次啟動(dòng)計(jì)算機(jī)時(shí),即便是操作系統(tǒng)也要從磁盤(pán)復(fù)制到RAM,否則CPU 無(wú)法運(yùn)行。確保RAM 永不枯竭 在典型活動(dòng)期間,確保計(jì)算機(jī)處理的所有數(shù)據(jù)與程序都能載入RAM 至關(guān)重要,否則計(jì)算機(jī)將不斷在磁盤(pán)與RAM 之間交換數(shù)據(jù)。由于這項(xiàng)操作的速度極慢,計(jì)算機(jī)性能將嚴(yán)重下降,甚至無(wú)法使用。這種情況下,計(jì)算機(jī)不得不花費(fèi)更多時(shí)間等待數(shù)據(jù)傳輸,而無(wú)法進(jìn)行實(shí)際的計(jì)算。當(dāng)計(jì)算機(jī)不斷將數(shù)據(jù)從磁盤(pán)讀入RAM 時(shí),則稱(chēng)計(jì)算機(jī)處于抖動(dòng)模式。必須對(duì)服務(wù)器進(jìn)行持續(xù)監(jiān)控,如果服務(wù)器開(kāi)始處理無(wú)法載入RAM 的數(shù)據(jù),那么抖動(dòng)可能會(huì)導(dǎo)致整個(gè)服務(wù)器崩潰。銀行或收銀機(jī)前將因此排起長(zhǎng)隊(duì),而服務(wù)員除了責(zé)怪發(fā)生抖動(dòng)的計(jì)算機(jī)系統(tǒng)之外別無(wú)他法。內(nèi)存不足或許是導(dǎo)致服務(wù)器故障的主要原因之一。
外部存儲(chǔ)器與第三級(jí)存儲(chǔ)器
我們繼續(xù)沿存儲(chǔ)器層次結(jié)構(gòu)向下分析。在連接到網(wǎng)絡(luò)之后,計(jì)算機(jī)就能訪(fǎng)問(wèn)由其他計(jì)算機(jī)管理的存儲(chǔ)器。它們要么位于本地網(wǎng)絡(luò),要么位于因特網(wǎng)(即云端)。但訪(fǎng)問(wèn)這些數(shù)據(jù)所需的時(shí)間更長(zhǎng):讀取本地磁盤(pán)需要1 毫秒,而獲取網(wǎng)絡(luò)中的數(shù)據(jù)可能耗時(shí)數(shù)百毫秒。網(wǎng)絡(luò)包從一臺(tái)計(jì)算機(jī)傳輸?shù)搅硪慌_(tái)計(jì)算機(jī)大約需要10 毫秒,如果經(jīng)由因特網(wǎng)傳輸則需要200 毫秒到300 毫秒,與眨眼的時(shí)間相仿。位于存儲(chǔ)器層次結(jié)構(gòu)底部的是第三級(jí)存儲(chǔ)器,這種存儲(chǔ)設(shè)備并非總是在線(xiàn)與可用的。在盒式磁帶或CD 中存儲(chǔ)數(shù)百萬(wàn)吉字節(jié)的數(shù)據(jù)成本較低,但訪(fǎng)問(wèn)這類(lèi)介質(zhì)中的數(shù)據(jù)時(shí),需要將介質(zhì)插入某種讀取設(shè)備,這可能需要數(shù)分鐘甚至數(shù)天之久(不妨嘗試讓IT 部門(mén)在周五晚上備份磁帶中的數(shù)據(jù)……)。有鑒于此,第三級(jí)存儲(chǔ)器僅適合歸檔很少訪(fǎng)問(wèn)的數(shù)據(jù)。
存儲(chǔ)技術(shù)的發(fā)展趨勢(shì)
一方面,很難顯著改進(jìn)“快速”存儲(chǔ)器(位于存儲(chǔ)器層次結(jié)構(gòu)頂端)所用的技術(shù);另一方面,“慢速”存儲(chǔ)器的速度越來(lái)越快,價(jià)格也越來(lái)越低。幾十年來(lái),硬盤(pán)存儲(chǔ)的成本一直在下降,這種趨勢(shì)似乎還將持續(xù)下去。新技術(shù)也使磁盤(pán)的速度得以提高。人們正從旋轉(zhuǎn)磁盤(pán)轉(zhuǎn)向固態(tài)硬盤(pán)(SSD),它沒(méi)有動(dòng)件,因而更快、更可靠且更省電。采用SSD 技術(shù)的磁盤(pán)正變得越來(lái)越便宜且越來(lái)越快,但其價(jià)格仍然不菲。有鑒于此,一些制造商推出了同時(shí)采用SSD 與磁技術(shù)的混合磁盤(pán)。后者將訪(fǎng)問(wèn)頻率較高的數(shù)據(jù)存儲(chǔ)在SSD 中,訪(fǎng)問(wèn)頻率較低的數(shù)據(jù)存儲(chǔ)在速度較慢的磁盤(pán)中。當(dāng)需要頻繁訪(fǎng)問(wèn)原先不經(jīng)常訪(fǎng)問(wèn)的數(shù)據(jù)時(shí),則將其復(fù)制到混合驅(qū)動(dòng)器中速度較快的SSD。這與CPU 利用內(nèi)部緩存提高RAM 訪(fǎng)問(wèn)速度的技巧頗為類(lèi)似。
小結(jié)
本文介紹了一些基本的計(jì)算機(jī)工作原理。任何可計(jì)算的事物都能采用簡(jiǎn)單的指令來(lái)表示。為將復(fù)雜的計(jì)算命令轉(zhuǎn)換為CPU 可以執(zhí)行的簡(jiǎn)單指令,需要使用一種稱(chēng)為編譯器的程序。計(jì)算機(jī)之所以能進(jìn)行復(fù)雜計(jì)算,僅僅是因?yàn)镃PU 可以執(zhí)行大量基本操作。計(jì)算機(jī)的處理器速度很快,但存儲(chǔ)器相對(duì)較慢。CPU 并非以隨機(jī)方式訪(fǎng)問(wèn)存儲(chǔ)器,而是遵循空間局部性與時(shí)間局部性原理。因此,可以將訪(fǎng)問(wèn)頻率較高的數(shù)據(jù)緩存在速度更快的存儲(chǔ)器中。這一原則在多個(gè)級(jí)別的緩存中得到了應(yīng)用:從一級(jí)緩存直到第三級(jí)存儲(chǔ)器,不一而足。本文討論的緩存原則可以應(yīng)用于多種場(chǎng)景。確定應(yīng)用程序頻繁使用的數(shù)據(jù),并設(shè)法提高這部分?jǐn)?shù)據(jù)的訪(fǎng)問(wèn)速度,是縮短計(jì)算機(jī)程序運(yùn)行時(shí)間的最常用策略之一。——本文選自《計(jì)算機(jī)科學(xué)精粹》
編輯:黃飛
評(píng)論