架構編譯器的感悟

這所謂的編譯器就是將c語言代碼編譯爲機器代碼的,先將C編譯爲彙編代碼,再由彙編器將彙編代碼編譯爲機器代碼,CPU執行的是機器代碼編程


忽然發覺好像不少書都這麼說,不少人也這麼說,因而很天然的記住了,可是,我忽然想起了,這但是隱藏着一些道理。網絡


1,C編譯爲彙編,對於這個過程,應該是平臺無關的,具體是怎麼實現的?這個是由C編譯器開發商來處理,總之,若是我用IAR ARM的話,那麼一樣的main函數,編譯出來應該是獲得 ARM 格式的彙編代碼,也就是說,使用的是 ARM 指令集,一樣道理,若是是 GCC,也產生相似的,不過由於他們的內部實現並不同,因此出來的結果可能有差別,不過最後的結果是一致的,也就是獲得對應平臺的彙編代碼。架構


2,彙編器將彙編代碼翻譯爲機器代碼,其實這個過程比較簡單的,有點像查表,通常廠家都會免費提供這麼一個編譯器,不然MCU就不用賣了,通常C編譯都會本身實現一個。這讓我想起了,什麼是架構 Architecture,一個CPU怎麼尋址,指令集是什麼,堆棧指針是怎麼變化的,PC程序指針是怎麼變化的,怎麼取指令,這些東西都是每一個CPU區別於其餘CPU所特有的東西,也就是一個CPU設計的時候所面臨的內容。這就是架構,還記得學校學MCS-51的時候說得最多的是什麼麼?對,尋址和指令集,這個就是架構,老師想你學51這種架構,而不單純是學怎麼用這個芯片。函數


3,再說回上層的,ARM是一個核心,是一個架構,他制定了指令集,他指定了SP指針的訪問方式,他指定了怎麼尋址,因此說「ARM架構」學習

而後不少廠家根據ARM架構生產的芯片,由於你就一個核心沒用啊?他就會執行指令,其餘什麼都不懂,咱們須要外設,須要不少外圍器件,例如I2C,USB什麼的,和ARM這個核心一同構成一個完整意義上的芯片,咱們叫 SOC (system on chip)單片系統,由於這個系統已經具有了基本跑起來的條件了,再在外圍加上適當的復位和晶振電路,那基本就沒有什麼問題了。翻譯


從這我又想到了,那既然是同一個核心出來的,每一個廠家生產芯片有什麼不一樣呢?其實說白了就是外圍器件的不一樣,例如ARM9芯片,三星的S3C2440,核心是 920T集成了LCD控制器,NAND FLASH 控制器,然而 Atmel的 AT91RM9200,核心一樣是 ARM架構的 920T,可是他沒有LCD控制器,卻集成了網絡方面的功能,這樣看來,在一個架構上面不一樣廠家添加不一樣的功能器件,而獲得了各類各樣的芯片,也就是SOC了。設計


繼續聯想,不一樣的SOC之間其實還真的沒有什麼本質性的差異,第1,2點也說明了,C是萬國語言,他到底也是通用的,因此同一段MAIN函數,若是你什麼都不寫,那麼編譯出來,在每個soc都跑得通。而爲何基於這個芯片開發的程序就不能用到拿個芯片呢?其實一個重要的問題就是地址的訪問,尋址的問題。ARM 32位總線固定了 4GB的尋址空間,可是ARM並無硬性的規定哪一個地址應該作什麼,哪一個地址不能作什麼,具體怎麼作,是SOC廠商說了算,因此產生的差別就是,可能這個soc裏面的uart模塊地址爲 0x50000000 ,而另一個廠商的soc的uart模塊地址爲 0x40000000 ,C語言的器件編程,說白了不就是對某個地址放某些數據,例如我要設置UART,很簡單,將正確的數據送到正確的地址,也就是UART模塊所在的地址,那麼一切都完工了。指針


這裏體現了很重要的一點,就是所謂的編程就是在什麼地址放什麼數據或者要什麼數據。從這個抽象層面來看,編譯器能夠很輕鬆的編譯同一個核心的不一樣SOC,由於不一樣核心,那牽涉到架構的問題,要修改編譯器內部的實現代碼,可是同一個架構同一個核心的,對不一樣芯片的訪問的區別,就在於不一樣的地址!!ip


若是對S3C2440的編程,核心是 ARM 920T ,咱們應該重點學習什麼?開發

應該學習通用的,若是理解上面的內容,就知道什麼是通用的了。那就是架構,搞懂了ARM 920T的架構,那麼你使用的幾個芯片又有什麼差異呢?最多就是對着那個新的功能模塊研究一下怎麼設置。


4,再談談編譯器

其實編譯器實現的目的都是一個,機器代碼,只是實現的辦法和過程並不相同而已。那麼對於衆多的編譯器,咱們應該選擇哪一個?其實選擇哪一個都沒有所謂,他們都一樣的強大,ARMCC, GCC, IAR KEIL-MDK都一樣強大,只要你學會了用,總能知足你的須要。可是,是否是每一個編譯器就各自爲政呢?那倒不必定,因此呢,學習,是須要抓住核心

什麼是核心?有幾點

1)main函數執行以前究竟作了些什麼

可能在學校不少老師都教,C語言的入口在main函數,全部的代碼從這裏開始!可是真的嗎?其實並非的,main函數執行以前須要必要的環境,這個環境由誰來提供?其實,在你的C語言程序鏈接的時候,一個初始化的模塊(通常是叫 crt0.o ,也就是 c running time的縮寫)已經悄悄的被鏈接進去了。這是每一個編譯器都會作的,關鍵是,這個crt0 的模塊是編譯器默認提供的仍是你本身去實現。若是是編譯器自帶的,那麼你應該怎麼去作部分修改以達到本身的目的,若是是本身寫,那又應該怎麼去寫?這就是學習的重點了。

初始化模塊主要的工做:設置棧,設置堆(heap,主要是爲C庫的malloc服務),.data的搬移(若是須要的話),.bss段的初始化,跳轉到 main函數。這個就是共性,不管什麼編譯器,都是在作這幾步而已,不一樣的只是用各個編譯器各自的語法去實現,可是本質也是不變的,固然,你也能夠本身去實現,在鏈接的時候鏈接進來就OK了。

2)怎麼處理鏈接(link)

編譯的過程基本不須要去關心,編譯器都作得好好的,你也沒有干預的餘地,最多就是傳遞一些不一樣的編譯參數,這個稍微看看自帶的文檔,或者乾脆用到的時候再翻,也問題不大。真正的大問題出在鏈接。

編譯的過程獲得的obj文件,是徹底和地址無關的,例如,每一個源文件對應的obj文件,地址都是從0開始的,它真正鏈接的時候才由鏈接器linker分配真正運行時候的地址,因此,你要想處理每一個具體的代碼段運行的時候應該在什麼地方,就要注意學習link了。

其中指導linker工做的就是對應的腳本 linker scrpit,鏈接腳本,一樣,這是一個共性,每一個編譯器都確定有,只是實現辦法的不一樣,腳本具體的編寫的語法不一樣,因此這個是應該重點研究的,讓你的代碼聽聽話話,就花多點功夫去研究這個吧。


因此,每遇到一個編譯器,先了解他怎麼初始化,而後研究它怎麼鏈接,瞭解了以後基本上你就會用這個編譯器了,剩下的就不用說了,上課老師也說得夠多了,怎麼從main函數開始寫,云云。

相關文章
相關標籤/搜索