編譯器
1.什么是編譯器
編譯器是指從高級語言到低級語言的翻譯器,同樣的技術可用于不同種類語言之間的翻譯。編譯器是一種電腦程序,它會將用某種編程語言寫成的源代碼(原始語言),轉換成另一種編程語言(目標語言)。
它主要的目的是將便于人編寫,閱讀,維護的高級計算機語言所寫作的源代碼程序,翻譯為計算機能解讀、運行的低階機器語言的程序,也就是可執(zhí)行文件。編譯器將原始程序(Source program)作為輸入,翻譯產(chǎn)生使用目標語言(Target language)的等價程序。源代碼一般為高階語言 (High-level language), 如 Pascal、C、C++、C# 、Java 等,而目標語言則是匯編語言或目標機器的目標代碼(Object code),有時也稱作機器代碼(Machine code)。
一個現(xiàn)代編譯器的主要工作流程如下: 源代碼 (source code) → 預處理器 (preprocessor) → 編譯器 (compiler) → 匯編程序 (assembler) → 目標代碼 (object code) → 鏈接器 (Linker) → 可執(zhí)行文件 (executables)
2.編譯器的分類[1]
典型的編譯器輸出是由包含人口點的名字和地址以及外部調用的機器代碼所組成的目標文件。一組目標文件,不必是同一編譯器產(chǎn)生,但使用的編譯器必須采用同樣的輸出格式,可以鏈接在一起并生成可以由用戶直接執(zhí)行的可執(zhí)行程序。
在運行過程中,編譯器又可分成只依賴于源語言的編譯器前端和只依賴于目標語言的編譯器后端兩大部分,編譯器前后端結構如圖所示。
前端主要負責解析(parse)輸入的源程序,由詞法分析器和語法分析器協(xié)同工作。詞法分析器負責把源程序中的“單詞”(Token)找出來,語法分析器把這些分散的單詞按預先定義好的語法組裝成有意義的表達式、語句、函數(shù)等。例如“a=b+c”,前端詞法分析器看到的是“'a'='b'+'c'”,語法分析器按定義的語法,先把它們組裝成表達式“b+C”,再組裝成“a=b+c”的語句。前端還負責語義(semantic checking)的檢查,例如檢測參與運算的變量是否是同一類型的,簡單的錯誤處理。最終的結果常常是一個抽象的語法樹AST(Abstract Syntax Tree),這樣后端可以在此基礎上進一步優(yōu)化處理。編譯器后端主要負責分析、優(yōu)化中間代碼以及生成機器代碼。一般來說所有的編譯器分析、優(yōu)化、變型都可以分成兩大類:函數(shù)內進行和函數(shù)間進行。很明顯,函數(shù)間的分析優(yōu)化更準確,但需要更長的時間來完成。
一般編譯器可以分為以下兩類:
?、佟氨镜亍本幾g器:編譯器可以生成用來在與編譯器本身所在的計算機和操作系統(tǒng)(平臺)相同的環(huán)境下運行的目標代碼。
?、诮徊婢幾g器:編譯器也可以生成用來在其他平臺上運行的目標代碼,交叉編譯器在生成新的硬件平臺時非常有用。
交叉編譯這個概念的出現(xiàn)和流行是和嵌入式系統(tǒng)的廣泛發(fā)展同步的。在進行嵌入式系統(tǒng)的開發(fā)時,運行程序的目標平臺通常只有有限的存儲空間和運算能力。例如常見的ARM平臺,其一般的靜態(tài)存儲空問為16~32MB,而處理器的主頻為100~500MHz。這種情況下,在ARM平臺上進行本機編譯就不太可能了,這是因為一般的編譯工具需要很大的存儲空間,并需要很強的處理器運算能力。為了解決這個問題,交叉編譯工具就應運而生了。通過交叉編譯工具,就可以在CPU能力很強、存儲控件足夠的主機平臺上(例如通用計算機)編譯出針對其他運行平臺的可執(zhí)行程序。
在一種計算機環(huán)境中運行的編譯程序,能編譯出在另外一種環(huán)境下運行的代碼,這個編譯過程就叫交叉編譯。也就是在主機平臺上開發(fā)程序,并在這個平臺上運行交叉編譯器,編譯出程序,這個編譯程序將在目標平臺上運行。這里需要注意的是所謂平臺,實際上包含兩個概念:體系結構和操作系統(tǒng)。同一個體系結構可以運行不同的操作系統(tǒng);同樣,同一個操作系統(tǒng)也可以在不同的體系結構上運行。舉例來說,常說的X86Linux平臺實際上是IntelX86體系結構和LinuxforX86操作系統(tǒng)的統(tǒng)稱;而X86WinNT平臺實際上是IntelX86體系結構和WindowsNTforX86操作系統(tǒng)的簡稱。
在交叉編譯技術中有兩種比較典型的實現(xiàn),一個稱之為Java模式,即Java的字節(jié)碼編譯技術;另一個稱之為GNUGCC模式,即通常所講的CrossGCC技術。Java模式(如圖所示)的最大特點是引入了一個自定義的虛擬機,即Java虛擬機JVM(Java Virtual Machine)。所有Java源程序都會首先被編譯成只在這個虛擬機上才能執(zhí)行的“目標代碼”:字節(jié)碼(Bytecode)。在實時運行時,可以有兩種運行方式:一種是編譯所獲得的字節(jié)碼由JVM在實際計算機系統(tǒng)上執(zhí)行;另一種方式是通過Java實時編譯器(Just-In-TimeCompiler)將字節(jié)碼首先轉換成本地機可直接執(zhí)行的目標代碼,而后交給實際的計算機系統(tǒng)運行。這實際上是一個兩次編譯過程,一次是非實時的,一次是實時的。由于第一次是非實時編譯,Java編譯器生成的是基于JVM的“目標代碼”,可以將它的編譯技術也稱為交叉編譯。
GCC模式(如圖所示)通過CrossGCC直接生成目標平臺的目標代碼,從而能夠直接在目標平臺上運行。這里的關鍵是CrossGCC的生成和選擇問題。需要根據(jù)目標平臺的不同,選擇針對這個平臺的CrossGCC。GCC模式和Java模式的最大不同在于GCC直接生成目標平臺的目標代碼,而Java模式首先只是生成字節(jié)碼,只有在有JIT編譯器的參與下才會進一步生成目標平臺的目標代碼。研究表明,Java模式雖然可以通過兩個編譯過程生成目標代碼,但是因為兩次編譯的優(yōu)化存在相互沖突,最終的目標代碼的執(zhí)行效率也不是很高。而GCC模式由于直接能夠生成目標代碼,其執(zhí)行效率一般很高。