國慶期間,我造了臺計算機

每一個時代,都不會虧待會學習的人。java

你們好,我是 yes。程序員

對於咱們程序員來講計算機的重要性不言而喻,相信你們對計算機內部也有必定的瞭解。服務器

可是你們有沒想過爲何一堆邏輯門組合起來就能運算了?它是如何運做來實現加減法的?微信

爲何 cpu 會不停地取指執行?是什麼在驅動着它?異步

今天我就和你們一塊兒來探索一下底層的奧祕,可是術業有專攻,咱們大體的瞭解一下便可,不少細節不清晰也不影響。工具

不過相信經過這篇文章你會對底層有不同的認識,包括運算單元、內存、時鐘、地址、溢出、補碼等等。學習

先打個預防針吧,這篇文章有不少電路圖,你可能感受這啥啊,和咱們開發有關係嗎?測試

看下去你會懂的,雖然說平日裏咱們都是 CRUD Boy,可是咱們也得時刻保持着好奇心,要有求知慾和探索精神。編碼

正文

這個故事得從「電」開始提及。spa

生活中電無處不在,而它卻時刻保持着神祕感,爲什麼插上電咱們的屏幕就會亮?咱們的服務器就能跑?

電是如何來的?

電起源於電子的運動,咱們知道一切物質都是由原子組成的,而原子又是由中子、質子和電子構成。

在某種狀況下電子從原子中電離出來,這樣電就產生了。

質子和電子都具備帶電荷的特性,質子帶正電荷、電子帶負電荷。

而異電相吸,同電相斥,當質子數和電子數相等的時候是最穩定的,若是數量不平衡也會往趨於平衡的方向發展

像雷雨天氣,雲層下層積累電子而云層頂層失去電子,而閃電就是大量的電子迅速從一端轉移到另外一端產生的結果,爲了趨於平衡。

題外話 :細心的朋友可能看到這原子核質子不都合在一塊兒了啊,不是說同電相斥嘛?這是由於有個叫強內力的玩意彙集了它們,釋放核能的原子核裂變就是由強內力致使的。

相信你們都作過電池點亮燈泡的物理實驗。

這其實就是電池發生化學反應,在負極產生多餘的電子,而後經過迴路中的原子相似接力的形式,一個原子獲得電子以後會傳遞給相鄰的另外一個原子,如此循環傳遞電路就造成了,最終經過燈泡到達電池的正極。

改裝下再套上個外殼,手電筒就這樣被造出來了。

而手電筒不只僅能夠用來照明,還能用來通訊。相信你們都看過相似的電影場景,我這手電筒的光閃三下我們就上!

而說到這樣簡易的通訊就不得不提摩爾斯電碼,相信你們也從各渠道對摩爾斯電碼有必定的瞭解,好比「星際穿越」這部賊好看的電影。

在 19 世紀初期,那時候的遠距離通訊還得利用馬車等工具長時間運輸傳遞,人們一直在摸索即時遠距離通訊的方法。那時的摩爾斯就開始埋頭實驗,最終發明了電報。

電報的思想和上述說的手電筒思想同樣,手電筒通訊的思想是經過開關來控制燈的亮暗,而電報利用的是電磁現象。

將導線纏繞在鐵棒上,而後通電以後鐵棒就變成了磁鐵,斷電了磁性又會消失,而後再搞個發聲器,經過磁性來吸引可動棒敲擊發聲。

通電後可動棒被拉下,敲擊下方就會發出 「滴」 的聲音,斷電則可動棒復位,敲擊上方發出 「嗒」 的聲音。將快速的滴答做爲點,慢速的滴答做爲劃。

經過導線的長距離鏈接就能實現遠距離通訊,經過判別點和劃的組合查閱摩爾斯電碼表,轉成最終的信息。

若是要雙向通訊,就再搞一個反過來部署就行了,這就是電報機了。

不過導線是有電阻的,導線越長電阻越大,因此是有距離限制的,不過這難不倒咱們,最簡單的方法就是轉發一下。

在中間距離也建個電報站,而後僱一我的,獲得發送方的電報信息以後,從新敲一遍發送給真正的接收方,可是這須要多餘的人力,因此能夠以下圖所示,搞個棒子連起來帶動下一個開關的輸出

這其實就是繼電器原理,咱們來看看繼電器是如何的設計的。

下方通電產生磁力,吸引上方的金屬桿掛下,而後上方造成迴路所以也通電了,這樣遠距離傳輸的微弱電流就被又一次放大輸出了,因此最終的遠距離電報應該是這樣的。

能夠看到繼電器這個發明是真的巧妙。

理解了上面所述的電的生成、電報以及繼電器以後咱們再來看看二進制

基於二進制的數字系統是最簡單的,只有 0 和 1,不能再進一步簡化了,而簡單就表明着清晰,就像開關要麼開要麼關。

而二進制的組合又能夠表明多種可能,好比第一個 0 表示男,1 表示女 ,第二個 0 表示胖,1 表示瘦。

讓咱們再回到以前的電池電燈圖中,此次搞兩個開關。

能夠得知兩個開關都閉合電燈纔會亮,若是轉化成二進制表示,0 表示開關斷開,1 表示開關閉合,0 表示燈泡不亮,1 表示燈泡亮,總結成一張表格的話就是:

左開關 右開關 燈泡
0 0 0
0 1 0
1 0 0
1 1 1

這其實就是咱們熟知的 AND 操做,若是把電路稍微改一下就是 OR 操做了。

若是把不少開關組合起來就能執行簡單的邏輯任務,可是開關須要手動的去控制。

記得以前提到的繼電器嗎?它也能串聯或者並聯電路,並且能夠被其餘繼電器聯動控制,不須要一個一個撥動,所以用繼電器來組合更加合適,而繼電器的組合稱之爲邏輯門

簡單點的就像下圖所示,開關閉合燈泡就會亮。

有些人以爲這不是屢次一舉嗎,這實際上是個緩衝器,能夠延遲信號,也能夠放大信號,並且這個電路比較簡單,實際上有不少組合,好比下圖的這個反向操做,開關閉合的燈反而不會亮。

還有像這樣的串聯組合,只有兩個開關都閉合燈泡纔會亮。

固然這裏的輸入不必定得是開關,輸出也不必定得是燈泡,只是爲了更加直觀的表現出來,不過這樣畫電路太麻煩了,因而電氣工程師們就搞了個符號來表示這些電路,好比上面的串聯其實就是 AND 操做,是與門。

簡化一下上面的圖就變成下面的樣子。

若是電路圖以下所示,就是並聯,隨便一個開關開了燈泡都會亮,這就是或門。

簡化符號是這樣的:

前面還提到個反向操做的,開關閉上燈泡反而不亮的叫反向器,符號以下圖所示。

咱們再來看看這樣的電路。

只有當兩個開關都斷開的狀況下燈泡纔會亮,任何一個開關閉合燈泡都會熄滅,這個操做和 OR 操做相反,稱之爲 NOR 即或非門,簡化後的符號比或門多了個小圓圈,表明反向。

或者這樣,組合着畫也同樣。

而後咱們再來看看這種電路,只有兩個開關都斷開纔會熄滅,這和與門正好相反,稱之爲 NAND 與非門。

簡化符號是這樣的,也是多了個圓圈:

我再總結一下這幾個簡化圖,加深一下印象。

二進制加法機

有了上面這幾樣東西,咱們就能夠造個二進制加法機,不要小看加法,由於能夠用加法來實現減法、乘法、除法等操做。

加法咱們知道會獲得當前的和、進位這兩個信息,例如二進制中 1 + 1,當前和是 0 ,進位 1。

進位 0 1
0 0 0
1 0 1

能夠看到只有 1 +1 進位 1 ,再仔細看看是否是和 AND 操做很像?只有 1 AND 1 結果才爲1 。

AND 0 1
0 0 0
1 0 1

咱們再來看看當前和的計算

0 1
0 0 1
1 1 0

你們能夠在腦子裏面想象下,若是拿 OR 操做來套用的話右下角結果不對,若是是 NAND 操做的話左上角結果不對,因此得兩個結合一下,電路圖以下。

分別經過或門和與非門以後再作與門,出來的結果就是當前和的結果,這個其實就是 XOR 異或門,簡化表示就是:

因此加法須要經過兩個邏輯門,分別是異或門來操做當前和,與門來操做進位,結合起來以下圖所示:

這其實就是個半加器,簡化的圖以下所示:

那爲何叫半加器?由於只能一位一位的加,而前一位的進位參與不到下一位的計算,若是要加入進位那下一位的運行就是 A 的當前位 + B 的當前位 + A 和 B 以前的進位。

所以須要改裝一下,兩個半加器合起來再加一個或門。

假設 A 輸入 1 , B 輸入 1, 進位輸入 1,從最左邊開始第一個半加器 S 輸出 0 , CO 輸出 1,第二個半加器 S輸出 1,CO 輸出 0,最終和輸出 1,進位輸出 1,結果沒毛病可行,這叫全加器,簡化一下圖:

全加器有了,我們得組合起來,而且須要有輸入和輸出,咱們經過開關來輸入數字,由燈泡的亮暗顯示結果。

這就是一個 8 位的計算器,有 9 個燈是由於兩個 8 位相加結果多是 9 位。

而後從最右邊開始以下圖所示接上全加器,進位接地表示 0 輸入

中間的都以下接法,前一個的進位輸出是下一位的進位輸入。

最後一個就是把進位輸出直接接到第九個燈上就好了。

此時你擺動控制面板的開關,就能夠經過機器獲得相加的結果。簡化的畫法以下圖所示:

如今咱們已經造出了八位加法器了,若是要 16 位呢?簡單合一下就行了。

固然真實的計算機原理差很少是這樣的,不過會更復雜,好比不會像咱們的加法器,一個一個的進位加,而是會先行進位,並且也不會用繼電器,而是晶體管等等。

減法怎麼弄?

加法器咱們搞出來了,那減法怎麼作?減法須要有借位操做。

咱們先拿熟悉的十進制來講。假設你的帳戶上限是499,你的透支額度是500,也就是說你的帳戶金額範圍是 -500~499 這 1000 個數字,要求不能用負號來表示。

能夠看到這是個三位數,而最大值就到 499 過,說明 500~999 之間的數沒用,那拿來表示負數不就剛恰好嗎?

因此讓 500 表示 - 500 ,501 表示  -499,以此類推。

500,501.......998,999,000,001......498,499讓五、六、七、八、9開頭的數都表明負數,並且是否是看起來還造成了個環形, 499 + 1 就變成 500 了,而後 999 + 1 變成 1000 ,可是隻能三位數表示,因此溢出了變成 000。

這種處理叫 10 的補數,若是要把三位負數轉爲 10 的補數,就是讓 999 減去它再加一,也就是說 10 的補數等於 9 的補數加一。

補數的概念:拿 9 的補數來講,將一個數從一串 9 中減去獲得的結果就叫這個數 9 的補數,好比 123 ,它是三位數 ,999-123 = 876 因此 123 的 9 的補數就是 876,若是把結果 + 1那就是 10 的補數了。

就拿 -499 來講,咱們要轉化成補數,就是 999 - 499 + 1 等於 501 ,看上面的排列確實用 501 來表明 - 499。

那減去一個數不就是加上一個數的負數嗎?因此經過補數咱們就不須要作減法,只須要轉成補數再相加就好了!

如今咱們再換成二進制,二進制相比於十進制就更簡單了。

拿八位二進制數來講,範圍是 00000000~11111111, 對應的十進制是 0~255,但如今咱們想讓它能表示負數,前面十進制的時候咱們將 五、六、七、八、9開頭的正數來表示負數,對應於二進制咱們能夠將第一位以1開頭的做爲負數。

那此時的範圍就是:

若是你理解了上面的十進制轉化,這個二進制確定是沒問題的,這其實就是算出 2 的補數,而 2 的補數又是 1 的補數 +1。

咱們拿 125 來舉個例子,125 二進制表示是 01111101,求 1 的補數就是 11111111 - 01111101,這個減法在二進制中不須要,由於這其實就是求反,還記得上文提到的反向器嗎?

取反了以後再加一,就獲得 2 的補碼。

因此  -125 就是 10000011。

固然這一切的前提都是數字的位數須要固定,因此計算機中的位數就是固定的,超出了就會溢出,到這裏你應該能夠理解計算機中的補碼是怎麼來的,並且理解了爲何最大值 +1會變成最小值?

因此減法咱們只須要改造一下上面的加法器,給個開關表示要這個數是負數,若是是負數則進行一波反向器操做而後再 +1,以後再進行加法操做便可獲得最終的結果。

乘法和除法我就不分析了,同樣也能經過加減法來實現。

振盪器(時鐘)、鎖存器(觸發器)和計數器

固然這個和咱們所認識的計算機還差不少,如今只能進行一些很是簡陋的加減操做,別急咱們先來看看這個電路。

這個電路頗有意思,當你閉合開關的時候電路通了,此時因爲電磁效應可動棒被吸了下來,電路就斷了,斷了以後磁性消失了可動棒又移了上去,這樣電路又通了,如此往復

這種電路叫振盪器,這是一個很關鍵的東西,記住它。

它的來回振盪其實就是在輸出 0 和 1 的交替序列,畫成圖以下所示:

隨着時間的變化在 0 和 1之間交替變化,所以也稱之爲時鐘。

一個變化循環所須要的時間稱之爲週期,頻率是週期的倒數,若是週期是 0.05 秒,那麼頻率就是 20,每秒 20 個循環,用赫茲來做其單位,因此就是 20 Hz。

咱們再來看下這個電路。

此時燈泡是不亮的。當上面的開關閉合後,左邊的或非門輸出 0 ,右邊的或非門輸出是 1,所以燈泡亮了。神奇的地方來了,此時你斷開上面的開關,燈泡依然是亮的,由於左邊的或非門輸出仍是 0,而或非門只要有一個輸入是 1,輸出就是 0 。

此時若是閉合下面的開關,燈泡就會熄滅,再斷開下面的開關燈泡仍舊不亮。

能夠看到這個電路是有記憶功能的,你看若是你發現此時的燈泡是亮的,你就能推斷上一次閉合的是上面的開關,若是此時燈泡是暗的那麼上次閉合的就是下面的開關!

這種電路叫觸發器,其實上面的開關就等於置位(set),下面的開關等於復位(Reset),因此這也叫 R-S觸發器。

不過更有用的電路應該能記住某個特定時間點的上上一個信號是 0 是 1

因此還須要搞個保持位,使得保持位關了以後,上下兩個開關隨意撥動都不影響以前保持結果(下面的圖復位和置位位置和咱們電路圖是相反了,不過沒影響同樣的)。

其實就是當保持位 0 的時候,復位和置位經過與門的輸出確定是 0 根本影響不到以前的結果。

可是這樣就有三位輸入了,比較麻煩。從上面的觀察來看有意義的輸入實際上是上面開下面關,或者上面關下面開,因此必定是相反的。因此搞個反向器這樣就只有兩個輸入了。

這個叫電平觸發的D型觸發器,D表示 Data,數據的輸入。電平觸發就是當保持位爲某一個特定電平時 (例子是 1),觸發器就會保存數據端的輸入值。

理解了保持位以後,咱們須要引入時鐘(標誌爲 clk),一個有規律的來回變化的時鐘,當時鍾從 1 切換到 0 的時候上一次操做的內容就被保存了,因此把保持位的輸入替換成時鐘輸入。

這樣的電路叫作電平觸發的D型鎖存器,它表示電路鎖存住一位數據,並保持到未來使用,它也稱之爲 1 位存儲器。

有了 1 位存儲器,那多位存儲器就很簡單了,就是將多個鎖存器合在一塊兒,以下圖是八位鎖存器。

這裏還須要提一下邊沿觸發器,不一樣於電平觸發器的是邊沿觸發器是在 0 變成 1 的瞬間記錄結果,像電平觸發器是在 1 的時候每一個結果都會被覆蓋性的記住,在某些場景下邊沿觸發器的瞬時性更合適。

電路圖以下,由兩級 R-S 觸發器連接而成,其實這種電路看不的很亂以爲很複雜沒事,知道結果就好了。

簡化的畫法以下:

而後咱們再來看下這個電路:

將振盪器的輸出做爲時鐘的輸入,而後反向 Q 端(上圖中下面的Q表明反向Q,圖少了一橫)的輸入又做爲 D 的輸入。

出來的波形圖是這樣的,能夠看到 Q 的輸出頻率是時鐘的一半,因此這種電路稱爲分頻器。

而分頻器的輸出又能夠是下一個分頻器的輸入,咱們再來看下這個圖:

出來的波形圖是這樣的:

再填上 0 和 1:

從 Q3 開始每一列從下往上看,是否是 0000、000一、0010.... 這就是計數器,把 8 個集成一下放在黑盒中,就構成了 8 位的計數器。

固然這個計數器是異步的,後面的得等前面的通知,比較不許確,因此更好的是同步計數器,不過比較複雜,這裏就不介紹了。

簡單組裝一下

至此咱們已經有了加法器、振盪器(時鐘)、鎖存器(觸發器)和計數器,接下來咱們就開始組裝一下它們。

好比如今咱們有一個燈泡,想測試一下八個鎖存器,八個鎖存器的話那麼須要 3 個開關來表示具體選擇哪一個鎖存器,2 的 3 次方等於8。

中間的黑盒確定是拿來選擇的,經過開關來控制通路,比較複雜我以爲稍微看看就行,反正就是電路選擇。

輸入的話也不用直接用八個,因此也搞個三個開關。

內部構造我就不貼了,也和選擇器同樣複雜,這叫譯碼器,最終完整電路圖以下:

而是S0、S一、S2 其實就是地址,經過地址來選擇寫入哪一個鎖存器中,而且對應輸出結果,這種配置叫讀/寫存儲器,也稱爲隨機訪問存儲器即 RAM

由於它能保存信息,因此叫存儲器,由於能根據地址選擇來寫入讀取因此是隨機。

上圖電路簡化圖以下,能存儲 8 個獨立的 1 位數據。

兩個 8*1 RAM 結合一下就能表示存儲 8 個獨立的 2 位數據。

若是是經過下面這樣的組合,則能表示 16*1 RAM,那個 DI 其實就是第四根地址線,因此是 2 的 4 次方。

能夠看到 RAM 陣列的存儲容量等於 2 的地址數次方,而後注意下咱們圖是簡化了的,裏面其實有不少繼電器的,像邏輯門都是由繼電器構成的,當斷電以後電磁效應就沒了,全部的觸點都回歸原樣,這就是 RAM 爲何是易失性存儲介質的緣由。

我們如今已經把內存給搞出來了

接下來咱們的目標就是把要計算的數據輸入內存中,而後讓加法器計算了以後把結果寫回內存,而且能夠再經過內存查看結果,大體的組裝樣子以下:

而後咱們能夠將加法器和鎖存器結合起來做爲一個累加器,即每次加法的值存儲到鎖存器中並做爲下一次累加的值。

有了累加器以後,咱們能夠將存儲器的值傳到累加器中,稱爲 Load 裝載,把下一個值添加到累加器中,稱爲 Add,而後將結果保存在某個位置,稱爲 Store。

能夠經過控制面板先往存儲器裏面寫好要操做的值而且能夠經過控制面板上的燈來查看內存寫入結果,而後一開始訪問存儲器的地址爲 0000,由計數器來驅動地址的前進,而後進行相加,最終將結果存儲回 RAM 陣列中,固然也須要設置中止信號

把咱們前面定義的 Load 等操做碼,轉化爲特定的代碼來控制總體的流程(你就認爲這代碼會指示電路作某種操做,不必細想反正就是經過邏輯門組合產生的)。

這個操做碼僅是個助記符,由於地址是固定的,而且操做碼指令字節是固定長度(1個字節),因此咱們能夠在每條操做後面跟上地址,總的而言每條指令(除中止)須要 3 個字節。

簡單的看下圖,就是在存儲器地址0000處存入如下「代碼」。

而且能夠搞個 Jump 指令用來跳轉地址,能夠經過設置計數器來達成跳轉地址的功能,有了跳轉咱們就能作循環操做了。某些重複的指令只須要編寫一次,經過條件跳轉來完成循環,最終的組裝示意圖以下:

2-1 選擇器是切換計數器的地址輸入或者是計算得出的輸入,經過三個 8 位鎖存器來分別表明代碼,地址高位和低位,上圖來看可能有點繞,不理解細節也沒有關係,大體的流程仍是簡單的。

至此咱們其實已經組裝了一臺計算機了,之因此能叫計算機而不是計算器是由於它能夠根據你寫入存儲器的指令自動取指執行,而且能夠進行條件跳轉和循環執行自動中止。

計算機的處理器就是咱們上面的累加器,能夠稱之爲算數邏輯單元,即 ALU。

那個計數器就是咱們的程序計數器PC。

存儲器就是內存了,輸入就是控制面板,輸出就是控制面板上的燈。

計算機幾個核心模塊就都有了。

至於前面咱們定義的操做碼其實就是機器語言,而人類爲了好記就會搞一些助記符來標識,發展到後來就是彙編語言,而彙編語言又太麻煩了,所以又抽象搞了高級語言,好比 C、Java 等等。

最後

這篇文章最終所描述的計算機實際上是至關簡陋的,真正的計算機也確定不會這樣造的,好比不會用繼電器,線路也會用各類總線啥的搭建起來各類集成電路等等,ALU 也不會如此簡單,會有各類並行計算等等。

主要是想借此大體的說下計算機基本的運行原理和構成,由於本質上的道理是同樣的。若是要我把不少細節都說出來我也不會,我也就懂一點點點點皮毛,我也不是搞硬件的,啥模電的課我也沒上過,我就會裝裝機的水準。

本文大量藉助了《編碼的奧義》一書的例子,或者說是對此書一些章節的梳理和總結,若是對原文有興趣的能夠本身購買書籍,若是以爲囊中羞澀能夠後臺回「233」,我來幫你想一想辦法。

 

我是 yes,從一點點到億點點,咱們下篇見

 

本文分享自微信公衆號 - yes的練級攻略(yes_java)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索