【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

lio-mengxiang發表於2020-02-23

前端開發人員中,有相當大比例的同學不是科班出來的,所以對於基本的科班必修課,例如:計算機組成原理作業系統計算機網路資料結構和演算法等知識接觸不多。

當你越深入學習,越會發現這些知識的重要性。

比如大家都知道js裡面0.1 + 0.2不等於0.3的,為什麼呢?這就牽扯到計算機組成原理中浮點數的表示方法,以及浮點數的加減運算(正文會有白話版解答)。

又例如從鍵盤輸入a+b這個指令,如何通過cpu的排程輸出到螢幕上呢?這就涉及到馮諾依曼體系,如果你是程式設計人員,都不清楚資料從鍵盤到螢幕的基本流向,那是時候看看這篇'十全大補文'了

本文是一篇計算機組成原理最基本的入門文章,我覺得前端沒有必要那麼深入這個專題,掌握基本的計算機組成原理的常識即可。

1、計算機的工作原理

首先,計算機最基本的5大組成部分如下圖,分別為:輸入裝置(比如鍵盤), 儲存器(比如記憶體), 運算器(cpu), 控制器(cpu), 輸出裝置(顯示器)。

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

工作原理如下

1.1 控制器 ---> 控制輸入裝置 ----> 指令流向記憶體

當我們輸入資料的時候,cpu裡的控制器會讓輸入裝置把這些指令儲存到儲存器(記憶體)上。

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

1.2 控制器分析指令 ---> 控制儲存器 ---> 把資料送到運算器

控制器分析指令之後, 此時讓儲存器把資料傳送到運算器裡(控制器運算器都在cpu裡面)

這裡需要注意,儲存器既能儲存資料,還能儲存指令

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

1.3 控制器控制運算器做資料的運算 並且將運算結果返回儲存器

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

1.4 控制器控制儲存器將結果返回給輸出裝置

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

從接下來,我們更近一步,看看計算機內部,CPU是怎麼跟儲存器互動的。

2、CPU及其工作過程

CPU中比較重要的兩個部件是運算器控制器,我們先來看看運算器的主要作用

2.1 運算器主要部件

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

如上圖,運算器裡最重要的部件是ALU,中文叫算術邏輯單元,用來進行算術邏輯運算的。其它的MQ,ACC這些我們不用管了,是一些暫存器

2.2 控制器主要部件

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

控制器中最重要的部件是CU(控制單元),只要是分析指令,給出控制訊號

IR(指令暫存器),存放當前需要執行的指令

PC存放的指令的地址。

2.3 舉例 - 取數指令執行過程

首先,是取指令的過程如下

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

  • 第一步,PC,也就是存放指令地址的地方,我們要知道下一條指令是什麼,就必須去儲存器拿,CPU才知道接下來做什麼。PC去了儲存器的MAR拿要執行的指令地址,MAR(儲存器裡專門存指令地址的地方)
  • 第二步和第三步,MAR去儲存體內拿到指令之後,將指令地址放入MDR(儲存器裡專門存資料的地方)
  • 第四步MDR裡的資料返回到IR裡面,IR是存放指令的地方,我們把剛才從儲存體裡拿的指令放在這裡

然後,分析指令,執行指令的過程如下

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

  • 第五步, IR將指令放入CU中,來分析指令,比如說分析出是一個取數指令,接著就要執行指令了(這裡取數指令,其實就是一個地址碼,按著這個地址去儲存體取資料)
  • 第六步,第七步 IR就會接著去找儲存體裡的MAR(儲存地址的地方),MAR就根據取數指令裡的地址嗎去儲存體裡去資料
  • 第八步,取出的資料返回給MDR(存放資料的地方)
  • 第九步,MDR裡的資料放到運算器的暫存器裡,這裡的取指令的過程結束了。

來個插曲,我們知道資料在記憶體裡是二進位制存著,也就是0和1, 0和1怎麼用表示呢?

我們拿其中一種儲存0和1的方式來說明

  • 電容是否有電荷,有電荷代表1,無電荷代表0
  • 如下圖
    【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

3、計算機程式語言

我們看看機器語言,怎麼表示存放一個數的指令,例如下圖

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!
我們來看二進位制程式碼 0000,0000,000000010000

  • 其中第一個0000,表示的是組合語言裡的LOAD,也就是載入,載入什麼呢
  • 載入地址000000010000上的資料到第二個0000(暫存器的位置)。

接下來,我們看看如果是組合語言怎麼表示

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

LOAD A, 16意思是將儲存體內的16號單後設資料,放到暫存器地址A中 ADD C, A, B意思是將暫存器裡的A,B資料相加,得到C STORE C, 17意思是將暫存器裡的資料存到儲存體17號單元內

最後,我們看看怎麼用高階語言表示

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

高階語言是不是很簡單,就一個a+b,你都不用去考慮暫存器儲存體這些事。

這部分的總結

高階語言一般有兩種方式轉換為機器語言

  • 一種是直接藉助編譯器,將高階語言轉換為二進位制程式碼,比如c,這樣c執行起來就特別快,因為編譯後是機器語言,直接就能在系統上跑,但問題是,編譯的速度可能會比較慢。
  • 一種是解釋性的,比如 js,是將程式碼翻譯一行成機器語言(中間可能會先翻譯為彙編程式碼或者位元組碼),解釋一行,執行一行

需要注意的是,按照第一種將大量的高階程式碼翻譯為機器語言,這其中就有很大的空間給編譯器做程式碼優化,解釋性語言就很難做這種優化,但是在v8引擎中,js還是要被優化的,在編譯階段(程式碼分編譯執行兩個階段)會對程式碼做一些優化,編譯後立即執行的方式通常被稱為 JIT (Just In Time) Comipler

4、進位制轉換

接下來4.3這個小節會解釋為什麼0.1 + 0.2 等於0.3

4.1 二進位制如何轉化為十進位制

例如2進位制101.1如何轉化為10進位制。(有些同學覺得可以用parseInt('101.1', 2),這個是不行的,因為parseInt返回整數)

轉化方法如下:

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

上圖的規則是什麼呢?

二進位制的每個數去乘以2的相應次方,注意小數點後是乘以它的負相應次方。 再舉一個例子你就明白了,

二進位制1101轉為十進位制

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

4.2 十進位制整數轉為二進位制

JS裡面可以用toString(2)這個方法來轉換。如果要用通用的方法,例如:將十進位制數(29)轉換成二進位制數, 演算法如下:

  • 把給定的十進位制數29除以2,商為14,所得的餘數1是二進位制數的最低位的數碼
  • 再將14除以2,商為7,餘數為0
  • 再將7除以2,商為3,餘數為1,再將3除以2,商為1,餘數為1
  • 再將1除以2,商為0,餘數為1是二進位制數的最高位的數碼

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!
其結果為:11101

4.3 十進位制小數轉為二進位制

方式是採用“乘2取整,順序排列”法。具體做法是:

  • 用2乘十進位制小數,可以得到積,將積的整數部分取出-
  • 再用2乘餘下的小數部分,又得到一個積,再將積的整數部分取出-
  • 如此進行,直到積中的小數部分為零,或者達到所要求的精度為止

我們具體舉一個例子

如: 十進位制 0.25 轉為二進位制

  • 0.25 * 2 = 0.5 取出整數部分:0

  • 0.5 * 2 = 1.0 取出整數部分1

即十進位制0.25的二進位制為 0.01 ( 第一次所得到為最高位,最後一次得到為最低位)

此時我們可以試試十進位制0.10.2如何轉為二進位制

0.1(十進位制) = 0.0001100110011001(二進位制)
十進位制數0.1轉二進位制計算過程:
0.1*2=0.2……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.2”接著計算。
0.2*2=0.4……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.4”接著計算。
0.4*2=0.8……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.8”接著計算。
0.8*2=1.6……1——整數部分為“1”。整數部分“1”清零後為“0”,用“0.6”接著計算。
0.6*2=1.2……1——整數部分為“1”。整數部分“1”清零後為“0”,用“0.2”接著計算。
0.2*2=0.4……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.4”接著計算。
0.4*2=0.8……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.8”接著計算。
0.8*2=1.6……1——整數部分為“1”。整數部分“1”清零後為“0”,用“0.6”接著計算。
0.6*2=1.2……1——整數部分為“1”。整數部分“1”清零後為“0”,用“0.2”接著計算。
0.2*2=0.4……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.4”接著計算。
0.4*2=0.8……0——整數部分為“0”。整數部分“0”清零後為“0”,用“0.2”接著計算。
0.8*2=1.6……1——整數部分為“1”。整數部分“1”清零後為“0”,用“0.2”接著計算。
……
……
所以,得到的整數依次是:“0”,“0”,“0”,“1”,“1”,“0”,“0”,“1”,“1”,“0”,“0”,“1”……。
由此,大家肯定能看出來,整數部分出現了無限迴圈。
複製程式碼

接下來看0.2

0.2化二進位制是
0.2*2=0.4,整數位為0
0.4*2=0.8,整數位為0
0.8*2=1.6,整數位為1,去掉整數位得0.6
0.6*2=1.2,整數位為1,去掉整數位得0.2
0.2*2=0.4,整數位為0
0.4*2=0.8.整數位為0
就這樣推下去!小數*2整,一直下去就行
這個數整不斷
0.0011001
複製程式碼

所以0.10.2都無法完美轉化為二進位制,所以它們相加當然不是0.3

5、定點數和浮點數

首先,什麼是定點數呢?

5.1 定點數

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

如上圖,舉例純整數的二進位制1011-1011,如果是整數,符號位用0表示,如果是負數符號為用1表示

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

同理,純小數表示舉例如下:

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

那如果不是純小數或者純整數,該怎麼表示呢?

比如10.1, 可以乘以一個比例因子,將10.1 ---> 101 比例因子是10, 或者10.1 ---> 0.101比例因子是100

定點數很簡單,接下來我們介紹浮點數,再JS裡面,數字都是用雙精度的浮點數,所以學習浮點數對我們理解JS的數字有幫助。

5.2 浮點數

浮點數怎麼表示呢?

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

上面是十進位制的科學計數法,從中我們需要了解幾個概念,一個是尾數基數階碼

  • 尾數必須是純小數,所以上圖中1.2345不滿足尾數的格式,需要改成0.12345
  • 基數,在二進位制裡面是2
  • 階碼就是多少次方

所以浮點數通用表示格式如下:

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

  • S代表尾數
  • r代表基數
  • j代表階碼

這裡需要注意的是,浮點數的加減運算,並不是像我們上面介紹的那樣簡單,會經過以下幾個步驟完成

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

這些名詞大家感興趣的話,可以去網上查詢,我們只要瞭解到浮點數加減運算很麻煩就行了,但如果你要做一個浮點數運算的庫,你肯定是要完全掌握的。

6、區域性性原理和catche(快取)

先看下圖

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

(說明一下,MDRMAR雖然邏輯上屬於主存,但是在電路實現的時候,MDRMARCPU比較近)

上圖是在執行一串程式碼,可以理解為js的for迴圈

const n = 1000;
const a = [1, 2, 3, 4, 5, 6, 7]
for(let i =0; i < n; i++) {
    a[i] = a[i] + 2
}
複製程式碼

我們可以發現

  • 陣列的資料有時候在記憶體是連續儲存的
  • 如果我們要取資料,比如從記憶體取出a[0]的資料需要1000ns(ns是納秒的意思),那麼取出a[0]到a[7]就需要1000 * 8 = 8000 ns
  • 如果我們cpu發現這是取陣列資料,那麼我就把就近的資料塊a[0]到a[7]全部存到快取上多好,這樣只需要取一次資料,消耗1000ns

cahce就是區域性性原理的一個應用

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

  • 空間區域性性:在最近的未來要用到的資訊(指令資料),很可能與現在正在使用的資訊在儲存空間上是鄰近的
  • 時間區域性性:在最近的未來要用到的資訊,很可能是現在正在使用的資訊

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

可以看到cache一次性取了a[0]a[9]儲存體上的資料,只需要1000ns,因為Cache高速儲存器,跟cpu互動速度就比cpu主存互動速度快很多。

接下里,進入最後一節(略過對匯流排知識的學習),I/O裝置的演變

7、I/O裝置的演變

I/O是什麼呢?

輸入/輸出(Input /Output ,簡稱I/O),指的是一切操作、程式或裝置與計算機之間發生的資料傳輸過程。
複製程式碼

比如檔案讀寫操作,就是典型的I/O操作。接下來我們看一下I/O裝置的演進過程

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!
在早期的計算機裡,cpu如何知道I/O裝置已經完成任務呢?比如說怎麼知道I/O裝置已經讀取完一個檔案的資料呢?CPU會不斷查詢I/O裝置是否已經準備好。這時,cpu就處於等待狀態。也就是cpu工作的時候,I/O系統是不工作的,I/O系統工作,cpu是不工作。

接著看第二階段

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

  • 為了解決第一階段CPU要等待I/O裝置序列的工作方式,所有I/O裝置通過I/O匯流排來跟CPU打交道,一旦某個I/O裝置完成任務,就會以中斷請求的方式,通過I/O匯流排,告訴CPU,我已經準備好了。

  • 但是對於高速外設,它們完成任務的速度很快,所以會頻繁中斷CPU, 為了解決這個問題,高速外設跟主存之間用一條直接資料通路,DMA匯流排連線,CPU只需要安排開始高速外設做什麼,剩下的就不用管了,這樣就可以防止頻繁中斷CPU

最後來看一下第三階段

【非科班前端】注意了! 計算機組成原理知識已送到你嘴邊!

第三階段,CPU通過通道控制部件來管理I/O裝置,CPU不需要幫它安排任務,只需要簡單的發出啟動和停止類似的命令,通道部件就會自動的安排相應的I/O裝置工作

本文完結,希望大家點個贊,比心?。

相關文章