前端開發人員中,有至關大比例的同窗不是科班出來的,因此對於基本的科班必修課
,例如:計算機組成原理
、操做系統
、計算機網絡
、數據結構和算法
等知識接觸很少。前端
當你越深刻學習,越會發現這些知識的重要性。算法
好比你們都知道js裏面0.1 + 0.2
是不等於0.3
的,爲何呢?這就牽扯到計算機組成原理中浮點數
的表示方法,以及浮點數
的加減運算(正文會有白話版解答)。編程
又例如從鍵盤輸入a+b
這個指令,如何經過cpu
的調度輸出到屏幕上
呢?這就涉及到馮諾依曼體系
,若是你是編程人員,都不清楚數據從鍵盤到屏幕的基本流向
,恐怕很難說本身是合格的編程人員吧。數組
本文是一個計算機組成原理
最基本的入門文章,我以爲前端沒有必要那麼深刻這個專題,具有基本的計算機組成原理
的常識便可。緩存
首先,計算機最基本的5大組成部分以下圖,分別爲:輸入設備
(好比鍵盤), 存儲器
(好比內存), 運算器
(cpu), 控制器
(cpu), 輸出設備
(顯示器)。bash
工做原理以下網絡
當咱們輸入數據的時候,cpu裏的控制器
會讓輸入設備
把這些指令存儲到存儲器
(內存)上。數據結構
控制器分析指令以後, 此時讓存儲器
把數據發送到運算器
裏(控制器
和運算器
都在cpu
裏面)數據結構和算法
這裏須要注意,存儲器
既能存儲數據
,還能存儲指令
(後面會講指令是什麼,怎麼執行) 編程語言
從接下來,咱們更近一步,看看計算機內部,CPU是怎麼跟存儲器交互的。
CPU中比較重要的兩個部件是運算器
和控制器
,咱們先來看看運算器的主要做用
如上圖,運算器裏最重要的部件是ALU
,中文叫算術邏輯單元
,用來進行算術
和邏輯運算
的。其它的MQ
,ACC
這些咱們不用管了,是一些寄存器
。
控制器中最重要的部件是CU
(控制單元),只要是分析指令
,給出控制信號
。
IR
(指令寄存器),存放當前須要執行的指令
PC
存放的指令的地址。
首先,是取指令的過程以下
PC
,也就是存放指令地址的地方,咱們要知道下一條指令是什麼,就必須去存儲器拿,CPU
才知道接下來作什麼。PC
去了存儲器的MAR
拿要執行的指令地址,MAR
(存儲器裏專門存指令地址的地方)MAR
去存儲體內拿到指令以後,將指令地址放入MDR
(存儲器裏專門存數據的地方)MDR
裏的數據返回到IR
裏面,IR
是存放指令的地方,咱們把剛纔從存儲體裏拿的指令放在這裏而後,分析指令,執行指令的過程以下
IR
將指令放入CU
中,來分析指令,好比說分析出是一個取數指令,接着就要執行指令了(這裏取數指令,其實就是一個地址碼,按着這個地址去存儲體取數據)IR
就會接着去找存儲體裏的MAR
(存儲地址的地方),MAR
就根據取數指令裏的地址嗎去存儲體裏去數據MDR
(存放數據的地方)MDR
裏的數據放到運算器的寄存器裏,這裏的取指令的過程結束了。來個插曲,咱們知道數據在內存
裏是二進制
存着,也就是0和1
, 0和1
怎麼用表示呢?
咱們拿其中一種存儲0和1的方式來講明
咱們看看機器語言,怎麼表示存放一個數的指令,例以下圖
咱們來看二進制代碼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.3這個小節會解釋爲何0.1 + 0.2 等於0.3
例如2
進制101.1
如何轉化爲10
進制。(有些同窗以爲能夠用parseInt('101.1', 2)
,這個是不行的,由於parseInt
返回整數)
轉化方法以下:
上圖的規則是什麼呢?
二進制
的每一個數去乘以2
的相應次方,注意小數點後是乘以它的負相應次方
。 再舉一個例子你就明白了,
二進制1101
轉爲十進制
JS
裏面能夠用toString(2)
這個方法來轉換。若是要用通用的方法,例如:將十進制數(29)
轉換成二進制數, 算法以下:
方式是採用「乘2取整,順序排列」法。具體作法是:
咱們具體舉一個例子
如: 十進制 0.25 轉爲二進制
0.25 * 2 = 0.5
取出整數部分:0
0.5 * 2 = 1.0
取出整數部分1
即十進制0.25
的二進制爲 0.01
( 第一次所獲得爲最高位,最後一次獲得爲最低位)
此時咱們能夠試試十進制0.1
和0.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.1
和0.2
都沒法完美轉化位二進制,因此它們相加固然不是0.3
了
首先,什麼是定點數呢?
如上圖,舉例純整數的二進制1011
和-1011
,若是是整數
,符號位用0
表示,若是是負數
符號爲用1
表示
同理,純小數表示舉例以下:
那若是不是純小數
或者純整數
,該怎麼表示呢?
好比10.1
, 能夠乘以一個比例因子,將10.1 ---> 101
比例因子是10
, 或者10.1 ---> 0.101
比例因子是100
定點數很簡單,接下來咱們介紹浮點數,再JS裏面,數字都是用雙精度的浮點數
,因此學習浮點數對咱們理解JS的數字有幫助。
浮點數怎麼表示呢?
上面是十進制
的科學計數法,從中咱們須要瞭解幾個概念,一個是尾數
,基數
和階碼
尾數
必須是純小數,因此上圖中1.2345
不知足尾數的格式,須要改爲0.12345
基數
,在二進制裏面是2
階碼
就是多少次方因此浮點數
的通用
表示格式以下:
這裏須要注意的是,浮點數的加減運算,並非像咱們上面介紹的那樣簡單,會通過如下幾個步驟完成
這些名詞你們感興趣的話,能夠去網上查詢,咱們只要瞭解到浮點數加減運算
很麻煩就好了,但若是你要作一個浮點數運算的庫,你確定是要徹底掌握的。
先看下圖
(說明一下,MDR
和MAR
雖然邏輯上屬於主存,可是在電路實現
的時候,MDR
和MAR
離CPU
比較近)
上圖是在執行一串代碼,能夠理解位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
}
複製代碼
咱們能夠發現
cahce
就是局部性原理
的一個應用
空間局部性
:在最近的將來要用到的信息(指令
和數據
),極可能與如今正在使用的信息在存儲空間
上是鄰近的時間局部性
:在最近的將來要用到的信息,極可能是如今正在使用的信息
能夠看到cache
一次性取了a[0]
到a[9]
存儲體上的數據,只須要1000ns
,由於Cache
是高速存儲器
,跟cpu
交互速度就比cpu
跟主存
交互速度快不少。
接下里,進入最後一節(略過對總線知識的學習),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設備工做
本文完結,但願你們點個贊,比心💗。