2008/05/06

程式Compile流程

一般寫程式的流程為:編譯-載入-執行。

編譯Compile流程

一個完整的編譯過程通常需要包含以下的4個程序:

  1. Preprocess 預處理器。
  2. Compiler 編譯器。會處理副檔名.c的檔案,產生副檔名.as的組譯檔。
  3. Assembler 組譯器。會處理副檔名.as的組譯檔,產生副檔名.o的中間檔。
  4. Linker 連結器。會處理副檔名.o或.obj的中間檔。
預處理器(Preprocess)

預處理器會在進行編譯前先處理原始碼內像#ifdef、#define相對簡單的詞句替換、和一些巨集代換的功能。

編譯器(Compiler)

編譯器是將高階語言所寫的原始程式,翻譯成機器語言組成的目的程式。在編譯過程中,會執行下列的步驟:

  • 語彙分析(Lexical analysis)。分析程式中每一個字眼(word):註解(comment, 在編譯過程會被編譯器忽略掉)、關鍵字(keyword, 如int、for、while等)、常數(constant, 如1、12、”embedded”等)、運算子(operator, 如+、-、*、/等)。
  • 語法分析(Syntax analysis)。主要是將程式符號,轉換成階層式的語法樹(Syntax tree)符號表示。在這個語法樹中,在正常情況下,階層最高的節點(node)為assign的符號,其餘的節點為其他的運算元符號,而葉子(leaf)就都是變數的標記(token)。
  • 語意分析(Semantic Analysis)。是藉由語法樹(Syntax tree)來分析程式的邏輯與語法是否符合規定。這個階段就是用來分析程式的「文法」是否正確,已經從文字符號的階段進入了程式語意的判別。
  • 中間碼的產生(Intermediate code generate)。是從語法樹(Syntax tree)中,以一個節點(node)為基本單位,從最底層的節點依序往上,拆解成一個個最基本的運算式,而每一個節點也會賦予一個暫時性的符號。
  • 程式碼的最佳化(code optimize)。基本上就是減少一些不必要的暫時性節點符號。當然,另外還有一些特別的最佳化演算法也會在這個階段使用,例如針對迴圈邏輯的最佳化有三種知名的演算法: code motion、induction variable、strength reduction;因為迴圈邏輯在語法上是最沒有執行效率的語法之一,因此需要特別的最佳化。或者,有時候編譯器會調整程式的前後順序,為了在下一階段程式碼的產生過程中,暫存器的使用數目降低。
  • 程式碼的產生(code generate)。以c語言為例,這裡就是將最佳化後的中間碼,搭配微處理器的暫存器,逐一轉換成組合語言。
組譯器(Assembler)

組譯器會將組合語言的原始程式,翻譯成機器語言組成的中間檔 ".obj"或".o"。

連結器(Linker)

連結器會將中間檔 obj files 連結起來,找到symbol(函式,變數名)與程式庫(shared obj)中的副程式,產生可執行的obj檔(executable obj)。

小結

「編譯-載入-執行」方式的缺點是產生的 List檔,其定址 Address 為相對位址,也就是說,每個source的各個section (Absolute section除外) 都由位置 "0" 排起,非常不方便作程式 debug用。但是產生 .obj 檔仍然有一個優點,就是會收錄 .lib 檔,以後要重新 make 時,只需要直接指定使用的 library 即可。

因為目前電腦速度已經非常快,對所有檔案做編譯及連結幾乎都是瞬間完成,所產生的 List檔已包含最終的正確位址,用來作為程式的 Debug 非常好用,如果沒有特殊需要的話,已經沒有必要產生中間檔 .obj。

2 則留言:

  1. 不一定要有 preprocessor, 那是針對 c 語言.
    另外針對你的小結, 似乎有點倒果為因了. 之所以產生的皆為相對位址, 是因為 cpu 的 segment 機制. 但這個情況在 virtual memory 這項技術出現後已經獲得紓解. 事實上在 Linux 下, 產生的 executable 是假設不被重新定址的(relocatable), 因此產生的 executable 中各個 sh_addr(在 section descriptor 中) 的數字就是你程式執行時載入的 virtual address.
    回到你的問題. 你提到 List 的部分是因為 M$ 並不排除 executable 可以被 relocate. 但事實上這在已經有 virtual memory 的現代系統下很不必要. 我猜測 M$ 會支援這樣的特性也許是因為相容性.
    總結來說. 你提到的缺點並不是 編譯-載入-執行 原罪

    回覆刪除
  2. Hi mitnick,
    謝謝你的說明和指正。
    這篇文章是我剛了解計算機原理時所寫的,所以是當時所理解的內容。事實上這篇文章也有很多人詬病,認為有誤導之嫌,但卻一直沒有人說出問題點,你是第一個,為此我得謝謝你。
    這篇文章我會再找足夠資料時後更新,或是你願意提供我更多的資料,如果是,我會更感謝你。

    回覆刪除