Recent Comments

2008年2月25日 星期一

[轉自IThome]IT自救術-處理器想的和你想的不一樣

術-處理器想的和你想的不一樣

文/嚴立群 (記者) 2008-02-14

人類做算術,心智的活動是很自由的,但是處理器不行,處理器一次只能做一步簡單的運算,所以剛接觸電腦的人,大多會覺得難以理解,「這東西的運作方法怎麼這麼怪?」

假設存在一個32位元加法器,要設法讓它幫我們不斷的計算A+B,那只需要讓數字在加法器後面排隊,照順序饋入加法器,加法器就會一個一個把結果算出來。

這是一個典型的「磨坊-穀倉」系統,加法器就是「磨」,不斷饋入的數字是「穀物」,算出的結果就是「穀粉」。這裡要引入一個概念,就是「穀倉」的必要性。


簡單的加法系統

從「加法器」擴展到「處理器」
接著我們來擴展這個系統,想一想處理器會是個怎樣的系統

● 加法器只能做加法,這不夠好,我們得設計個全功能的處理器,能作加法、減法、乘法、除法、各種邏輯運算……,而且都是越快越好。

● 原來的加法器因為只做加法,所以我們放在記憶體裡面的「數字」就只是依序排隊饋入即可──因為反正只有加法動作要做而已。但是既然有了更複雜的處理器,後面排隊的就不能只是數字(不能只有運算子),還得有「運算方式」(運算元),得設計處理器讓它判斷該怎麼處理後面排隊的「穀物」(我是說,數字)。

這裡有個重要的概念,「你不能用人類的想法去操作處理器,你得用處理器的方式」。

人類習慣的做法是,3 + 5 = 8,很單純的計算加法,但處理器沒辦法。

處理器的做法(以80x86為例)是:

MOV AX, 03
ADD AX, 05

第一行的「MOV AX, 03」,就是「把16進位的3搬到AX暫存器裡面」的意思,第二行的「ADD AX, 05」,則是指「把16進位的5和AX目前的值加起來,並把結果存到AX裡面」。

是,有沒有覺得「這真是『微言大義』啊!」,每個處理器都像是筆削春秋的孔子,一個指令就蘊含著多麼深奧的奧義在裡頭啊!
請參考下列的「8086的『3 + 5 = 8』操作過程」。


8086的「3 + 5 = 8」操作過程
1. 執行「命令提示字元」,在命令提示符號下輸入「DEBUG」然後按下Enter

2. 輸入A100(A表示開始組譯程式碼,100表示從100這個位址開始,不要問我為什麼要這樣,反正就是這樣),按下Enter

3. 輸入「MOV AX, 03」後按下Enter

4. 輸入「ADD AX, 05」後按下Enter,然後再按一次Enter表示輸入結束

5. 輸入R並按下Enter,可看一下目前的8086暫存器內容,AX目前內容應該是0(R=Register,DEBUG程式中用來顯示暫存器內容的命令。另,如果你輸入問號「?」後按下Enter,可以看到所有DEBUG的指令)

6. 目前只是輸入完成,請按下「P」(P = proceed,執行的意思)然後按下Enter來執行第一行指令(也就是MOV AX, 03)

7. 請看AX內容,此時果然變成3了

8. 接著再輸入一次P並按下「Enter」,此時執行第二行指令(ADD AX, 05)

9. 再看AX內容,此時果然變成8了(因為3 + 5 = 8啊)

10. 輸入U100 U103(U表示unassemble,反組譯的意思),可以看到兩行程式碼前面有一些數字q,請先看一下,等下會解釋。

處理器的動作
當我們請處理器算「3 + 5 = 8」這麼簡單的算數時,給它的東西得是「MOV AX, 03」和「ADD AX, 05」。

事實上這樣講也不太精確。

處理器實際上是一顆晶片,它只能接受電子訊號。假設低電位是0,高電位是1,所以你真的對它講「MOV AX, 03」,它也不認得,它唯一能認得的,只有電子訊號。

事實上,它認得的,就是剛才的過程中,第10步裡,用U100反組譯出來的機器碼。舉例來說,「MOV AX, 03」的機器碼是「B80300」,「ADD AX, 05」的機器碼是「050500」。你對處理器只能用這樣的形式溝通。事實上,你得在適當的腳位輸入適當的訊號(B80300和050500的二進位形式),然後處理器收到訊號,就會跟據訊號做出對應的動作。

現代處理器系統的運作模式,就是這樣。

所謂的程式,只是充滿了各種人類「表面上看起來難解」的數字,「B80300」、「050500」……在記憶體裡面排隊,這些數字饋入處理器之後,處理器就會跟據這些數字的意義,做適當的動作,把結果算出來,就這樣而已。

至於能夠多快?很簡單,廠商會想辦法。如果MOV AX, 03是「一則運算」,那以前的處理器可能慢些,4.77MHz(一秒鐘做4.77百萬次),現在的處理器可能快些,1GHz應該很快了(一秒鐘做100億次)。而且以效能來看,處理器的效能還因為技術的進步,效能可能高達早期處理器的2000倍?4000倍?總之是快得恐怖,而且還會一路快下去。

Little-Endian和Big-Endian
接著我們來研究一下,機器碼的一個小知識,「Little Endian」和「Big-Endian」。

簡而言之,資料的最小單位假設一般用「byte」,每一個記憶體位址儲存的是1-byte的數字,但處理器一次處理資料的最小單位是「word」,那這一個word裡面,較小的數字放在「較低位址」,較大的數字放在「較高位址」,稱為「Little-Endian」。

比方說,以8086處理器而言,它的1個word是16-bit,當我們寫0300,0300是一個word的數字(16位元的16進位數字)。左邊的數字是較高(代表較大)的數字。但是,一般印刷或是螢幕顯示的慣例,從左到右卻是「越來越大」。所以,0300在螢幕上顯示成0003。

所以,「MOV AX, 03」翻譯成機器碼,卻變成「B8 03 00」,B8是MOV AX的機器碼,03其實是0003,但在Debug下顯示卻變成0300。
至於Big-Endian,則是指「處理器一次處理的單位資料,較高位數的資料卻放在較低的位址」。目前世界上,IBM的PowerPC處理器是Big-Endian的典型代表作,Intel x86系列處理器則是Little-Endian的代表作。

組合語言「妙處難與君說」
為何組合語言程式不能更容易些?

老實說,很難。

處理器裡面有很多暫存器,這是因為人類想了很久,都只想到這個辦法,「把數字搬到暫存器,然後對暫存器做出運算的動作」。所以一個「3 + 5」,以8086而言,就得分兩次(先MOV,再ADD)。一旦你習慣了這種「思考模式」,就可以用組合語言寫各種程式。

因此,古時候的人學寫程式,尤其是用某處理器的組合語言寫程式,就得先研究該處理器的「能耐」。處理器有哪些指令?學熟了,才知道怎樣的指令可以運用在什麼場合。處理器能夠怎麼讀取資料?得把所謂的「定址模式」搞清楚,才能夠存取大量資料(畢竟程式是用來處理資料的)。怎樣寫程式才會快?你得知道這處理器的「脾胃」,不然你寫出來的程式會跑得像龜一樣,不能用。你還得熟習各種工具的使用,幫助你寫程式寫得又快又好,又沒有「蟲」。

用組合語言寫的程式,我印象最深刻的,應該就是Lotus 1-2-3這個試算表了。其實Lotus 1-2-3並不是世界上第一個試算表產品。那難道是Microsoft的Excel?那更加不是。目前公認的「試算表之祖」,是丹.布魯克林所寫的VisiCalc。

不過,總之,VisiCalc因為某些原因後來並沒有壯大,後來的PC市場,主要是Microsoft的Multiplan(不是Excel)和 Lotus在爭奪DOS下的試算表市場。但是Lotus 1-2-3因為完全用組合語言撰寫,所以速度快,因此獲得華爾街諸多公司的青睞。Multiplan則為了「跨平臺」,當時使用C語言撰寫,效能普通,因此被打得潰不成軍,早已經不見蹤影。

這故事給我們幾個啟示:

1. Microsoft也有過失敗之作,所以失敗並不可恥
2. 此一時也彼一時也,以前的人用組合語言才厲害,現在則否
3. 組合語言的跨平臺性不佳,但如果你不考慮跨平臺性,可以用用
4. Microsoft的恐怖並不只在於它失敗了還會再爬起來而已,它更恐怖的地方是它爬起來了之後還會試著把敵人幹掉,絕對不是說說而已

本期結語:流動與靜止的藝術
處理器的運作方式很單純,一條指令,一次只能做一個簡單動作而已。就連3 + 5,都只能分兩次做。

在那一瞬間,真是很單純很單純。

但是,工程師的挑戰,就是要不斷的縮短這「一瞬間」,讓電子的流動化為運算的智慧,把很高速很短暫的一瞬間集合起來,這樣就可創造出複雜的結果,運算的結果。

想像一下,你買了一個「吹風機」,吹風機插上電,一打開開關就開始運作,那是電力的作用,讓風扇轉動,這種力量固然厲害,小小的電子驅動各種裝置,但是吹風機沒法告訴你3 + 5等於多少,無論如何沒辦法。

但是一樣是電子的流動,要怎樣的設計才能讓晶片能夠算出3 + 5?這很有趣,我們現在知道,「半導體」的單通特性使它變成一種開關,可以用來模擬演算。人類是走了很長的路才走到今天,這實在是非常有趣的事情。流動的電子,但每一瞬間卻又得維持靜止的狀態,讓人類可以構思各種運算。

處理器的運作原理大致講到這裡,我們下期可以來開始研究一下x86的組織結構,簡單的組合語言程式。

0 意見: