C++程式的執行過程
要理解一個程式的執行過程,首先要理解什麼是編譯器。我們用C語言寫的程式,計算機只能識別機器語言(二進位制指令),計算機並不能理解。因此,必須 將C語言轉換為機器語言。編譯器就是用來實現這一功能的。編譯器將原始碼檔案按照一定的對應規則對映程計算機能夠識別的二進位制指令。
關於原始檔、目標檔案和可執行檔案
原始檔:儲存原始碼的檔案(*.c, *.cpp)
目標檔案:編譯器根據原始檔建立的機器語言指令成為目的碼,包含目的碼的磁碟檔案即目標檔案。
一般為(*.obj)檔案,Linux下為(*.o)檔案。
編譯完原始碼後生成的目標檔案並不能直接執行。一般我們編寫的程式都會引用基本的庫函式,因此在執行程式之前還要把庫函用定義好的目的碼替換,這個過程稱為連結。就是把自己寫的原始碼的目標檔案與庫函式的目標檔案組合起來,生成最終的可執行檔案。連結由連結器完成。
一般程式的執行過程:
程式先由程式設計師編輯建立並儲存在硬碟上,源程式在機器上表現為01組成的位序列,8個位一個位元組,每個位元組表示一個文字字元,它是以ascii碼的形式表示的。只由ascii字元組成的檔案叫做文字檔案,所有其他的叫做二進位制檔案。
為了讓機器能夠識別並執行程式,每條語句必須被轉為低階機器語言指令,然後將指令按照可執行目標程式的格式打包,並以二進位制磁碟檔案的形式存放起來。以c程式為例,轉換過程大致分為預處理,編譯,彙編,連結四個步驟。下面進行詳細解釋。
前處理器根據以字元#開頭的命令修改原始的c程式,比如#include<stdio.h>告訴前處理器讀取系統標頭檔案stdio.h的內容,並把它直接插入程式文字中,將#define的變數替換等等,結果得到了另一個c程式,通常以.i作為副檔名。
編譯器將文字檔案hello.i翻譯成hello.s,它包含了一個組合語言程式。組合語言程式中的每條語句都以一種標準的文字格式確切的描述一條低階機器語言指令。
彙編器將彙編程式翻譯為機器語言指令,把這些指令打包成可重定位目標程式(relocateble object program)的格式,並把結果儲存在hello.o中。hello.o檔案是二進位制檔案,因為他的位元組編碼是機器語言指令而不是ascii碼。如果用文字編輯器開啟hello.o會看到一堆亂碼。
連結階段,比如hello中用到了printf函式,這是標準c庫的函式,存在於一個名為printf.o的單獨編譯好的目標檔案中,這個檔案必須以某種方式合併到我們編譯好的的目標檔案中。連結器(ld)程式負責處理這種合併,結果得到hello檔案,它是可執行目標檔案,可以被載入到記憶體中,由系統執行。
轉換完成之後就是執行了。在unix系統中,shell是一個命令列直譯器,輸出一個提示符,等待使用者輸入然後執行命令。如果輸入的第一個單詞不是一個內建的shell命令,shell將其解釋為可執行檔案,比如輸入./hello,它將載入並執行這個檔案。hello在螢幕上輸出資訊,然後終止。shell輸出一個提示符,等待下一個輸入的命令列。具體的過程為:初始時,shell執行它的指令,等待輸入。使用者輸入字元創“./hello”後,shell將字元逐一讀入暫存器,然後存放到儲存器中,敲回車鍵後,shell知道使用者結束命令輸入。然後shell執行一系列的指令來載入可執行的hello檔案,將hello目標檔案的程式碼和資料從磁碟複製到主存,資料包含輸出的字串"HELLO,WORLD\n"。一旦目標檔案hello中的程式碼和資料被載入到主存,處理器開始執行main的機器語言指令,將字串從主存拷貝到暫存器,並輸出到螢幕上。
由於涉及大量的主存,磁碟,暫存器通訊,故產生了cache等緩衝提高速度的裝置,減少通訊阻塞。
為了減少使用者的負擔,作業系統對計算機硬體資源進行了抽象,產生了程序,執行緒,虛擬地址等概念。程序是程式的一次執行,是作業系統分配資源的單位,多個程序是可以併發執行的,併發執行實際上每個時刻執行的還是一個程序,只不過程序間切換的速度比較快,給人的感覺是併發執行。作業系統為每個程序儲存執行的狀態資訊,稱為上下文,包括pc和暫存器檔案當前值,主存內容等等。切換程序時,發生上下文切換。一個程序中可以有多個執行緒執行單元,每個執行緒都執行在程序的上下文中,共享同樣的程式碼和資料,由於網路伺服器等應用對並行處理的需求越來越大,多執行緒模型也越來越重要。虛擬地址為每個程序提供了一個假象,即每個程序都在獨佔主存,每個程序看到的是一致的儲存器,稱為虛擬地址空間。虛擬地址空間是由大量的準確定義的區構成,linux從低地址到高地址依次為:程式程式碼和資料;堆;共享庫;棧;核心虛擬儲存器。