以前寫過一篇相似的文章( 非科班前端注意了! 計算機組成原理知識已送到你嘴邊! ),可是已是1年前的事了,今年我以爲本身又成長了不少,再次總結一次,其內容豐富程度遠超上篇文章!廢話很少說!上車吧!javascript
就拿咱們立刻要講的計算機組成原理來講,咱們立刻的迭代任務,有一個同窗的任務是寫相似javascript計算器,而且帶有不少特定業務的公式計算任務,其實這個任務有不少細節問題,須要懂javascript的數字基本原理才行,好比說:javascript使用的 IEEE 754標準的64位浮點數,那麼64位浮點數天生就會有一些問題出來:css
0.3 - 0.2 == 0.1
緣由是什麼?這個須要跟產品說清楚,精度問題怎麼處理, 不瞭解小數在浮點數中的表示和如何轉化10進制是不理解的你們別小看這個發展史,對咱們來講也很重要,好比後面講到I/O設備,其實I/O設備的演進就是計算機發展史的一個縮影,對咱們理解I/O設備的演進是頗有幫助的,你是在一個大的框架下理解一些更細節的概念。前端
大概是如何進行計算的呢?當時的計算機會有不少邏輯處理元件,它們在高低電壓(能夠表示01的二進制)下用線路鏈接起來實現計算的功能。java
當時的主要邏輯元件是電子管,電子管有咱們半個手掌那麼大,同時也意味着這個機器的體積是很大的,電子管耗電,計算機的耗電量也很高,並且此時的計算機只能識別0101的二進制數,因此只能用機器語言來編程,此時程序員編程是在一個紙袋上的,以下圖,有孔表明0,沒孔表明1webpack
以下圖:最右邊的是電子管,挨着的是晶體管和集成電路,咱們能夠看到晶體管比電子管小不少 css3
由於早期的計算機好比ENIAC,每一步的計算,須要執行的指令都須要程序員手動去操做,也就是手工就浪費了大量的時間c++
爲了解決這個問題,馮諾依曼就提出了存儲程序
的概念,就是指,將指令以二進制代碼的形式事先輸入到計算機的內存裏,而後內存根據裏面存儲的指令從首地址也就是第一條指令開始按順序一條一條的執行,直到程序執行結束,這種自動執行的機制比人工操做使計算機的計算效率大大提高程序員
馮諾依曼體系是以運算器爲核心的,咱們現代的計算機是以存儲器爲核心,咱們這裏瞭解馮諾依曼體系如何以運算器爲核心的價值不大,因此就直接介紹以存儲器爲核心的現代計算機涉及的部件吧web
首先,計算機最基本的5大組成部分以下圖,分別爲:輸入設備
(好比鍵盤), 存儲器
(好比內存), 運算器
(cpu), 控制器
(cpu), 輸出設備
(顯示器),咱們看一下這些基本的硬件設備是如何處理數據的面試
上圖的實線是數據線,是數據交換的通路,虛線是控制線和反饋線,是傳遞命令的通路
首先咱們的數據經過輸入設備會被加工程計算機可以識別的0101
的形式,咱們直接輸入的代碼計算機是不認識的。
而後通過輸入設備處理的數據,先存到了存儲器
裏(控制器控制輸入設備),存儲器能夠存放數據和程序指令
而後控制器
能夠直接從存儲器裏取得所須要執行的程序指令,取得指令後,控制器會分析指令
要作什麼(指令分爲操做碼
和地址碼
),分析的就是操做碼,到底要幹嗎
假設分析出來是讀取數據
的操做,也就是從存儲器中取一個數據給運算器,那麼讀取數據
的地址就在寫在地址碼
裏面,這時運算器就去就告訴存儲器要取數據的地址,而後存儲器直接把數據傳遞給運算器
最後運算結束,運算結果會返回存儲器,存儲器能夠直接把結果返回給輸出設備
(在控制器的控制下)
最後輸出設備,好比顯示器
上就看到咱們想要的數據
接下來又是乾巴巴的文字,太枯燥了,休息5分鐘,咱們先吃個雞腿,繼續吧!
上面是基本的計算機運算的過程,咱們拿一個實際的javascript
代碼來舉例:
假設在咱們的JS代碼裏,運行代碼 let a = 1 + 1
,此時上述的5大計算機部件如何處理的呢? `
首先鍵盤輸入代碼let a = 1 + 1
將被解析爲2進制代碼,在控制器的控制下放入了內存
而後內存存儲完畢, CPU
的控制器開始從內存裏取出指令,分析出指令是一個加法操做(先讓 1+1運算,後面纔會把1+1運算的結果賦值給變量a)
而後控制器控制運算器,運算器直接從內存裏取出數據兩個1,作一個加法運算得出結果,並返回給存儲器,存儲到一個內存的地址裏
而後控制器接着執行第二條指令(let a = 2),由於以前2已經被算出來了,第二條指令是賦值操做了(把1+1
的值賦給變量a
,a
其實就是一個內存地址而已)
此時CPU的控制將控制CPU的運算器作1+1
的加法運算,並得出結果2
最後執行指令完畢,若是咱們要打印console.log(a)的話,a由於本質上是一個內存地址,cpu會根據內存地址,找到這個地址裏存放的值,也就是console.log顯示的值
獲取到要顯示的值後,存儲器直接將數據傳給顯示器,這樣咱們就能夠在屏幕上看到2這個結果了
經過下圖,咱們簡單介紹一下相似javascript、Python這種解釋型語言和c, c++這種編譯型語言的區別。理解爲何解釋性語言一般都比編譯型語言運算速度慢。
高級語言通常有兩種方式轉換爲機器語言
編譯器
,將高級語言轉換爲二進制
代碼,好比c
,這樣c
運行起來就特別快,由於編譯後是機器語言,直接就能在系統上跑,如上圖,但問題是,編譯的速度可能會比較慢。js
,是將代碼翻譯一行成機器語言
(中間可能會先翻譯爲彙編
代碼或者字節碼
),解釋一行,執行一行須要注意的是,按照第一種將大量的高級代碼翻譯爲機器語言,這其中就有很大的空間給編譯器
作代碼優化,解釋性語言就很難作這種優化,可是在v8
引擎中,js
仍是要被優化的,在編譯階段
(代碼分編譯
和執行
兩個階段)會對代碼作一些優化,編譯後當即執行的方式一般被稱爲 JIT (Just In Time) Comipler
這章主要介紹進制轉換,好比10進制轉2進制怎麼轉,2進制轉10進制怎麼轉。
掌握這些事必要的,好比leetcode有一道簡單題叫excel序號,本質就是26進制轉10進制,不瞭解進制轉換就不容易作出來這道題。
例如2
進制101.1
如何轉化爲10
進制。(有些同窗以爲能夠用parseInt('101.1', 2)
,這個是不行的,由於parseInt
返回整數)
轉化方法以下(按權相加法): 2進制的 101.1 = 1 x 22 + 0 x 21 + 1 x 20 + 1 x 2-1
規律就是二進制
的每一個數去乘以2
的相應次方,注意小數點後是乘以它的負相應次方
。
到這裏我出一個思考題,unicode碼,第一個平面的也就是能包含字符範圍是 0000 - FFFF(16進制),請問16進制的FFFF是10進制的多少?
方法是除商取餘法:好比10進制轉2進制
例如: 把89化爲二進制的數
把89化爲二進制的數
89÷2=44 餘1
44÷2=22 餘0
22÷2=11 餘0
11÷2=5 餘1
5÷2=2 餘1
2÷2=1 餘0
1÷2=0 餘1
而後把餘數由下往上排序
1011001
這樣就把89化爲二進制的數了
咱們仍是以2進製爲例,方式是採用「乘2取整,順序排列」法。具體作法是:
因此n進制是一個道理
咱們具體舉一個例子
如: 十進制 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.2不等於0.3了
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
了
例如:
+15 => 01111(2進制)
-8 => 11000(2進制)
真值是咱們平時生活中用到的數字形式,好比+15,-8,機器數是存到機器裏的形式,也就是2進制的形式,其中01111,第一個0是表明正數的意思,1111是保存的數值,轉換成10進制就是15
因此合起來就是+15
let a = 0b10100;//二進制
let b = 0o24;//八進制
let c = 20;//十進制
let d = 0x14;//十六進制
console.log(a == b);
console.log(b == c);
console.log(c == d);
複製代碼
10進制轉任意進制 10進制數.toString(目標進制)
console.log(c.toString(2));
複製代碼
複製代碼
任意進制轉十進制 parseInt('任意進制字符串', 原始進制),小數部分會被截斷;
console.log(parseInt('10100', 2));
複製代碼
複製代碼
最開始計算機只在美國用,八位的字節能夠組合出256種不一樣狀態。0-32種狀態規定了特殊用途,一旦終端、打印機趕上約定好的這些字節被傳過來時,就要作一些約定的動做,如:
又把全部的空格、標點符號、數字、大小寫字母分別用連續的字節狀態表示,一直編到了第 127 號,這樣計算機就能夠用不一樣字節來存儲英語的文字了
這128個符號(包括32個不能打印出來的控制符號),只佔用了一個字節的後面7位,最前面的一位統一規定爲0
這個方案叫作 ASCII 編碼
後來西歐一些國家用的不是英文,它們的字母在ASCII裏沒有爲了能夠保存他們的文字,他們使用127號這後的空位來保存新的字母,一直編到了最後一位255。好比法語中的é的編碼爲130。固然了不一樣國家表示的符號也不同,好比,130在法語編碼中表明瞭é,在希伯來語編碼中卻表明了字母Gimel (ג)。
從128 到 255 這一頁的字符集被稱爲擴展字符集。
中國爲了表示漢字,把127號以後的符號取消了,規定
0xA1
用到0xF7
,後面一個字節(低字節)從 0xA1
到 0xFE
;後來仍是不夠用,因而乾脆再也不要求低字節必定是 127 號以後的內碼,只要第一個字節是大於 127 就固定表示這是一個漢字的開始,又增長了近 20000 個新的漢字(包括繁體字)和符號。
各個國家都像中國這樣搞出一套本身的編碼標準,結果互相之間誰也不懂誰的編碼,誰也不支持別人的編碼
ISO 的國際組織廢了全部的地區性編碼方案,從新搞一個包括了地球上全部文化、全部字母和符 的編碼! Unicode 固然是一個很大的集合,如今的規模能夠容納100多萬個符號。
ISO 就直接規定必須用兩個字節,也就是 16 位來統一表示全部的字符,對於 ASCII 裏的那些 半角字符,Unicode 保持其原編碼不變,只是將其長度由原來的 8 位擴展爲16 位,而其餘文化和語言的字符則所有從新統一編碼。
從 Unicode 開始,不管是半角的英文字母,仍是全角的漢字,它們都是統一的一個字符!同時,也都是統一的 兩個字節
Unicode 使用的數字是從 0
到 0x10ffff
,這些數字都對有相對應的字符(固然,有的尚未編好,有的用做私人自定義)。每個數字,就是一個代碼點(Code Point)。
這些代碼點,分爲 17 個平面(Plane)。其實就是17 組,只是名字高大上而已
Plane 3 到 Plane 14 尚未使用,TIP(Plane 3) 準備用來映射甲骨文、金文、小篆等表意文字。PUA-A, PUA-B 爲私人使用區,是用來給你們本身玩兒的——存儲自定義的一些字符。
Plane 0,習慣上稱做基本平面(Basic Plane);剩餘的稱做擴展平面(Supplementary Plane)。
UTF-32 使用四個字節來表示存儲代碼點:把代碼點轉換爲 32 位二進制,位數不夠的左邊充 0。
4個字節就是4 * 8 = 32位,就能表示2的32次方個數字,這些數字能夠對應2的32次方個字符,但其實咱們經常使用的是 0 - 2的16次方的字符,能夠看到utf32編碼特別浪費空間
UTF-16 用二個字節來表示基本平面,用四個字節來表示擴展平面。也就是說,UTF-16的編碼長度要麼是2個字節(U+0000到U+FFFF),要麼是4個字節(U+010000到U+10FFFF)
UTF-8是一種變長的編碼方法,字符長度從1個字節到4個字節不等。 越是經常使用的字符,字節越短,最前面的128個字符,只使用1個字節表示,與ASCII碼徹底相同。
編號範圍 | 字節 |
---|---|
0x0000 - 0x007F | 1 |
0x0080 - 0x07FF | 2 |
0x0800 - 0xFFFF | 3 |
0x010000 - 0x10FFFF | 4 |
4E00~9FA5 | 中日韓統一表意文字 |
---|---|
2E80-A4CF | 中日朝部首補充、康熙部首、表意文字描述符、中日朝符號和標點、日文平假名、 日文片假名、注音字母、諺文兼容字母、象形字註釋標誌、注音字母擴展、 中日朝筆畫、日文片假名語音擴展、帶圈中日朝字母和月份、中日朝兼容、 中日朝統一表意文字擴展A、易經六十四卦符號、 中日韓統一表意文字、彝文音節、彝文字根 |
F900-FAFF | 中日朝兼容表意文字 |
FE30-FE4F | 中日朝兼容形式 |
FF00-FFEF | 全角ASCII、全角中英文標點、半寬片假名、半寬平假名、半寬韓文字母 |
通常用4E00-9FA5已經能夠,若是要更廣,則用2E80-A4CF || F900-FAFF || FE30-FE4F
看到了吧,4E00-9FA5 就是通常正則表達式匹配中文的範圍。爲何來的,這下知道原理了吧
可使用encodeURIComponent
encodeURIComponent('張')
"%E5%BC%A0"
複製代碼
並且,平時咱們說中文是兩個字節表示的,這個是錯誤的,幾個字節表示徹底是看編碼,好比utf8
和utf16
有可能一樣的unicode
碼,編碼出來的字節數是不同的。
咱們平時的頁面都是utf8
編碼的,其實在底層2
進制上,中文一般是3
個字節表示的。
雖然 JavaScript 源文件能夠有任何類型的編碼,但 JavaScript 會在執行以前在內部將其轉換爲 UTF-16。
JavaScript 字符串都是 UTF-16 序列,正如 ECMAScript 標準所說:
當 String 包含實際文本數據時,每一個元素都被視爲單個 UTF-16 代碼單元
就是整個機器字長(機器字長是指計算機進行一次整數運算所能處理的二進制數據的位數,好比咱們常說32位機器,64位機器)的所有二進制位均爲數值位,沒有符號位,至關於都是正數。
好比8位無符號整數的範圍就是 二進制: 00000000 - 11111111
轉化爲10進制就是0 - 255
注意咱們說的無符號數都是針對整數,沒有小數
先來看看定點整數和定點小數如何在計算機裏表示。
定點數整數和小數均可以用原碼,反碼,補碼錶示,整數還能夠用移碼錶示,具體什麼意思咱們稍後介紹。
原碼就是用尾數表示真值的絕對值,符號位0表示正數,1表示負數,假設咱們機器字長爲8位
咱們拿 +19和 -19來解釋一下
+19表示爲:0,0010011 -19表示爲: 1,0010011
下面是定點小數的表示,同理
若符號位爲0,則反碼和原碼一致
若符號位爲1,則數值位所有取反
補碼分爲:
正數的補碼 = 原碼
負數的補碼 = 反碼末尾 + 1
補碼的基礎上符號位取反,只能表示整數。爲何須要移碼,移碼能夠很是方便的判斷兩個數的大小。以下圖:
咱們會發現移碼從左往右,只要先有1就更大,若是都有1,就日後面比,先出來1的就更大
爲何原碼有問題呢,好比咱們作一個運算 14 + (-14)
按道理應該等於0,可是咱們把它們轉爲2進制,定點數的加法就出現問題了,竟然不等於0,以下圖
那該怎麼辦呢,原碼的加法須要變爲減法也就是14-14,這樣就對了,可是這意味咱們的計算機既要設計一個加法器又要設計一個減法器,減法器的複雜度是很高的,爲了方便運算,一些聰明的人實現了讓加法代替減法,這就須要咱們以前講的補碼知識了。
14 + (-14)怎麼才能計算正確呢?
咱們可讓14的原碼 加上 -14的補碼,這時候就是
00001110 + 11110010(這個是-14的補碼) = 100000000,由於機器字長是8位,也就是最多容納8位2進制,最左邊的1會被機器自然丟棄,這樣最終結果就是00000000.
爲何須要浮點數,主要是定點數對於很大的數字是特別浪費空間的,舉個例子,好比說浮點數1.2 x 10的20次方,咱們知道是10進制,就只須要存1.2和20這些數據就能表示這個數,可是定點數一個數字佔一個坑,確定沒有浮點數在更小的空間表示更大的數。
咱們舉一個例子來理解浮點數的表示,好比數字+302657264526,這是定點整數的表示方法,若是是科學計數法,咱們表示爲:+3.026 * 1011 ,而其中的10是否是固定不變的呢,因此若是要保存這個科學技術法表示的數字,咱們能夠不看10這個基數,只須要保存+11 和 +3.026就能推出這個數字的科學技術法,從而獲得這個數字
咱們能夠給+11 和 +3.026取兩個名字,在浮點數裏分別叫階碼和尾數,以下圖
注意階碼分爲了階符和階碼的數值部分,尾數分爲數符合尾數的數值部分。
階符是正表示小數點日後移,爲負表示小數點往前移動。階碼錶示小數點移動多少位。
數符表示數值的正負性,尾數表示數字精度。
其中, 階碼反映數值的大小,尾數反應數值的精度,爲何這麼說呢,好比以前舉的例子中,+11表示小數點要右移多少位,是否是越大,移動的位數越多,數字就越大呢,對於尾數,好比+3.0265748是否是一樣右移5位比+3.026表示的數字更精確呢
在二進制表示的計算機內,階碼經常使用補碼或者移碼錶示的定點整數,尾數經常使用原碼或者補碼錶示的定點小數。
浮點數的表示爲 N = rE * M , r至關於底數,是2(跟10進制科學計數法是10意思是同樣的),E表明階碼,M表明尾數。
常見的IEEE 754標準分爲單精度浮點數float和雙精度浮點數double,咱們能夠看一下它的區別(階碼也能夠稱爲指數)
由於javascript的數字就是雙精度浮點數,因此咱們只介紹這一種。在雙精度浮點數的尾數是52位,實際上是能夠表示53位,爲何呢,咱們知道科學計數法,但二進制只能表示0和1,0不符合科學計數法,因此52位最前面有一個省略的數字是1,默認存在,可是不顯示在52位中。
而且尾數用原碼錶示。
如今咱們講一下階碼須要注意的點。階碼是用的無符號定點整數表示,爲[0,2047](2的11次方減一)但這就有一個問題了,不能表示負數,爲了表示負數通常能夠引入符號位,可是符號位定點數減法運算又要引入補碼,就很麻煩,因此採起一種取巧的方式,將階碼部分統一減去1023,就變成[-1023, 1024]
又由於階碼的全0和全1有特殊用途,因此-1023和1024的移碼就是全1和全0,因此階碼的範圍變爲[-1022, 1023]。那若是階碼是0和1有什麼特殊用途呢?
當階碼E全爲0,尾數M不全爲0時,此時尾數隱藏的首位1.xxx的1變爲0
當階碼E全爲0,尾數M全爲0時,表示真值正負0
當階碼E全爲1,尾數M全爲0時,表示無窮大
當階碼E全爲1,尾數M不全爲0時,表示NaN
首先什麼是指令呢?是指計算機執行某種操做的命令,是計算機運行的最小功能單位。一臺計算機的全部指令的集合構成該機的指令系統,也稱爲指令集。好比著名的x86架構(intel的pc)和ARM架構(手機)的指令集是不一樣的。
好比以前有新聞說,蘋果開發了基於ARM架構(精簡指令集)的本身的芯片,放棄了以前採用複雜指令集的intel芯片。
一條指令就是機器語言的一個語句,它是一組有意義的二進制代碼。一條指令一般包括操做碼(OP) + 地址碼(A)
根據指令中操做數地址碼的數目不一樣,可將指令分紅如下幾種格式。咱們舉幾個例子(沒有覆蓋所有)讓你們感覺一下,尤爲注意三地址指令,就能理解指令的大體格式了
一、零地址指令
只給出操做碼OP,沒有顯式地址。這種指令有兩種可能:
二、三地址指令
指令的含義:(A1)OP(A2)->A3
它表示從A1和A2地址上取出數據,而後進行OP操做,最後存放到A3地址上。
尋址尋什麼呢?咱們計算機裏無非保存的是指令和數據,尋的就是上面這兩個傢伙。
指令尋址方式有兩種:一種是順序尋址方式
,另外一種是跳躍尋址方式
。
一、順序尋址可經過程序計數器PC加1,也就是按照在內存的順序依次執行指令
二、跳躍尋址經過轉移類指令實現,跳躍,就是不按照程序計數器自動加一的方式(不是按順序執行指令)給出下調指令地址,而是由本條指令給出的下條指令格式
肯定本條指令的地址碼指明的真實地址。大體有10種尋址方式,咱們只介紹其中3種,由於這部份內容都是以瞭解爲主。
直接尋址是,在指令中的地址碼指向的內存地址就是操做數的有效地址,以下圖
如上圖,地址碼A對應的就是咱們要的操做數
如上圖,地址碼A對應的不是操做數,而是另外一個地址,這個地址指向的地址纔是操做數
意思是尋到的地址,並非咱們要去內存尋找的真正地址,而是須要加上一個基礎地址,至關於偏移量。以下圖:
CISC (複雜指令系統計算機):一條指令完成一個複雜的基本功能。好比x86架構的計算機,主要用於筆記本和臺式電腦。計算機的指令系統比較豐富,有專用指令來完成特定的功能。所以,處理特殊任務效率較高。
RISC (精簡指令系統計算機):一條指令完成一個基本"動做";多條指令組合完成一個複雜的基本功能。好比ARM架構,主要用於手機,平板等。設計者把主要精力放在那些常用的指令上,儘可能使它們具備簡單高效的特點。對不經常使用的功能,常經過組合指令來完成。所以,在RISC 機器上實現特殊功能時,效率可能較低。
我這裏補充一些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
裏的數據放到運算器的寄存器裏,這裏的取指令的過程結束了。
這裏咱們主要補充一下GPU的內容。
GPU(Graphics Processing Unit) 圖形處理單元,又稱圖形處理器,是咱們所周知的顯卡的核心部件,是顯卡的「心臟」。GPU是專爲複雜數學運算和幾何運算而設計的芯片,它的用途咱們日常所周知的就是用於圖形圖像處理(顯卡)。
咱們能夠看一下CPU和GPU的對比圖
上圖的一段總結很是好,CPU至關於1名老教授,奧數題和小學算術題都會,GPU至關於1000名小學生,只會小學算術題。
從上圖咱們能夠知道GPU將更多的空間(晶體管)用做執行單元,而不是像CPU那樣用做複雜的控制單元和緩存(CPU須要同時很好的支持並行和串行操做,須要很強的通用性來處理各類不一樣的數據類型,同時又要支持複雜通用的邏輯判斷,這樣會引入大量的分支跳轉和中斷的處理。
這些都使得CPU的內部結構異常複雜,計算單元的比重被下降了),實際來看CPU的芯片控件25%是ALU,而GPU則高達90%(GPU面對的則是類型高度統一的、相互無依賴的大規模數據和不須要被打斷的純淨的計算環境。所以GPU的芯片比CPU芯片簡單不少),這也就是爲啥GPU運算能力超強的緣由。
首先咱們要知道爲何要用(開啓)GPU加速(硬件加速), 而後咱們才能去探討如何以及怎麼樣去應用GPU加速。
3D 或透視變換(perspective,transform) CSS 屬性
使用加速視頻解碼的video元素
擁有 3D (WebGL) 上下文或加速的 2D 上下文的 canvas 元素
混合插件(如 Flash)
對本身的 opacity 作 CSS 動畫或使用一個動畫 webkit 變換的元素
擁有加速 CSS 過濾器的元素
元素A有一個 z-index 比本身小的元素B,且元素B是一個合成層(換句話說就是該元素在複合層上面渲染),則元素A會提高爲合成層
這裏裏面最經常使用的是1和7。1很好理解,就是transfrom3d屬性。第七點我解釋一下,你怎麼來判斷本身的頁面是否使用了3d加速。請看下圖:
首先:
而後觀察這兩個圖層:
那如何讓2d也能單獨的圖層渲染啓用GPU加速呢,只須要給2d的css上加一個index以後,而後點擊動畫,就會出現黃色邊框,你們能夠用這個網址作測試:www.w3school.com.cn/css3/css3_3…
總線是一組能爲多個部件分時共享的公共信息傳送線路
一、簡化了硬件的設計。咱們從計算機簡史裏面知道,當時的設備是分散接入計算機的,這樣計算機沒辦法統一接口命令來控制這些設備。總線結構便於採用模塊化結構設計方法,面向總線的微型計算機設計只要按照這些規定製做cpu插件、存儲器插件以及I/O插件等,將它們連入總線就可工做,而沒必要考慮總線的詳細操做。
二、系統擴充性好。一是規模擴充,規模擴充僅僅須要多插一些同類型的插件。二是功能擴充,功能擴充僅僅須要按照總線標準設計新插件,插件插入機器的位置每每沒有嚴格的限制。
就至關於webpack的插件系統,加入功能和減小功能都是可插拔的,比把代碼寫死更加靈活。
咱們拿上圖爲例:
爲何須要多級存儲的結構呢?以下圖所示,能夠了解到爲何要引入cache
主存的執行速度相比cpu要慢不少,這會形成主存在運行的時候,cpu會等待的問題,好比cpu1秒就處理10條指,但從內存裏取10條指令就須要1分鐘,就很浪費cpu資源,因此爲了解決這個問題,採用了cache-主存的方式,cache是高速緩衝儲存器,它的速度接近於cpu。
RAM又被稱做「隨機存儲器」,是與CPU直接交換數據的內部存儲器,也叫主存(內存)。它能夠隨時讀寫,並且速度很快,一般做爲操做系統或其餘正在運行中的程序的臨時數據存儲媒介。當電源關閉時RAM不能保留數據(掉電數據消失哦)若是須要保存數據,就必須把它們寫入一個長期的存儲設備中(例如硬盤)。
ROM又被稱爲「只讀存儲器」,ROM所存數據,通常是裝入整機前事先寫好的,整機工做過程當中只能讀出,而不像隨機存儲器那樣能快速地、方便地加以改寫。ROM所存數據穩定,斷電後所存數據也不會改變
先看下圖
(說明一下,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
}
複製代碼
咱們能夠發現
數組的數據有時候在內存是連續存儲的(代碼裏但數組a,對應圖中主存裏但a[0]-a[7]的數據塊)
若是咱們要取數據,好比從內存取出a[0]的數據須要1000ns(ns是納秒的意思),那麼取出a[0]到a[7]就須要1000 * 8 = 8000 ns
若是咱們cpu發現這是取數組數據,那麼我就把就近的數據塊a[0]到a[7]所有存到緩存上多好,這樣只須要取一次數據,消耗1000ns
cahce
就是局部性原理
的一個應用
空間局部性
:在最近的將來要用到的信息(指令
和數據
),極可能與如今正在使用的信息在存儲空間
上是鄰近的(好比for循環用到數據在主存都是相鄰存儲的)時間局部性
:在最近的將來要用到的信息,極可能是如今正在使用的信息下圖注意的是,cpu拿數據是先從cache裏拿,若是沒有才從主存裏面取
能夠看到cache
一次性取了a[0]
到a[9]
存儲體上的數據,只須要1000ns
,由於Cache
是高速存儲器
,跟cpu
交互速度就比cpu
跟主存
交互速度快不少
輸入/輸出(Input /Output ,簡稱I/O),指的是一切操做、程序或設備與計算機之間發生的數據傳輸過程。
好比文件讀寫操做,就是典型的I/O
操做。接下來咱們看一下I/O設備的演進過程
關鍵:I/O設備的演進過程其實就是解放cpu的過程,爲何這麼說呢,看完下面的介紹就知道了!
在早期的計算機裏,由於cpu啓動外設後,外設準備數據是須要時間的,好比讀取外部傳來的數據,此時cpu
如何知道I/O設備
已經完成任務呢?好比說怎麼知道I/O設備
已經讀取完一個文件的數據呢?CPU
會不斷查詢I/O設備
是否已經準備好。這時,cpu
就處於等待狀態。也就是cpu
工做的時候,I/O
系統是不工做的,I/O
系統工做,cpu
是不工做。並且主存和外設也須要藉助cpu來通訊,因此留給CPU的時間又少了。
因此我來看此階段比較明顯的問題:
接着看第二階段
爲了解決第一階段CPU
要等待I/O設備
,串行
的工做方式,全部I/O設備
經過I/O總線
來跟CPU
打交道,一旦某個I/O設備
完成任務,就會以中斷請求
的方式,經過I/O總線
,告訴CPU
,我已經準備好了。
可是對於高速外設
,它們完成任務的速度很快,因此會頻繁中斷CPU
,(舉一個例子,每輸入一個字符就中斷CPU,是否是很影響cpu的執行呢) 爲了解決這個問題,高速外設跟主存之間用一條直接數據通路,DMA總線
鏈接,DMA控制方式只須要CPU
安排最開始高速外設最初的任務,接下來的數據交換就是靠DMA控制器控制了,這樣就能夠防止頻繁中斷CPU
,讓CPU獲得瞭解放
問題:
最後來看一下第三階段
第三階段,CPU經過通道控制部件來管理I/O設備,CPU不須要幫它安排任務,只須要簡單的發出啓動和中止相似的命令,通道部件就會自動的安排相應的I/O設備工做。爲何這種方式比DMA更好呢,由於商用的中型機、大型機可能會接上超多的I/O設備,若是都讓CPU來管理,那麼CPU就很是累。
通道能夠理解爲一種「弱雞版的CPU」。能夠識別通道指令,你能夠理解爲CPU告訴通道,取多少數據,數據存放到內存哪裏,通道就本身去處理,不用cpu來管理這麼多事情,注意看下圖
如上圖,通道是跟CPU並列的,因此通道能幫CPU分擔任務,此時的通道有本身的一套指令集,能夠執行通道指令。
後面進一步加強了通道的功能,這裏出現了跟cpu處理能力差很少的I/O處理機
以前咱們講到一個名詞叫中斷,這個概念很是重要,咱們做爲補充概念學習一下
程序中斷是指在計算機執行現行程序的過程當中,出現某些急需處理的異常狀況或特殊請求,CPU暫時停止現行程序,而轉去對這些異常狀況或特殊請求進行處理,在處理完畢後CPU又自動返回到現行的斷點處,繼續執行程序。咱們來舉個例子就明白了。
當程序執行到K的時候,鍵盤的敲擊產生了中斷(I/O中斷),此時CPU會終止執行當前的指令,轉而去處理中斷,等中斷服務程序執行完畢,繼續執行k+1。
本文主要參考資料:
唐朔飛:計算機組成原理
袁春風: 計算機組成原理
王道考研:計算機組成原理
極客時間:深刻淺出計算機組成原理