計算機系統漫遊

劉小緒同學發表於2018-10-31

計算機中的資訊

    對於程式設計師來說,HelloWorld 程式再熟悉不過了,下面是 C 語言寫的 HelloWorld 程式。

#include<stdio.h>

int main()
{
	printf("hello, world\n");
	return 0;
}
複製程式碼

    這段程式很簡單,僅僅是在螢幕上輸出hello world,對於程式設計師很容易閱讀,但是從計算機的角度來看,也是這麼簡單嗎?

    我們都知道,計算機的世界只有 0 和 1組成的位(位元),這對於人類來說太不友好了,因此電腦科學家們設計了 ASCⅡ 碼來表示現實世界的符號。每 8 個位構成一個組(稱為一個位元組),用一個組來表示一個符號,上面的 C 程式用 ASCⅡ 表示就是下面的樣式。

image

    因此對於計算機來說,所有的資料都是由一串位元表示的,要區分這些資料物件的唯一方法就是讀到它們時的上下文

資訊 = 位 + 上下文
複製程式碼

源程式編譯

    雖然我們把 C 語言程式編寫完成了,但是它並不能執行,因為它目前還僅僅是由 ASCⅡ 字元構成的文字檔案,計算機並不能執行文字檔案。

    想要得到 HelloWorld 的可執行檔案,需要經過預處理、編譯、彙編、連結四個階段。

    預處理階段,前處理器會根據以字元#開頭的命令去修改源程式,前處理器去讀取系統標頭檔案stdio.h中的內容,並將其直接插入程式文字中,結果就得到了另一個 C 程式—— hello.i。

    編譯階段,編譯器會把 hello.i 程式翻譯成組合語言程式 hello.s,組合語言本質上就是機器語言,此時的程式仍然是文字檔案。

    彙編階段,彙編器會將 hello.s 翻譯成機器語言指令,儲存在 hello.o 檔案中,此時得到的就是二進位制檔案了。

    程式中使用了 printf 函式,這是由編譯器提供的標準 C 庫中的函式,它儲存在 printf.o 檔案中,連結器會將這個檔案合併到 hello.o 中,結果就得到一個可執行的 hello 檔案,儲存於磁碟中。

image

系統組成及程式執行

    在執行可執行檔案 hello 檔案之前,應該先了解一下系統的硬體組成,一個典型系統的硬體組織如下圖所示。

image

    為了執行 hello 程式,我們需要在 shell 中輸入指令./hello,指令通過鍵盤經 I/O 匯流排 --> I/O 橋 --> 匯流排介面 --> 暫存器 --> I/O 橋存於主存中,當敲擊Enter鍵時,等於告訴 shell 程式,命令的屬於已經結束, shell 將會執行一系列指令來載入 hello 檔案。

    利用**直接存取(DMA)**技術,hello 檔案不需要通過暫存器就能到達主存,當目標檔案 hello 到達主存中,處理器就開始執行 hello 程式的機器指令。其指令即將 "hello, world\n" 字串的位元組從主存複製到暫存器,再從暫存器複製到顯示裝置中,最終顯示在螢幕上面。

    我們發現這個簡單的 HelloWorld 程式會讓系統花費大量的時間把資訊從一個地方挪到另一個地方,而我們都清楚,暫存器、主存、磁碟之間的訪問速度是相互差了好幾個數量級的,而這種複製的開銷會嚴重減慢程式的執行,為了加快這些複製操作的速度,系統設計者就引入了快取記憶體

抽象的重要性

    電腦科學中最為重要的概念之一就是抽象,指令集提供了對硬體處理器的抽象,作業系統同樣通過程式、虛擬記憶體、檔案(磁碟、鍵盤、網路都可以看成檔案)這幾個抽象概念為應用程式提供簡單一致的機制來控制低階硬體裝置,同時防止硬體被失控的應用程式濫用。

image

    程式是對正在執行的程式的抽象,在一個系統上可以同時執行多個程式,但是每個程式看起來都好像在獨佔的使用硬體,CPU 看上去是在併發的執行多個程式,這就需要作業系統進行上下文切換。

    在我們還沒有輸入./hello之前,只有 shell 程式在執行,當我們讓其執行 hello 程式時,shell 會通過系統呼叫來執行我們的請求。作業系統首先儲存 shell 程式的上下文(PC、暫存器等資訊),然後建立一個新的 hello 程式,並將控制權轉交給 hello 程式。

image

相關文章