當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 嵌入式系統(tǒng)移植步驟
在嵌入式系統(tǒng)移植中重要的一部分是操作系統(tǒng)的移植,與其它操作系統(tǒng)相比,Linux大的特點(diǎn):它是一款遵循GPL的操作系統(tǒng),我們可以自由地使用、修改、和擴(kuò)展它。正是由于這一特色,嵌入式系統(tǒng)移植過程中Linux系統(tǒng)受到越來越多人士的青睞。于是,一個(gè)經(jīng)常會(huì)被探討的問題出現(xiàn)了,即關(guān)于Linux系統(tǒng)的移植。對于操作系統(tǒng)而言,這種移植通常是跨平臺的、與硬件相關(guān)的,即硬件系統(tǒng)結(jié)構(gòu)、甚至CPU不同。下面就讓我們來看看在嵌入式Linux系統(tǒng)移植方面,我們都需要做些什么。
一、Linux系統(tǒng)移植的兩大部分
對于系統(tǒng)移植而言,Linux系統(tǒng)實(shí)際上由兩個(gè)比較獨(dú)立的部分組成,即內(nèi)核部分和系統(tǒng)部分。通常啟動(dòng)一個(gè)Linux系統(tǒng)的過程是這樣的:一個(gè)不隸屬于任何操作系統(tǒng)的加載程序?qū)inux部分內(nèi)核調(diào)入內(nèi)存,并將控制權(quán)交給內(nèi)存中Linux內(nèi)核的第一行代碼。加載程序的工作就完了,此后Linux要將自己的剩余部分全部加載到內(nèi)存(如果有的話,視硬件平臺的不同而不同),初始化所有的設(shè)備,在內(nèi)存中建立好所需的數(shù)據(jù)結(jié)構(gòu)(有關(guān)進(jìn)程、設(shè)備、內(nèi)存等)。到此為止Linux內(nèi)核的工作告一段落,內(nèi)核已經(jīng)控制了所有硬件設(shè)備。至于操作和使用這些硬件設(shè)備,則輪到系統(tǒng)部分上場了。內(nèi)核加載根設(shè)備并啟動(dòng)init守護(hù)進(jìn)程,init守護(hù)進(jìn)程會(huì)根據(jù)配置文件加載文件系統(tǒng)、配置網(wǎng)絡(luò)、服務(wù)進(jìn)程、終端等。一旦終端初始化完畢,我們就會(huì)看到系統(tǒng)的歡迎界面了。小結(jié)一下:
(1)內(nèi)核部分初始化和控制所有硬件設(shè)備(嚴(yán)格說不是所有,而是絕大部分),為內(nèi)存管理、進(jìn)程管理、設(shè)備讀寫等工作做好一切準(zhǔn)備。
(2)系統(tǒng)部分加載必需的設(shè)備,配置各種環(huán)境以便用戶可以使用整個(gè)系統(tǒng)。
二、系統(tǒng)移植所必需的環(huán)境
在進(jìn)一步敘述之前,我們有必要提一下做系統(tǒng)移植所必需的環(huán)境。
首先,需要一個(gè)新版本的gcc。對于一個(gè)準(zhǔn)備系統(tǒng)移植的程序員而言,“新”到什么程度應(yīng)該心里有數(shù)。做跨平臺編譯,gcc也許是好的選擇。另外,Linux內(nèi)核依賴許多gcc特有的特性,非它不可。如果你已經(jīng)會(huì)使用gcc并實(shí)地操練過多回,那你只需要再進(jìn)一步鞏固一下跨平臺編譯的操作即可。兩種編譯環(huán)境是可用的:非目標(biāo)平臺上的Linux或目標(biāo)平臺上的非Linux系統(tǒng),除非你的開發(fā)平臺過于特殊,否則你一定能夠找到你能用的gcc。
其次,編譯鏈接庫是必需的,而且必須是目標(biāo)平臺的編譯鏈接庫。通常這是一個(gè)枯燥、繁瑣、又絲毫沒有成就感的過程。幸運(yùn)的話,會(huì)有現(xiàn)成的鏈接庫可以用。否則,你需要自己用gcc建立它。
后,需要目標(biāo)平臺的所有文檔,越多越好。如果有一定的開發(fā)支持/仿真環(huán)境,Loader(加載程序)則好,這些可以幫助你減少移植過程中浪費(fèi)在瑣事上的時(shí)間。
三、Linux系統(tǒng)移植
接下來我們從內(nèi)核和系統(tǒng)兩個(gè)方面描述一下移植中的關(guān)鍵。
(1) 內(nèi)核移植
Linux系統(tǒng)采用了相對來說并不是很靈活的單一內(nèi)核機(jī)制,但這絲毫沒有影響Linux系統(tǒng)的平臺無關(guān)性和可擴(kuò)展性。Linux使用了兩種途徑分別解決這些問題,很干凈利落,絲毫不拖泥帶水,而且十分清晰易懂。分離硬件相關(guān)代碼和硬件無關(guān)代碼,使上層代碼永遠(yuǎn)不必關(guān)心低層換用了什么代碼,如何完成了操作。不論對x86上還是在Alpha平臺上分配一塊內(nèi)存,對上層代碼而言沒什么不同。硬件相關(guān)部分的代碼不多,占總代碼量的很少一部分。所以對更換硬件平臺來說,沒有什么真正的負(fù)擔(dān)。另一方面,Linux使用內(nèi)核機(jī)制很好地解決了擴(kuò)展的問題,一堆代碼可以在需要的時(shí)候輕松地加載或卸下,象隨身聽,需要的時(shí)候帶上,不需要時(shí)則鎖在抽屜里。
Linux內(nèi)核可以視為由五個(gè)功能部分組成:進(jìn)程管理(包括調(diào)度和通信)、內(nèi)存管理、設(shè)備管理、虛擬文件系統(tǒng)、網(wǎng)絡(luò)。它們之間有著復(fù)雜的調(diào)用關(guān)系,但幸運(yùn)的是,在移植中不會(huì)觸及到太多,因?yàn)長inux內(nèi)核良好的分層結(jié)構(gòu)將硬件相關(guān)的代碼獨(dú)立出來。何謂硬件相關(guān),何謂無關(guān)?以進(jìn)程管理為例,對進(jìn)程的時(shí)間片輪轉(zhuǎn)調(diào)度算法在所有平臺的Linux中都是一樣的,它是與平臺無關(guān)的;而用來在進(jìn)程中切換的實(shí)現(xiàn)在不同的CPU上是不同的,因此需要針對該平臺編寫代碼,這就是平臺相關(guān)的。上面所講的五個(gè)部分的順序不是隨便排的,從前到后分別代表著它們與硬件設(shè)備的相關(guān)程度。越靠前越高,后面的兩個(gè)虛擬文件系統(tǒng)和網(wǎng)絡(luò)則幾乎與平臺無關(guān),它們由設(shè)備管理中所支持的驅(qū)動(dòng)程序提供底層支持。因此,在做系統(tǒng)移植的時(shí)候,需要改動(dòng)的就是進(jìn)程管理、內(nèi)存管理和設(shè)備管理中被獨(dú)立出來的那部分即硬件相關(guān)部分的代碼。在Linux代碼樹下,這部分代碼全部在arch目錄下。
如果你的目標(biāo)平臺已經(jīng)被Linux核心所支持的話,那么你是幸運(yùn)的,因?yàn)橐呀?jīng)沒有太多的工作讓你去做。只要你的交叉編譯環(huán)境是正確的,你只需要簡單的配置、編譯就可以得到目標(biāo)代碼。否則,需要你去編寫,或修改一些代碼。只需修改平臺相關(guān)部分的代碼即可。但需要對目標(biāo)平臺,主要是對CPU的透徹理解。在Linux的代碼樹下,可以看到,這部分的典型代碼量為:2萬行左右C代碼和2千行左右的匯編(C代碼中通常包含許多偽匯編指令,因此實(shí)際上純C代碼要少很多),這部分工作量是不可小看的。它包含了對絕大多數(shù)硬件的底層操作,涉及IRQ、內(nèi)存頁表、快表、浮點(diǎn)處理、時(shí)鐘、多處理器同步等問題,頻繁的端口編程意味著需要你將目標(biāo)平臺的文檔用C語言重寫一遍。這就是為什么說目標(biāo)平臺的文檔極其重要。
代碼量大的部分是被核心直接調(diào)用的底層支持部分,這部分代碼在arch/xxx/kernel下(xxx是平臺名稱)。這些代碼重寫了內(nèi)核所需調(diào)用的所有函數(shù)。因?yàn)榻涌诤瘮?shù)是固定的,所以這里更象是為硬件平臺編寫API。不同的系統(tǒng)平臺,主要有以下幾方面的不同:
* 進(jìn)程管理底層代碼:從硬件系統(tǒng)的角度來看,進(jìn)程管理就是CPU的管理。在不同的硬件平臺上,這有很大的不同。CPU中用的寄存器結(jié)構(gòu)不同,上下文切換的方式、現(xiàn)場的保存和恢復(fù)、棧的處理都不同,這些內(nèi)容主要由CPU開發(fā)手冊所描述。通常來說,CPU的所有功能和狀態(tài)對于Linux不一定有意義。實(shí)現(xiàn)時(shí),需要在小的開發(fā)代價(jià)和好的系統(tǒng)性能之間加以權(quán)衡。
* BIOS接口代碼:這一名稱似乎并不太準(zhǔn)確,因?yàn)樗赜昧薖C一貫的叫法。但在不致引起混淆的情況下我們還是這么叫它。在通用平臺上,通常有基本輸入輸出系統(tǒng)供操作系統(tǒng)使用,在PC上是BIOS,在SPARC上是PROM,在很多非通用系統(tǒng)上甚至并沒有這樣的東西。多數(shù)情況下,Linux不依賴基本輸入輸出系統(tǒng),但在某些系統(tǒng)里,Linux需要通過基本輸入輸出系統(tǒng)中得到重要的設(shè)備參數(shù)。移植中,這部分代碼通常需要完全改寫。
* 時(shí)鐘、中斷等板上設(shè)備支持代碼:即使在同一種CPU的平臺上,也會(huì)存在不同的板上外設(shè),異種CPU平臺上更是如此。不同的系統(tǒng)組態(tài)需要不同的初始化代碼。很典型的例子就是MIPS平臺,看看arc/mips/的代碼,與其它系統(tǒng)比較一下就知道。因?yàn)镸IPS平臺被OEM得廣,在嵌入式領(lǐng)域應(yīng)用多(相對其它幾種CPU而言)。甚至同一種MIPS芯片被不同廠家封裝再配上不同的芯片組。因此要為這些不同的MIPS平臺分別編寫不同的代碼。
* 特殊結(jié)構(gòu)代碼:如多處理器支持等。其實(shí)每一種CPU都是十分特殊的,熟悉x86平臺的人都知道x86系列CPU著名的實(shí)模式與虛模式的區(qū)別,而在SPARC平臺上根本就沒有這個(gè)概念。這就導(dǎo)致了很大的不同:PC機(jī)上的Linux在獲得控制權(quán)后不久就開始切換到虛模式,SPARC機(jī)器上則沒有這段代碼。又如電源管理的支持更是多種多樣,不同的CPU有著不同的實(shí)現(xiàn)方式(特殊的電源管理方式甚至被廠商標(biāo)榜)。在這種情況下,除非放棄對電源管理的支持,否則必須重寫代碼。
還有一部分代碼量不多,但不能忽視的部分是在arch/xxx/mm/下的內(nèi)存管理部分。所有與平臺相關(guān)的內(nèi)存管理代碼全部在這里。這部分代碼完成內(nèi)存的初始化和各種與內(nèi)存管理相關(guān)的數(shù)據(jù)結(jié)構(gòu)的建立。Linux使用了基于頁式管理的虛擬存儲技術(shù),而CPU發(fā)展的趨勢是:為了提高性能,實(shí)現(xiàn)內(nèi)存管理的功能單元統(tǒng)統(tǒng)被集成到CPU中。因此內(nèi)存管理成為一個(gè)與CPU十分相關(guān)的工作。同時(shí)內(nèi)存管理的效率也是影響系統(tǒng)性能的因素之一。內(nèi)存可以說是計(jì)算機(jī)系統(tǒng)中頻繁訪問的設(shè)備,如果每次內(nèi)存訪問時(shí)多占用一個(gè)時(shí)鐘周期,那就有可能將系統(tǒng)性能降低到不可忍受。在Linux系統(tǒng)里,不同平臺上的內(nèi)存管理代碼的差異程度是令人吃驚的,可以說是差異大的。不同的CPU有不同的內(nèi)存管理方式,同一種CPU還會(huì)有不同的內(nèi)存管理模式。Linux是從32位硬件平臺上發(fā)展起來的操作系統(tǒng),但是現(xiàn)在已經(jīng)有數(shù)種64位平臺出現(xiàn)。在64位平臺上,可用內(nèi)存范圍增大到原來的232倍,其間差異可略窺一斑了。鑒于這部分代碼的重要性和復(fù)雜性,移植工作在這里變得相當(dāng)謹(jǐn)慎。有些平臺上甚至只是用保守的內(nèi)存管理模式。如在SPARC平臺上的頁面大小可以是多種尺寸,為了簡單和可靠起見,SPARC版的Linux只是用了8K頁面這一種模式。這一狀況直到2.4版才得以改善。
除了上面所講的之外,還有一些代碼需要考慮,但相對來說次要一些。如浮點(diǎn)運(yùn)算的支持。較完美的做法是對FPU編程,由硬件完成浮點(diǎn)運(yùn)算。但在某些時(shí)候,浮點(diǎn)并不重要,甚至CPU根本就不支持浮點(diǎn)。這時(shí)候就可以根據(jù)需求來取舍。
對于內(nèi)核移植的討論到此為止。實(shí)際上,還有一些移植工作需要同時(shí)考慮,但很難說這是屬于內(nèi)核范疇還是屬于驅(qū)動(dòng)程序范疇,比如說顯示設(shè)備的支持,和內(nèi)核十分相關(guān),但在邏輯上又不屬于內(nèi)核,并且在移植上也更像是驅(qū)動(dòng)程序的開發(fā)。因此不在這里討論。
(2)系統(tǒng)移植
當(dāng)內(nèi)核移植完畢后,可以說所有的移植工作就已經(jīng)完成大半了。就是說,當(dāng)內(nèi)核在交叉編譯成功后,加載到目標(biāo)平臺上正常啟動(dòng),并出現(xiàn)類似VFS: Can't mount root file system的提示時(shí),則表示可以開始系統(tǒng)移植方面的工作了。系統(tǒng)移植實(shí)際上是一個(gè)小系統(tǒng)的重建過程。許多Linux愛好者有過建立Linux系統(tǒng)應(yīng)急盤的經(jīng)驗(yàn),與其不同的是,你需要使用目標(biāo)平臺上的二進(jìn)制代碼生成這個(gè)小系統(tǒng)。包括:init、libc庫、驅(qū)動(dòng)模塊、必需的應(yīng)用程序和系統(tǒng)配置腳本。一旦這些工作完成,移植工作就進(jìn)入聯(lián)調(diào)階段了。
一個(gè)比較容易的系統(tǒng)部分移植辦法是:先著手建立開發(fā)平臺上的小系統(tǒng),保證這套小系統(tǒng)在開發(fā)平臺上正確運(yùn)行。這樣可以避免由于小系統(tǒng)本身的邏輯錯(cuò)誤而帶來的麻煩。由于小系統(tǒng)中是多個(gè)應(yīng)用程序相互配合工作,有時(shí)出現(xiàn)的問題不在代碼本身而在系統(tǒng)的邏輯結(jié)構(gòu)上。
Linux系統(tǒng)移植工作至少要包括上述的內(nèi)容,除此之外,有一些看不見的開發(fā)工作也是不可忽視的,如某個(gè)特殊設(shè)備的驅(qū)動(dòng)程序,為調(diào)試內(nèi)核而做的遠(yuǎn)程調(diào)試工作等。另外,同樣的一次移植工作,顯然符合小功能集的移植和完美移植是不一樣的;向16位移植和向64位移植也是不一樣的。
在移植中通常會(huì)遇見的問題是試運(yùn)行時(shí)鎖死或崩潰,在系統(tǒng)部分移植時(shí)要好辦些,因?yàn)榭梢匀菀椎囟ㄎ诲e(cuò)誤根源,而在核心移植時(shí)確實(shí)很讓人頭疼。雖然可以通過串口對運(yùn)行著的內(nèi)核進(jìn)行調(diào)試,但是在多任務(wù)情況下,有很多現(xiàn)象是不可重現(xiàn)的。又如,在初始化的開始,很多設(shè)備還沒法確定狀態(tài),甚至串口還沒有初始化。對于這種情況沒有什么很好的解決辦法,好的開發(fā)/仿真平臺很重要,另外要多增加反映系統(tǒng)運(yùn)行狀態(tài)的調(diào)試代碼;再者要吃透硬件平臺的文檔。硬件平臺廠商的專業(yè)支持也是很重要的。
還有一點(diǎn)很重要:Linux本身是基于GPL的操作系統(tǒng),移植時(shí),可以充分發(fā)揮GPL的優(yōu)勢,讓更多的愛好者參與進(jìn)來,向共同的目標(biāo)前進(jìn)。