前一篇中粗略講述了二進制加法運算的過程,其中假定數據是從寄存器直接裝載到加法器兩端,加法器產生的結果也一樣保存在另外一寄存器中,而沒有在乎數據是如何從外界傳遞到了寄存器裏,也並未給出寄存器中結果將以何種方式呈現給加法操做的真正用戶。 程序員
所以本章將從計算機體系結構入手,介紹天然語言與機器語言間的共識。 算法
當咱們但願求助他人幫忙解決某一問題時,首先要作的是用雙方共同支持的天然語言(如普通話、英語)來描述問題,等待對方提出解決問題的方法或是協同完成。然而若是但願將某一任務交付給計算機來完成,那麼很抱歉,計算機並不會思考,他只是流程的接收和執行者,也就是說,如何造成流程,仍然須要咱們來完成。 數組
也可能有人會講,不是啊,當我打開計算機,雙擊一個視頻文件,計算機就會幫我播放,我並無告訴它如何播放,用什麼播放吧。但你回過頭來看看這段文字,就能夠發現其中仍是含有通用流程,和你要尋找食物必須打開冰箱同樣。至於視頻播放的真正過程,後面會講到,此處先給一個概念。 架構
全部的信息都是以電信號存儲於存儲器中,讀取後獲得的是二進制字節流,至於該字節流描述成什麼具體內容須要依賴上下文,以及解釋器。好比一樣一句「很差吧」使用不一樣語氣,對於不一樣接收者而言可能獲取到不一樣的含義。一樣,一串字節流,使用音頻播放器可能被解析爲聲音,使用notepad可能被處理爲文本,甚至若是想象力再豐富一點,也能夠假象成這是一個文字解密遊戲。
所以一般會在字節流中埋下一些記號,標明數據類型,這些稱爲元數據。在元數據基礎上,不一樣視頻格式會有不一樣數據組合方式,經過枚舉不一樣格式嘗試解析視頻文件,如能夠匹配爲某格式,就按照該格式進行播放;如不能,則報錯不能正確播放某文件。
模塊化![]()
綜上所述,要想託管任務給計算機執行,那麼計算機必須有兩個充分條件: 函數
計算機可以執行的任務歸根結底是數學計算,而對於如何進行統1、可行的任務描述,人們作了大量嘗試,直到圖靈機概念的出現。 post
圖靈爲了給出計算機的清晰數學描述,他觀察人們計算式採用的方法和行爲,而後將其抽象成一種統一的表達機制。圖靈將人們用紙筆進行數學運算的過程抽象成下列兩種簡單動做: 性能
爲了模擬人的運算過程,圖靈造出一臺假想的機器,該機器按照讀取規則讀取紙帶上數值,而後進行計算。
操作系統
這臺機器主要由以下幾個部分組成: 翻譯
簡而言之就是TABLE中規則控制讀寫頭HEAD在無限長紙帶TAPE上讀寫,根據讀寫內容進行運算,並選擇是否保存結果至紙帶特定位置中。如運行過程當中發生狀態變化,則保留在狀態寄存器內。
圖靈機是思想指導,迴歸到實踐而言,對於任務描述的方法可表達爲七層轉換。《計算機系統概論》1.7節中提出,要控制電子器件按照咱們的意圖工做,需經歷以下七個完整過程:
6 問題
一般咱們採用「天然語言」來描述問題,如英語、漢語等,存在的問題是有不少語言組成部分具備二義性,而計算機指令極可能由於沒法肯定語句的準確含義而採起錯誤措施,致使錯誤結果。所以,剩下的幾層轉換,都可以視爲消除二義性的步驟。
5 算法
算法描述的特定是流程化、步驟清晰,並確保該流程能終止。具體以下:
4 語言(開發語言)
有了人類能夠理解的計算方法,還須要轉達計算機如何計算。計算機只能識別「機械語言」,這種語言具備嚴格的順序方式,以便讓計算機順序地執行指令序列。這一層的語言一般稱爲開發語言,如C/C++,Java,Python等,因爲開發語言須要程序員編寫,所以仍具備必定可讀性。
3 機器(ISA)結構
然而到開發語言一層仍然不夠,由於開發語言多是面向多種硬件環境的,爲了在特定的硬件平臺上也可以順利執行,還要作一次翻譯,即將程序由開發語言翻譯成爲特定計算機的指令集。該部分功能一般由「編譯器」或「解釋器」來完成。
指令集結構實際上就是程序和計算機硬件之間接口的一個完整定義。
2 微結構
有了接口,就能夠順利完成任務描述,而真正要調動硬件的運行能力,還須要將各接口進行實現。好比不少處理器都使用了ISA X86,但具體實現卻能夠各不相同。
1 電路
微結構的概念最終要落實到一組組簡單的邏輯電路,一樣一個功能,如加法器,可使用多種實現方式,不一樣的實現方式也一樣帶來了性能和成本上的差別。須要設計者從全局上進行衡量取捨。
0 器件
最後,相同的邏輯電路也仍然可使用器件技術來實現,如材料、規格等等。
綜上所述,從人類天然語言表述的問題到最終執行的單元器件,須要作七層轉換。對於爲何要分層以及爲何是七層,一般來說,高層一般是低層的抽象結果,意在忽略部分無關細節,提供分析問題的更高層次的視野。不過到目前爲止表述的方法都是理論性的,接下來就看一下歷史中實踐的結果。
馮·諾依曼結構是一種將程序指令存儲器和數據存儲器合併在一塊兒的電腦設計概念結構,它用於實現通用圖靈機和一種相對於並行計算的序列式結構參考模型。
接下來咱們對馮·諾依曼結構進行細化,以下圖所示。
內存
內存存放程序,訪問內存的第一步,是想內存提供被訪問內存單元的地址。以讀寫操做爲例:
處理單元
ALU所能處理的量化大小一般稱爲計算機的字長,如X86,X64。設計中一般會在ALU附近配置少許存儲器,以便存放最近生成的中間計算結果。
輸入
經過計算機解決問題的前提是可以將問題描述成電信號,轉換層次可參考1.2小節。
輸出
計算結果要具備可讀性就必須經過設備進行輸出,或是顯示器、或是音響等可將電信號轉換爲天然語言的設備。
控制單元
控制單元負責指令的有序執行,其中具備兩個特殊寄存器。
哈佛結構
雖然馮·諾依曼結構是現代計算機的主要結構,但咱們也必須知道,還有一個結構叫作哈佛結構。它和馮·諾依曼結構之間最大的差別在於數據在內存中排列方式。馮·諾依曼結構中容許指令和數據混合存儲在同一存儲模塊中,而哈佛結構必須使用獨立的存儲模塊來分別存儲指令和數據。![]()
本系列的第一篇中主要介紹了電學知識,爲的就是在理解計算機時可以造成完整通路,而不是隻能觸及操做系統這一層,視底層硬件爲黑盒,不知因此然。從前面咱們知道硬件只能識別電信號,不管是組合邏輯電路仍是時序邏輯電路,輸出的產生終歸須要輸入的參與,而在託管任務時,任務就是咱們須要的輸入。
然而咱們不可能在託管任務後還要守在一旁切換電信號以提供準確輸入換取輸出,既不高效也不可理智。所以最好能將任務描述使用一種可以最終轉換爲電信號的語言進行描述,而硬件在收到任務描述後,根據其中規則和數據自動完成計算,產出結果。同時爲了保證任務描述的精確性,對於電信號咱們進行了一次抽象,將信號幅度抽象成有無,對應爲0和1,即所謂的二進制。
馮·諾依曼結構在現有基礎上提出計算過程應該是:程序和數據都是以bit流的方式存放在計算機內存中,程序在控制單元的控制下,依次完成指令的讀取和執行。
二進制在歷史悠久的人類天然語言面前是過於蒼白的,爲了可以將二進制位和人類想表達的操做對應起來,這一次,抽象的任務落在了天然語言上。既然計算機實質上是負責計算任務,所以能夠不直接參考天然語言,而是選擇抽象數學運算的基本規則(如加減乘除),造成指令。
目前通用計算機中指令是其執行的最小單位,指令自己自己由操做碼和操做數組成。操做碼標明其行爲,操做數標明行爲做用對象。
指令集架構還定義了其餘內容,現存的指令集結構也各不相同,如Intel和AMD系的。但一般一條指令包含內容以下:
指令的處理過程是在控制單元控制下一步步完成的,這裏執行的步驟順序稱爲指令週期,每一步稱爲節拍,一般一個指令週期包含6個節拍,分別以下:
取指令FETCH
從內存中讀取下一條待執行的指令,並將其裝入控制單元的指令寄存器IR中。
譯碼DECODE
譯碼操做的任務是分析、檢查指令的類型,並肯定對應微結構操做細節。
地址計算EVALUATE ADDRESS
如操做數不是當即數(如123),而是寄存器地址等涉及尋址的表達方式時,須要進行地址計算以獲得操做數的真正地址。
尋址方式一般有以下幾種:
當即尋址 | 操做數爲當即數 | MOV BX,8080H |
---|---|---|
寄存器尋址 | 操做數爲寄存器 | MOV BX,AX |
直接尋址 | 操做數爲地址 | MOV AX,[1234H] |
寄存器間接尋址 | 操做數爲SI/DI/BX/BP之一 | MOV BX,[DI] |
寄存器相對尋址 | 操做數爲SI/DI/BX/BP之一,加上偏移量 | MOV BX,[DI+100H] |
基址加變址尋址 | 操做數是BX/BP之一,加上SI/DI之一 | MOV BX,[BX+DI] |
相對基址加變址尋址 | 操做數是BX/BP之一,加上SI/DI之一,加上偏移量 | MOV BX,[BX+DI+100H] |
取操做數FETCH OPERAND
讀取指令處理所須要的源操做數,分爲兩個步驟:
執行EXECUTE
負責指令的執行操做,不一樣操做碼在該節拍的操做也各不相同。
存放結果STORE RESULT
將執行結果寫入目的寄存器,該節拍結束後,循環進入第一個取指令節拍,因爲PC寄存器在連續空間內指令上移動,所以也稱爲順序執行。有順序執行天然就有非順序執行,這裏不去深究跳轉的具體實現,只要知道,知足條件後須要跳轉前,將會修改PC寄存器中內容,跳轉完成後,繼續順序執行連續指令,直到下一次跳轉的來臨。
終於如今有了指令集,它定義了處理器能夠執行的全部操做,而咱們剩下要作的就是將問題描述成一條條順序或條件控制的跳轉執行指令。對於簡單的、小規模的任務,經過編寫彙編函數,一般也能實現功能,除了效率略低,移植性不佳(平臺支持的指令集可能不一樣)等。但對於複雜、大規模的任務,使用指令集語言就略顯力不從心了,畢竟全部操做如MOV等都須要當心翼翼地不停叮囑,這時候人們又想到了抽象。
雖然指令集接口各家、各平臺互不相同,那能不能想出一套更高層次的,不在乎平臺差別的開發語言來描述問題。這樣一來,還能夠順便把一些繁瑣的、囉嗦的地址處理指令模塊化,將任務描述這件事情從細節中解脫?答案是確定的,如今咱們有了更貼近底層的C/C++,主打一次編譯到處運行的Java,甚至不要編譯純靠解釋器在運行時逐字逐句翻譯的Python等腳本語言,這些都讓咱們可以更關注問題自己,而不是事無鉅細的實現。
固然從開發語言到指令集並非省略了那些繁瑣的細節,而是經過編譯器、解釋器、虛擬化運行環境等模塊代勞。軟件開發中有一句話和東北燒烤哲學相似,沒有什麼問題是一頓燒烤不能解決的,若是有,那就兩頓;一樣沒有什麼問題是一箇中間層不能解決的,若是有,那就兩個。
到目前爲止,咱們闡述了從問題自己出發,造成任務描述(指令),並由電路執行運算。這些都是總體的概念,或者說,對於實現細節,都是作了較多的抽象,爲的是從宏觀上有所瞭解。後續將對體系中各組成部分進行分別深刻,力求淺出,再不濟也要知道一些關鍵概念。
這裏或者本系列也均不會進行任務無謂的語言之爭,本系列文章尋求的是問題的解決方法、思惟,而不是各實現細節之間的差別。
追尋知識融會貫通後的快感。