- 「MoreThanJava」 宣揚的是 「學習,不止 CODE」,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 「幫助新朋友快速高質量的學習」。
- 當然 不論新老朋友 我相信您都可以 從中獲益。如果覺得 「不錯」 的朋友,歡迎 「關注 + 留言 + 分享」,文末有完整的獲取連結,您的支援是我前進的最大的動力!
Part 1. 原來,我們是這樣記數的
本節內容節選自下方 參考資料 1
在討論「二進位制」和「CPU 如何工作」之前,我們先來討論一下我們生活中最稀疏平常的 數字,我們與之頻繁地打交道:一個約定的時間、一件商品的價格、一個人的身高....卻很少有人細細想過,這些數字是如何表達出來的?為什麼你理所當然地把 1024
理解為「一千零二十四」而不是別的含義?
也許你從未想過,在這簡單的記數中,沉澱著人類的大智慧。
一進位制計數法
早在數字的概念產生之前,人類就學會了使用樹枝、石子、貝殼等自然界隨處可見的小物件表示獵物的、果實的、部落人口的數量。比如在某個角落堆上一堆石子,每打到 1 只獵物,就扔 1 顆石子進去,每吃掉 2 只獵物,就從中取走 2 顆石子。他們並不在意石子的總數,只是時不時地瞅一眼,心底大致有數。
其實這是一種最樸素的記數方式,數學家稱之為 一進位制記數法(unary numeral system)。我們把它符號化一下,比如用斜槓 /
來表示:
1
就是/
;2
就是//
;4
就是////
;
好像沒毛病,我們平時掰手指用的就是這種記數法,但數字一大,場面就要失控了。
符值相加記數法
為了解決記錄大數的問題,於是我們得發明一些其他符號來表示更大的數值,比如用橫槓 -
表示 10
,用十字 +
表示 100
。那麼:
16
就是-//////
;32
就是---//
;128
就是+--////////
;
漂亮....這種靠符號型別和符號數量表示數字的方法被稱為 符值相加記數法(sign-value notation),古埃及和古羅馬用的都是它,只不過符號各不相同。
古埃及的記數符號:
1 | 10 | 100 | 1000 | 10000 | 100000 | 1000000 |
---|---|---|---|---|---|---|
1024
在古埃及就寫作:
你會發現,符值相加記數法的一大優點是,符號的順序可以任意打亂,數字含義不受影響。我國藏族曾用石子表示 1
、木棍表示 10
、果核表示 100
、蠶豆表示 1000
、瓦片表示 10000
,那麼,當你把 1
顆蠶豆、2
根木棍和 4
顆石子胡亂地攥在手裡,別人依然知道它們是 1024
。
古羅馬的做法略有不同,他們對五進位制情有獨鍾:
1 | 5 | 10 | 50 | 100 | 500 | 1000 |
---|---|---|---|---|---|---|
I | V | X | L | C | D | M |
這些符號沿用至今,想必大家(至少對前 3
個)都比較熟悉,許多鐘錶仍保留著使用羅馬數字的習慣,1~12
分別表示為:I
、II
、III
、IV
、V
、VI
、VII
、VIII
、IX
、X
、XI
、XII
。你會發現,羅馬記數法是符值相加記數法的變種,因為它不光「相加」,還「相減」。這種方式就不允許符號亂序了,IV
和 VI
表示的是不同的數字。
那羅馬人何苦要使用這種更復雜的記數法呢?無非是為了讀寫方便。同樣表示 9
,IX
比 VIIII
更簡潔。
其實有一種更好使的方法——用另外一些列符號來表示符號的數量。比如用 A
表示 1
個符號,用 B
表示 2
個符號,以此類推,用 I
表示 9
個符號。
如此,上文表示 256
的 ++-----//////
就可以寫作 B+E-F/
。你一定感覺莫名其妙,這種寫法哪裡方便了。其實中文的數字表示就是這種形式,只不過我們用得太習慣了,以至於沒有發現。
在中文中,個
、十
、百
代替了 /
、-
、+
,而 一
、二
、三
代替了 A
、B
、C
。256
就寫作 二百五十六個
,個
比較累贅,我們通常把它省略了。
其實像日語、英語用的也同樣是這種記數法,簡潔、優雅。
美中不足的是,這種形式雖便於讀寫,卻不便於計算。中國古人為算籌和算盤這類經典算具搭建起廣闊的舞臺,卻沒給筆算留出一席之地。想象一下,如果讓你把這些漢字寫在草稿紙上,列個豎式,你的內心一定非常彆扭。
位值制記數法
公元5世紀,印度數學家阿耶波多(Aryabhata 476–550)創立了現在廣泛使用的 位值制記數法(positional notation/place-value notation),該記數法使用的主要符號,是同為印度人發明的阿拉伯數字:0
、1
、2
、3
、4
、5
、6
、7
、8
、9
。
與符值相加記數法類比,位值制中的 1
、2
、3
代替的是 A
、B
、C
,那 /
、-
、+
呢?是 靠阿拉伯數字的位置來表示的。眾所周知,最右位相當於 /
,次右位相當於 -
。靠每個位置上的數值來表示數字,故名位值制。
嚴謹的數學家用一種多項式高度概括了位值制記數法的本質,在十進位制中,這個多項式是這樣的:
這是一個 n
位十進位制數,ai 就是第 i 位上的數值。為便於直觀理解,舉個 1024
的例子吧:
由於我們熟悉了十進位制,這樣費心費力的展開可能會讓你覺得好笑,但當我們把它推廣到其他進位制時,這個多項式的價值就體現了出來。n 位 b 進位制數的位值製表示:
1024
用二進位制怎麼表示?
因此,1024
的二進位制寫作 10000000000
。
除了最普遍的十進位制和計算機中的二進位制,常見的還有七進位制(如 1
周 7
天)、十二進位制(如 1
年 12
個月)、十六進位制(如古代 1
斤 16
兩)、六十進位制(如六十甲子)等等,只要有意義,任何進位制都可以為你所用。
非標準位值制
在上述的多項式中,如果 ai 或 b 的取值奇葩一點,就形成了 非標準位值制(non-standard positional numeral systems),這類記數法往往應用於專業領域,很難在日常生活中見到。比如標準位值制中的三進位制 ai 的取值為 0
、1
、2
,但在一種名為平衡三進位制(balanced ternary)的非標準位值制中,ai 取 -1
、0
、1
,蘇聯曾使用這種進位制研發電子計算機。
Part 2. 二進位制簡介
至此,你對「二進位制」應該會感覺親切了些,它只是一種數制而已,本質上與我們熟悉的十進位制沒有很大的差別,我們這一 Part 來稍微理解一下二進位制。(至於電腦為什麼使用二進位制我們在下一 Part 中介紹)
二進位制的基本運算
十進位制中的那些基本運算原則,二進位制中同樣適用,只不過需要稍加變幻而已,下面我們分別就加、減、乘、除四則運算來介紹。
二級制加法
根據「逢二進一」規則,二進位制數加法的法則為:
0+0=0
0+1=1+0=1
1+1=0 (進位為1)
1+1+1=1 (進位為1)
例如:1101
和 1011
相加過程如下:
二進位制數的減法
根據「借一有二」的規則,二進位制數減法的法則為:
0-0=0
1-1=0
1-0=1
0-1=1 (借位為1)
例如:1101
減去 1011
的過程如下:
二進位制的乘法
二進位制數乘法過程可仿照十進位制數乘法進行。但由於二進位制數只有 0
或 1
兩種可能的乘數位,導致二進位制乘法更為簡單。二進位制數乘法的法則為:
0×0=0
0×1=1×0=0
1×1=1
例如:1001
和 1010
相乘的過程如下:
二進位制的除法
二進位制數除法與十進位制數除法很類似。
例如:100110
÷ 110
的過程如下:
二進位制為什麼能表示所有的資料
因為編碼規定。
之前我們有說到,所有儲存的程式和資料在計算機中都被描述為 檔案,也就是說我們能夠知道當前的資料集合被期望的用途是什麼,也就能夠找到對應的 處理器 來正確處理當前的資料。
例如儲存文字
拿文字舉例,為了讓一串 0
、1
能夠代表特定的文字,人們規定使用一個位元組中的七位來表達特定的文字, 這就是大名鼎鼎 ASCII (American Standard Code for Information Interchange) 碼,ASCll 碼能夠表達 27=128 種字元(編碼從 0~127
),對於 26
個英文字母和一些常用的可列印字元,這完全足夠了:
可是,世界文化是多元的,面對類似漢字這樣的象形文字,ASCll碼錶用起來自然是捉襟見肘。
窮則思變,一個位元組不行,那就兩個位元組,這就是大名鼎鼎的 Unicode 碼,不難看出,Unicode 碼有 216=65536 種表示方式,這樣就足以表達一些常用的字元了,值得一提的是,Unicode 碼算是在 ASCll 碼上的一種擴充,其第 0~127
個編碼字元與 ASCll 碼錶一模一樣。
再比如圖片
這裡涉及一點點物理知識,話說很久以前,牛頓通過三稜鏡把白色的光分解成七種不同顏色的光,後來又通過各種實驗發現紅、綠、藍三種顏色的光是無法被分解的,因此我們就稱為紅藍綠為光的三原色。
至此人類已經知道了:可以通過組合不同比例的紅、綠、藍三種顏色來獲得各種各樣的顏色,那麼我們就可以在計算機上模擬了。現在的計算機,一般使用 32
位來表示顏色,32
位平分給四個分量,也就是每個分量 8
位。
為啥是四個顏色分量?
因為顏色模型中有一個 alpha 值,用來表示透明度,這一點我們先不考慮。總之三種顏色,每個使用 8
位來表示的話,我們就能夠表示 256 * 256 * 256 = 16777216
種顏色了,已經足夠基礎的使用了。
先來看一張圖片:
這張影像的尺寸是 600px * 664px
(px 是一種圖片單位,中文名稱為畫素,你可以暫時理解為一個點)。我們把它放大一下,如下圖所示:
看見了嗎?實際上,大部分影像(你拍攝的照片、你掃描的圖片、你使用 iPad 畫的圖片等等...)都是點陣圖檔案,點陣圖就是由畫素點構成的,它就像是一個網格一樣,每個格子裡面填一個顏色。(除了點陣圖外,還有一種圖是向量圖,它描述的是形狀而非網格)
OK,我想你已經能理解影像是由畫素點組成的了(事實上我們的顯示器也是),我們只需要在編碼中附帶上一些額外的資訊,例如影像有多大的尺寸、時間、作者、顏色深度、是否支援透明度之類的就能夠對影像進行正確表示了。(視訊可以簡單理解成一張張連續不斷的圖片)
要讓顯示器正確顯示圖片或者視訊,只需要讓顯示器上每個畫素顯示特定的顏色就好了。
Part 3. 為什麼是二進位制?
可為什麼一定是二進位制呢?使用人類習慣的十進位制不好嗎?
理由一:物理上易於實現
計算機依靠電力工作,這也就意味著需要將數字訊號對映到電訊號,實現這種對映最簡單的方法是:
- 0 - 沒有電(0 V)
- 1 - 有點(5 V)
二進位制在技術上最容易實現。這是因為具有兩種 穩定狀態 的物理器件很多,如閘電路的導通與截止、電壓的高與低等,而它們恰好可以對應表示 “1” 和 “0” 這兩個數碼。假如採用十進位制,那麼就要製造具有 10
種 穩定狀態 的物理電路,而這是非常困難的。
理由二:機器可靠性高
為什麼使用更復雜的數字系統是一個問題?
假設我們使用三元(3 位數字)數字系統涉及計算機,如果我們具有從 0 V
到 5 V
的電壓,那麼我們可以進行以下的對映:
- 0 - 0 V;
- 1 - 2.5 V;
- 2 - 5 V;
看起來合理吧?但是,想象一下,我以 2.5 V
的電壓傳送了一個數字。但是由於電路中的一些噪聲,我在輸出端得到 2.3 V
的電壓,因此將其視為 0
。結果是?
有人給我傳送了 1
,但我將其視為 0
。資料丟失可是一個非常嚴重的問題。
使用二進位制則可靠得多,由於電壓的高和低、電流的有和無等都是一種 質的變化,兩種物理狀態穩定、分明,因此,二進位制碼傳輸的抗干擾能力強,鑑別資訊的可靠性高。
為什麼計算機系統必須有時鐘
建立數字系統的目的是 僅在某些時間點測試開/關(二進位制)值,這使電線(或其他裝置)有時間更換。這就是計算機系統有時鐘的原因。
時鐘會週期性地進行訊號的測量,圖中所示的 T1 和 T2 就是可以測量訊號的時間點。
時鐘利用所有這些時間點來保持同步。更快的時鐘意味著每秒可以對電線進行更多次測試,並且整個系統執行得更快。2 GHz
處理器每秒檢查二進位制值 20
億次。在這些時間之間,允許值改變並穩定下來。處理器晶片速度越快,每秒可以測試的次數就越多,每秒可以做出的決策就越多。
理由三:運算規則簡單
數學推導已經證明,對 N
進位制數進行算術求和或求積運算,其運算規則各有 N(N+1)/2
種。如採用十進位制,則 N=10
,就有 55
種求和或求積的運算規則;而採用二進位制,則 N=2
,僅有 3
種求和或求積的運算規則,以上面提到的加法為例:
0+0=0,0+1=1 (1+0=1),1+1=10
因而可以大大簡化運算器等物理器件的設計。
理由四:邏輯判斷方便
採用二進位制後,僅有的兩個符號 “1” 和 “0” 正好可以與邏輯命題的兩個值 “真” 和 “假” 相對應,能夠方便地使用邏輯代數這一有力工具來分析和設計計算機的邏輯電路。
雖然在 1950 年代就造出了更加高效的三元計算機,但在效率和複雜度的取捨上,始終抵不過二進位制。二進位制仍然在當今世界中長期存在。
Part 4. CPU 的實際工作方式
上面我們瞭解到計算機以二進位制的形式執行,它們只有兩種狀態:開(1)和關(0),為了執行二進位制計算,我們需要採用一種特殊的電子元器件,稱為 「電晶體」。暫時我們把它理解為一種開關吧,通電就開啟,沒電流通過就關閉。
利用"開關"搭建邏輯電路
我們知道,給電燈通上電,它就會亮:
於是,結合上開關,我們就能搭建出最基礎的 與門 和 或門。
與門
該電路的邏輯是:只有當 A 和 B 同時開啟時,LED 燈才會亮,也就是認為輸出 1,我們可以利用電訊號來簡單模擬一下:
A | B | Y |
---|---|---|
0 | 0 | 0 |
1 | 0 | 0 |
0 | 1 | 0 |
1 | 1 | 1 |
或門
該電路的邏輯是:當 A 或者 B 開啟時,LED 燈就會亮,也就是認為輸出 1,我們可以利用電訊號來簡單模擬一下:
A | B | Y |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 1 |
其他門
類似地,我們可以藉助更多的電子元器件來創造出基礎的 7
種邏輯閘電路:
這裡需要特別提一下 異或門,我們需要先知道有一種電子元器件可以利用電氣特性對 輸入取反,也就是說輸入 1
則輸出 0
,輸入 0
則輸出 1
,那麼我們就可以 簡單模擬 出異或門邏輯電路(實際會更復雜些,這裡僅展示出異或的意思):
A'
和 B'
分別表示 A
和 B
開關的反值,從圖中我們很容易知道只有當 A
、B
只存在一個輸入 1
時,整個電路才會輸出 1
。
利用邏輯閘簡單計算加法
OK,上面我們瞭解到我們能夠利用 "開關" 來模擬邏輯的運算,我們接下來試著還原一個簡單的加法運算器是如何實現的:
僅需兩個門,就可以完成基本的二進位制加法運算。上圖是利用 logic.ly
建立的半加法器,A
、B
相當於使我們計算的兩個數,最後一塊相當於是我們的數顯晶片,它的功能是根據輸入顯示數字,從上到下的引腳(也就是圖中輸入的地方,通常我們這樣稱呼)分別對應了 20=1、21=2、22=4、23=8 的輸入,沒有任何輸入時顯示為 0
,如果 引腳 1
(對應 20=1)像上圖一樣有輸入,則顯示 0 + 1 = 1
。
我們來理解一下上方的電路:
- 如果僅開啟一個輸入,但不同時開啟兩個輸入,則此處的 XOR 門(異或門)將開啟,此時對應輸入
引腳 1
,顯示數字 1
(類似於1 + 0 和 0 + 1
); - 如果兩個輸入均開啟,則 AND 門(與門)將開啟,此時對應輸入
引腳 2
,顯示數字 2
(類似於1 + 1
); - 如果沒有輸入,則 AND 門和 XOR 門都保持關閉,此時顯示
數字 0
(類似於0 + 0
);
因此,如果兩個都開啟,則 XOR 保持關閉,並且 AND 門開啟,得出正確的答案為 2
:
但這只是最基礎的半加法運算器,不是太有用,因為它只能解決最簡單的數學問題之一。但如果我們把它們兩個與另一個輸入連線,就會得到一個完整的加法器:
仔細思考幾遍,你就會得知這個三個輸入的加法器已經可以計算 3
個二進位制數字的加法運算了,我們如法炮製,可以通過連線更多的"進位"來使這個加法器能夠運算更多的數,這當然也意味著這個計算鏈條更長。
大多數其他數學運算都可以加法完成。乘法只是重複加法,減法可以通過一些奇特的位反轉來完成,而除法只是重複減法。並且,儘管所有現代計算機都具有基於硬體的解決方案以加快更復雜的操作,但從技術上講,您可以使用完整的加法器來完成全部操作。
匯流排和記憶體
現在,我們的計算機只不過是一個計算器,它記不住任何內容也對輸出沒有任何操作,上述電路只是接了一個顯示單元而已。
上面展示的是一個儲存單元。它使用了大量的 NAND 門,並且在實際生產中,根據儲存技術的不同,它們可能會大不相同,但其功能是相同的。
您給它一些輸入,並開啟“寫”位(Write
輸入 1
),它將把輸入儲存在單元內。這不僅是一個儲存單元,因為我們還需要一種從中讀取資訊的方法。這是通過一個使能器完成的,該使能器是「儲存器」中每個位的“與”門的集合,所有位都與另一個輸入(即“讀取”位)繫結在一起。寫入和讀取位通常也稱為“設定”(set
)和“啟用”(enable
)。
上面整個儲存單元都包裹在所謂的暫存器中。這些暫存器連線到 匯流排,匯流排是圍繞整個系統執行的一束電線,並連線到每個元件。即使現代計算機也具有匯流排,儘管它們可能具有多個匯流排以提高多工處理效能。
每個暫存器仍有一個讀寫位,但是在這種設定下,輸入和輸出是一樣的。這實際上很好。例如:如果要將 R1 的內容複製到 R2,則應開啟 R1 的讀取位,這會將 R1 的內容壓入匯流排。當讀取位開啟時,您將開啟 R2 的寫入位,這會將匯流排內容複製到 R2 中。
暫存器也用於製作 RAM。RAM 通常佈置在網格中,並且導線有兩個方向:
解碼器採用二進位制輸入並開啟相應的編號線。例如,11
在二進位制數中是 3
,即最高的 2
位數字,因此解碼器將開啟最高的線路。每個路口都有一個暫存器。所有這些都連線到中央匯流排以及中央寫入和讀取輸入。只有跨暫存器的兩條導線也都開啟時,讀和寫輸入才會開啟,從而有效地允許您選擇要從中進行讀寫的暫存器。同樣,現代 RAM 要複雜得多,但是此設定仍然有效。
時鐘,步進器和解碼器
暫存器無處不在,是在 CPU 中移動資料並將資訊儲存在 CPU 中的基本工具。那麼,是什麼告訴他們移動資料的呢?
時鐘是 CPU 核心中的第一個元件,它將按設定的時間間隔(以赫茲或每秒週期為單位)關閉和開啟。這就是您看到的最直觀的 CPU 速度指標。
時鐘具有三種不同的狀態:基本時鐘,使能時鐘和設定時鐘。基本時鐘將開啟半個週期,另一半關閉。使能時鐘用於開啟暫存器,並且需要更長的時間才能確保資料被使能。設定時鐘必須始終與使能時鐘同時開啟,否則可能會寫入錯誤的資料。
時鐘連線到步進器,步進器將從 1
到最大步數進行計數,並在完成後將自身重置為 1
。時鐘還連線到 CPU 可以寫入的每個暫存器的 AND 門:
這些 “與” 門還連線到另一個元件的輸出,即指令解碼器。指令解碼器接受 SET R2 TO R1
之類的指令,並將其解碼為 CPU 可以理解的內容。它有自己的內部暫存器,稱為“指令暫存器”,該暫存器儲存了當前操作。它的精確程度取決於您正在執行的系統,但是一旦解碼,它將開啟正確的設定並啟用正確暫存器的位,這些暫存器將根據時鐘觸發。
程式指令儲存在 RAM(或現代系統中的 L1 快取記憶體,更靠近 CPU)中。由於程式資料與其他所有變數一樣都儲存在暫存器中,因此可以隨時對其進行操作以在程式中跳轉。這就是程式通過迴圈和 if
語句獲取結構的方式。跳轉指令將指令解碼器正在讀取的儲存器中的當前位置設定到其他位置。
一切如何配合
現在,我們對 CPU 工作原理的有了一些基本的瞭解。主匯流排跨越整個系統,並連線到所有暫存器。完整的加法器以及其他一系列運算都打包在算術邏輯單元或 ALU 中。該 ALU 將與匯流排建立連線,並且還將具有自己的暫存器來儲存正在操作的第二個數字。
為了執行計算,將程式資料從系統 RAM 載入到控制部分。控制部分從 RAM 中讀取兩個數字,將第一個數字載入到 ALU 的指令暫存器中,然後將第二個數字載入到匯流排上。同時,它向 ALU 傳送指令程式碼,告知其操作方法。然後,ALU 執行所有計算,並將結果儲存在另一個暫存器中,CPU 可以從該暫存器中讀取該值,然後繼續該過程。
參考資料
- 原來,我們是這樣記數的 - https://www.jianshu.com/p/58844323e4fb
- 二進位制數的運算方法 - https://www.jianshu.com/p/560aba49c9a4
- 文字,圖片,視訊,音訊的二進位制表示 - https://blog.csdn.net/c46550/article/details/91040925
- 知乎 - 計算機只認識0和1但是怎麼表示影像和影視等等眾多應用的? | @kross - https://www.zhihu.com/question/36269548
- Introduction to binary numbers - https://pmihaylov.com/intro-binary-numbers/
- What is Binary, and Why Do Computers Use It? - https://www.howtogeek.com/367621/what-is-binary-and-why-do-computers-use-it/
- CPU 是怎樣認識程式碼的? | 知乎 - https://www.zhihu.com/question/348237008/answer/843382847 | @Zign
- HTG Explains: How Does a CPU Actually Work? - https://www.howtogeek.com/367931/htg-explains-how-does-a-cpu-actually-work/
往期推薦
- 「MoreThanJava」當大學選擇了計算機之後應該知道的
- 「MoreThanJava」計算機發展史—從織布機到IBM
- 「MoreThanJava」計算機系統概述
- 媽媽再也不擔心我面試被Redis問得臉都綠了
- 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
- 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!
非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!
創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!