1.引言
前面我們已經提到過寄存器以及cpu的一些基本工作原理,那麼在一個程式運行時,寄存器、cpu、記憶體等都是擔任著怎樣的角色呢。我們學習彙編的目的就是為了更好的瞭解電腦底層是如何工作的,所以我覺得有必要在學習彙編指令之前談一談一個電腦程式是如何在電腦上運行起來的。本文會儘可能詳細地描述從寫一個來源程式到它被執行的過程中都電腦內部都發生了哪些事情。 2.電腦是如何工作的呢。
從宏觀上來講,電腦是給人提供服務的,提供方便的。人類想要告訴電腦一些東西,通過什麼途徑呢。輸入裝置(如滑鼠,鍵盤,等等);電腦想要告訴人類一些東西也會通過輸出裝置(如螢幕,印表機,等等);電腦在讀入使用者的資料後,會對資料進行計算加工,這時會用到運算器;而運算的結果可能不會立刻輸出給使用者,而是暫存起來,這時電腦會用到儲存空間;而不論是運算的過程還是儲存的過程,邏輯流程的安排,步驟序列則需要控制器。人類與電腦的互動,就是通過這五大組件的通力合作展開的,或者說,電腦的工作就是通過這五大組件實現的。
現代電腦基本上都是遵照馮·諾依曼體繫結構進行設計、生產建造的。而該體繫結構的核心概念就是儲存程式電腦,即儲存在電腦中的程式的執行,是通過一條條指令的執行而實現的(三階段:取指令、指令解碼、執行指令)。這是我們從微觀的角度來看待電腦是如何工作的。
————-摘自電腦是如何工作的 3.來源程式到目標程式的過程
1.來源程式的儲存
我們就拿大家最熟悉的程式hello world來描述這個過程。
相信讀者知道電腦是以0、1串來表示資料的,所以我們的hello world程式在電腦中也是以這樣的形式存在的,8個位為一個位元組,一個位元組表示程式中的某些字元,也就是每個字元對應一個8位的整數值,這個整數值就是ascii碼。
所以我們的來源程式用ascii碼就是這樣表示的:
現在這些ascii碼都是十進位方式展示在我們面前,但是在電腦當中還是一串串0、1組成的8位的串。這種以ascii碼儲存的檔案就是文字檔,其它方式儲存的檔案都是二進位檔案。
2.來源程式的編譯
雖然我們能夠很輕鬆地看懂來源程式,但是電腦卻不能理解這些是什麼東西,更不要談運行我們的hello.c了,所以必須要讓電腦也能看懂我們的意思,這時候就需要編譯系統來幫忙了。 編譯系統的作用:將來源程式翻譯為一些電腦能夠理解的低級機器指令。 編譯系統的組成:前置處理器、編譯器、彙編器、連接器
編譯系統的工作流程:
3.
1.前置處理器:根據以#開頭的命令,修改來源程式。如根據#include stdio.h>行,前置處理器讀取系統標頭檔stdio.h中的內容,代替此行內容。來源程式經過預先處理後,得到另一個c程式,此程式通常以.i為尾碼儲存。
2.編譯器:將預先處理後的.i檔案轉換成組譯工具。編譯器將不同的進階語言(如c語言,C++語言)轉換成嚴格一致的組合語言格式進行輸出。組合語言以標準的文字格式設定確切的描述每機器語言指令。編譯器得到的檔案通常以.s為尾碼儲存。
3.彙編器:將組合語言(.s檔案)翻譯成機器語言指令,並將這些指令打包成一種可定位目標程式格式。彙編後得到的檔案即為二進位檔案,通常以.o為尾碼。 連結器:hello,world程式中調用過printf函數,它是一個c標準庫裡的函數。Printf函數存放在一個名為printf.o的單獨先行編譯的檔案中。而這個檔案必須以適當的方式併入到我們的程式中,這個工作由連結器完成。將外部的.o檔案併入後,得到一個完整的hello,world可執行檔。可執行檔載入到儲存空間後,由系統複製執行。
在unix系統中,來源程式到目的程式的轉換室友編譯器驅動程式執行的。
輸入命令:gcc hello.c -o hello
就會執行上述步驟並產生目標二進位程式。 4.運行目標程式
現在我們已經有了目標程式了,是時候運行了。
在unix系統中輸入程式名:
當使用者輸入一行命令後,shell先判斷它是不是一個shell內建命令,如果不是,shell會假定使用者輸入為一個可執行檔的名字,從而去載入並執行該檔案。
註:
來源程式是放在磁碟中的。
載入:把一個位元組或一個字從主存複製到寄存器,覆蓋掉寄存器中原來的值。
現在我們來關注下細節,電腦硬體到底做了什麼。
上圖是電腦一些基本的硬體組成。
其中一些硬體需要特別說明:
1.匯流排:在各個組件之間傳輸資料。
2.I/O裝置:IO裝置是系統與外界通訊的通道,如滑鼠,鍵盤,顯示器,磁碟都是典型的IO裝置。
3.主儲存空間:簡稱主存,是處理器執行程式時用於臨時存放程式及其資料。主存由一組動態隨機儲存空間晶片集成。也就是我們通常說的記憶體,它是與磁碟不一樣的東西,就是我們電腦中的記憶體條
4.處理器:也就是cpu,解釋執行儲存在主存中的指令。其內部的pc永遠指向下一條將要被執行的指令的地址。處理器的操作主要是圍繞PC、ALU(算術\邏輯運算單元)、主存來進行運作的
CPU按照指令可能會執行的操作有:
載入:把一個位元組或一個字從主存複製到寄存器,覆蓋掉寄存器中原來的值。
儲存:把一個位元組或一個從寄存器複製到主存,並覆蓋主存中原來的值。
操作:把兩個寄存器的內容複寫到ALU,ALU對兩個字做算術運算後存回其中的一個寄存器,該寄存器中原來的值會被覆蓋。
跳轉:從cpu執行的指令抽取一個字的內容存入PC,覆蓋掉原來的值,從而改變下一條要執行的指令,達到跳轉的目的。
最開始shell程式處於執行狀態,等待使用者輸入命令。當我們在shell環境下輸入“.\hello”時,輸入的字元被一個個讀入寄存器並送入主存。
按下斷行符號後,shell程式知道我們已經輸入完成。
由於我們的可執行檔是放在磁碟裡的。所以接下來要做的就是把磁碟的代碼和資料複製到主儲存空間中(通過DMA方式載入程式,則不需要通過CPU,而是將hello可執行檔直接從磁碟複製到主存),如下圖
可執行程式載入到主存後,cpu就執行hello程式的機器指令,而這些指令完成的工作便是將”hello,world\n”這幾個字元從主存中複製寄存器檔案中(register file),再將其從寄存寄檔案中複製到顯示裝置上進行顯示,如圖:
到此,hello world程式從來源程式到成功執行整個流程已經完成了。 5.拓展
1.快取
其實上面的硬體組成並不符合現在的電腦,從上文可以看到記憶體是直接與寄存器進行資料交換的,這樣其實是很浪費資源的,因為現在的cpu的處理速度非常快,但是對記憶體的讀取與其比起來就顯得特別慢,如果寄存器直接與記憶體打交道,效率就會很低,所以前輩們引入了快取(cache)。快取是寄存器與主存的一個過渡,也結合了兩者的優點。快取可以存放比寄存器多的資料,讀取速度又快於主存。所以很多需要頻繁使用的資料就可以放在cache中,這樣就不用每次都去訪問主存,大大提高了效率。
後來這種在較快的處理器與較大較慢的儲存空間之間插入一個過渡裝置的思想得到了普及,就形成了後來的儲存空間分層結構:
2.作業系統管理硬體
在我們的hello程式中,我們都沒有直接操作鍵盤、顯示器、磁碟等,這些事情都是作業系統幫我們做了。作業系統其實就是介於硬體與我們的引用程式之間的一層軟體。
作業系統有兩大功能: 防止硬體被失控的程式操縱。 提供給應用程式提供操縱複雜的低級裝置一個簡單一致的方法。
每天進步一點點