登錄

編譯器

1.什么是編譯器

  編譯器是指從高級(jí)語(yǔ)言到低級(jí)語(yǔ)言的翻譯器,同樣的技術(shù)可用于不同種類語(yǔ)言之間的翻譯。編譯器是一種電腦程序,它會(huì)將用某種編程語(yǔ)言寫成的源代碼(原始語(yǔ)言),轉(zhuǎn)換成另一種編程語(yǔ)言(目標(biāo)語(yǔ)言)。

  它主要的目的是將便于人編寫,閱讀,維護(hù)的高級(jí)計(jì)算機(jī)語(yǔ)言所寫作的源代碼程序,翻譯為計(jì)算機(jī)能解讀、運(yùn)行的低階機(jī)器語(yǔ)言的程序,也就是可執(zhí)行文件。編譯器將原始程序(Source program)作為輸入,翻譯產(chǎn)生使用目標(biāo)語(yǔ)言(Target language)的等價(jià)程序。源代碼一般為高階語(yǔ)言 (High-level language), 如 Pascal、C、C++、C# 、Java 等,而目標(biāo)語(yǔ)言則是匯編語(yǔ)言或目標(biāo)機(jī)器的目標(biāo)代碼Object code),有時(shí)也稱作機(jī)器代碼(Machine code)。

  一個(gè)現(xiàn)代編譯器的主要工作流程如下: 源代碼 (source code) → 預(yù)處理器 (preprocessor) → 編譯器 (compiler) → 匯編程序 (assembler) → 目標(biāo)代碼 (object code) → 鏈接器 (Linker) → 可執(zhí)行文件 (executables)

2.編譯器的分類[1]

  典型的編譯器輸出是由包含人口點(diǎn)的名字和地址以及外部調(diào)用的機(jī)器代碼所組成的目標(biāo)文件。一組目標(biāo)文件,不必是同一編譯器產(chǎn)生,但使用的編譯器必須采用同樣的輸出格式,可以鏈接在一起并生成可以由用戶直接執(zhí)行的可執(zhí)行程序。

  在運(yùn)行過程中,編譯器又可分成只依賴于源語(yǔ)言的編譯器前端和只依賴于目標(biāo)語(yǔ)言的編譯器后端兩大部分,編譯器前后端結(jié)構(gòu)如圖所示。

  Image:圖編譯器前后端示意圖.jpg

  前端主要負(fù)責(zé)解析(parse)輸入的源程序,由詞法分析器和語(yǔ)法分析器協(xié)同工作。詞法分析器負(fù)責(zé)把源程序中的“單詞”(Token)找出來(lái),語(yǔ)法分析器把這些分散的單詞按預(yù)先定義好的語(yǔ)法組裝成有意義的表達(dá)式、語(yǔ)句、函數(shù)等。例如“a=b+c”,前端詞法分析器看到的是“'a'='b'+'c'”,語(yǔ)法分析器按定義的語(yǔ)法,先把它們組裝成表達(dá)式“b+C”,再組裝成“a=b+c”的語(yǔ)句。前端還負(fù)責(zé)語(yǔ)義(semantic checking)的檢查,例如檢測(cè)參與運(yùn)算的變量是否是同一類型的,簡(jiǎn)單的錯(cuò)誤處理。最終的結(jié)果常常是一個(gè)抽象的語(yǔ)法樹AST(Abstract Syntax Tree),這樣后端可以在此基礎(chǔ)上進(jìn)一步優(yōu)化處理。編譯器后端主要負(fù)責(zé)分析、優(yōu)化中間代碼以及生成機(jī)器代碼。一般來(lái)說(shuō)所有的編譯器分析、優(yōu)化、變型都可以分成兩大類:函數(shù)內(nèi)進(jìn)行和函數(shù)間進(jìn)行。很明顯,函數(shù)間的分析優(yōu)化更準(zhǔn)確,但需要更長(zhǎng)的時(shí)間來(lái)完成。

  一般編譯器可以分為以下兩類:

 ?、佟氨镜亍本幾g器:編譯器可以生成用來(lái)在與編譯器本身所在的計(jì)算機(jī)和操作系統(tǒng)(平臺(tái))相同的環(huán)境下運(yùn)行的目標(biāo)代碼。

 ?、诮徊婢幾g器:編譯器也可以生成用來(lái)在其他平臺(tái)上運(yùn)行的目標(biāo)代碼,交叉編譯器在生成新的硬件平臺(tái)時(shí)非常有用。

  交叉編譯這個(gè)概念的出現(xiàn)和流行是和嵌入式系統(tǒng)的廣泛發(fā)展同步的。在進(jìn)行嵌入式系統(tǒng)的開發(fā)時(shí),運(yùn)行程序的目標(biāo)平臺(tái)通常只有有限的存儲(chǔ)空間和運(yùn)算能力。例如常見的ARM平臺(tái),其一般的靜態(tài)存儲(chǔ)空問為16~32MB,而處理器的主頻為100~500MHz。這種情況下,在ARM平臺(tái)上進(jìn)行本機(jī)編譯就不太可能了,這是因?yàn)橐话愕木幾g工具需要很大的存儲(chǔ)空間,并需要很強(qiáng)的處理器運(yùn)算能力。為了解決這個(gè)問題,交叉編譯工具就應(yīng)運(yùn)而生了。通過交叉編譯工具,就可以在CPU能力很強(qiáng)、存儲(chǔ)控件足夠的主機(jī)平臺(tái)上(例如通用計(jì)算機(jī))編譯出針對(duì)其他運(yùn)行平臺(tái)的可執(zhí)行程序。

  在一種計(jì)算機(jī)環(huán)境中運(yùn)行的編譯程序,能編譯出在另外一種環(huán)境下運(yùn)行的代碼,這個(gè)編譯過程就叫交叉編譯。也就是在主機(jī)平臺(tái)上開發(fā)程序,并在這個(gè)平臺(tái)上運(yùn)行交叉編譯器,編譯出程序,這個(gè)編譯程序?qū)⒃谀繕?biāo)平臺(tái)上運(yùn)行。這里需要注意的是所謂平臺(tái),實(shí)際上包含兩個(gè)概念:體系結(jié)構(gòu)和操作系統(tǒng)。同一個(gè)體系結(jié)構(gòu)可以運(yùn)行不同的操作系統(tǒng);同樣,同一個(gè)操作系統(tǒng)也可以在不同的體系結(jié)構(gòu)上運(yùn)行。舉例來(lái)說(shuō),常說(shuō)的X86Linux平臺(tái)實(shí)際上是IntelX86體系結(jié)構(gòu)和LinuxforX86操作系統(tǒng)的統(tǒng)稱;而X86WinNT平臺(tái)實(shí)際上是IntelX86體系結(jié)構(gòu)和WindowsNTforX86操作系統(tǒng)的簡(jiǎn)稱。

  在交叉編譯技術(shù)中有兩種比較典型的實(shí)現(xiàn),一個(gè)稱之為Java模式,即Java的字節(jié)碼編譯技術(shù);另一個(gè)稱之為GNUGCC模式,即通常所講的CrossGCC技術(shù)。Java模式(如圖所示)的最大特點(diǎn)是引入了一個(gè)自定義的虛擬機(jī),即Java虛擬機(jī)JVM(Java Virtual Machine)。所有Java源程序都會(huì)首先被編譯成只在這個(gè)虛擬機(jī)上才能執(zhí)行的“目標(biāo)代碼”:字節(jié)碼(Bytecode)。在實(shí)時(shí)運(yùn)行時(shí),可以有兩種運(yùn)行方式:一種是編譯所獲得的字節(jié)碼由JVM在實(shí)際計(jì)算機(jī)系統(tǒng)上執(zhí)行;另一種方式是通過Java實(shí)時(shí)編譯器(Just-In-TimeCompiler)將字節(jié)碼首先轉(zhuǎn)換成本地機(jī)可直接執(zhí)行的目標(biāo)代碼,而后交給實(shí)際的計(jì)算機(jī)系統(tǒng)運(yùn)行。這實(shí)際上是一個(gè)兩次編譯過程,一次是非實(shí)時(shí)的,一次是實(shí)時(shí)的。由于第一次是非實(shí)時(shí)編譯,Java編譯器生成的是基于JVM的“目標(biāo)代碼”,可以將它的編譯技術(shù)也稱為交叉編譯。

  Image:圖Java模式交叉編譯.jpg

  GCC模式(如圖所示)通過CrossGCC直接生成目標(biāo)平臺(tái)的目標(biāo)代碼,從而能夠直接在目標(biāo)平臺(tái)上運(yùn)行。這里的關(guān)鍵是CrossGCC的生成和選擇問題。需要根據(jù)目標(biāo)平臺(tái)的不同,選擇針對(duì)這個(gè)平臺(tái)的CrossGCC。GCC模式和Java模式的最大不同在于GCC直接生成目標(biāo)平臺(tái)的目標(biāo)代碼,而Java模式首先只是生成字節(jié)碼,只有在有JIT編譯器的參與下才會(huì)進(jìn)一步生成目標(biāo)平臺(tái)的目標(biāo)代碼。研究表明,Java模式雖然可以通過兩個(gè)編譯過程生成目標(biāo)代碼,但是因?yàn)閮纱尉幾g的優(yōu)化存在相互沖突,最終的目標(biāo)代碼的執(zhí)行效率也不是很高。而GCC模式由于直接能夠生成目標(biāo)代碼,其執(zhí)行效率一般很高。

  Image:圖GCC模式交叉編譯.jpg

評(píng)論  |   0條評(píng)論