|
前些日子基于arm+uClinux開發(fā)了一個(gè)網(wǎng)絡(luò)監(jiān)控系統(tǒng),眼看項(xiàng)目馬上要做完了,終于松了一口氣,于是整理了一些筆記和心得想和大家針對(duì)這種開發(fā)模式進(jìn)行一些探討,希望對(duì)各位有所幫助。 按照我的開發(fā)過(guò)程想分以下幾部分逐一介紹。 1.開發(fā)平臺(tái)的選擇和論證 2.開發(fā)環(huán)境的建立 3.一般程序的開發(fā) 4.Linux程序向ARM+uClinux平臺(tái)的移植 5.剩下的問(wèn)題 希望諸位多多補(bǔ)充自己的想法,以利于大家共同提高。 1. 開發(fā)平臺(tái)的選擇和論證 一個(gè)項(xiàng)目拿到手,如何選擇開發(fā)平臺(tái)(主要是指CPU和操作系統(tǒng)以及開發(fā)環(huán)境和工具)應(yīng)該說(shuō)至關(guān)重要,有時(shí)這不光影響進(jìn)度,產(chǎn)品質(zhì)量,可維護(hù)性等一般問(wèn)題,甚至涉及到方案的可實(shí)現(xiàn)性。本人結(jié)合自己的網(wǎng)絡(luò)監(jiān)控系統(tǒng)簡(jiǎn)單歸納了一些對(duì)平臺(tái)的考慮,還請(qǐng)各位補(bǔ)充。 從系統(tǒng)功能實(shí)現(xiàn)考慮: (1) 是否有片上外設(shè),專用指令或配套的軟件模塊直接實(shí)現(xiàn)系統(tǒng)功能要求。 感 覺(jué)這一條對(duì)很多人的決策影響很大 (2) 價(jià)格 這一點(diǎn)應(yīng)通過(guò)CPU提供的資源綜合考慮,它提供了多少有用的資源,多少?zèng)]用的資源(那可都是銀子呀!),還是那三個(gè)字,性價(jià)比,另一方面,是要抓主要矛盾,是不是有些特性是必須的,什么特性是用戶需求里的亮點(diǎn)(就靠這些亮點(diǎn)往上抬價(jià)),這時(shí)該花的就得花了。 (3) 功耗 本系統(tǒng)對(duì)CPU功耗要求不高,但對(duì)移動(dòng)設(shè)備,這一點(diǎn)可是致命,而且這一點(diǎn)不是僅針對(duì)CPU,所有幾乎器件都要勒緊褲腰帶運(yùn)行。 (4) 處理速度 這項(xiàng)不用多說(shuō),大家都明白重要性,但具體算起來(lái)可是一門學(xué)問(wèn),一方面是自己需要多快的速度,如果加上非實(shí)時(shí)操作系統(tǒng)這事就不好控制,余量還是大點(diǎn)穩(wěn)妥,另一方面,CPU指令周期多少,有沒(méi)有流水,有沒(méi)有并行,什么體系結(jié)構(gòu),有沒(méi)有專用指令(看人家DSP多牛,干這事一絕),對(duì)外部存儲(chǔ)器和外設(shè)的存取速度等等,哪一個(gè)慢了都叫瓶頸。 (5) 需要的硬件支持(如外部存儲(chǔ)器,雙電源等) 這算是雜項(xiàng),但會(huì)增加額外的價(jià)格,系統(tǒng)體積等,不容忽視。 從開發(fā)者的角度考慮: (1) 是否有足夠的技術(shù)支持包括demo版及原理圖,demo程序,操作系統(tǒng)和BSP,測(cè)試開發(fā)工具等。 (2) 自身?xiàng)l件;包括對(duì)項(xiàng)目開發(fā)周期的要求,開發(fā)人員對(duì)器件和開發(fā)模式的熟悉程度以及掌握的難易程度。 (3) 可用資源是否豐富(書籍,網(wǎng)絡(luò)等) 以上三點(diǎn)主要考慮迅速開發(fā)出穩(wěn)定的系統(tǒng)。 (4) 系統(tǒng)的可繼承性,可移植性和可擴(kuò)展性。 (5) 是否有現(xiàn)貨。 (6) 方案提供商的素質(zhì)。(包括技術(shù)水平和服務(wù)意識(shí))。 根據(jù)以上考慮選擇了s3c4510b(ARM7TDMI)+uClinux開發(fā)模式 (1) 以下是該平臺(tái)對(duì)我的系統(tǒng)的滿足情況:(和上面幾點(diǎn)對(duì)應(yīng)) 本監(jiān)控系統(tǒng)硬件部分主要要求以下部分: a.以太網(wǎng)接口 (s3c4510b自帶網(wǎng)絡(luò)控制器) b.串口 (自帶) c.與數(shù)據(jù)采集芯片的接口(8位數(shù)據(jù)線,小于8位地址總線)。(自帶) 本系統(tǒng)軟件部分主要要求以下部分: a. 硬件接口驅(qū)動(dòng)程序 (uClinux提供串口和網(wǎng)絡(luò)控制器驅(qū)動(dòng)) b. 網(wǎng)絡(luò)協(xié)議棧支持(uClinux提供TCPIP,UDP等的協(xié)議棧) c. 應(yīng)用層程序(如果算上可以從linux移植的程序來(lái)看,那就太多了,我就用到了一個(gè)現(xiàn)成的) (2) 本應(yīng)用系統(tǒng)不是那種批量的東西,對(duì)價(jià)格要求不苛刻,而且這款CPU最便宜可以到55左右,可以接受。 (3) 本應(yīng)用系統(tǒng)有固定電源,功耗要求不高。當(dāng)然,據(jù)說(shuō)ARM在節(jié)省功耗上很有特點(diǎn)。 (4) 本應(yīng)用系統(tǒng)速度方面要滿足兩方面:1。串口:115200bps 2。網(wǎng)絡(luò)速度 能到10Mbps就行,所以對(duì)系統(tǒng)速度要求也不高。這款A(yù)RM內(nèi)部可以到50M。 (5) 系統(tǒng)對(duì)體積要求也不高,加片flash和RAM還是沒(méi)問(wèn)題(到目前為止感覺(jué)我的系統(tǒng)真是無(wú)欲無(wú)求!) 從開發(fā)者的角度考慮: (1) 因?yàn)闀r(shí)間很緊(一個(gè)半月),所以支持越多越好。目前從開發(fā)商那里拿到了開發(fā)板,原理圖,uClinux,相應(yīng)驅(qū)動(dòng),bootloader,拿來(lái)就可以用了。軟件硬件并行開發(fā)。(bootloader和網(wǎng)絡(luò)控制器驅(qū)動(dòng)沒(méi)提供原碼,比較可惜:-( (2) 當(dāng)時(shí)我對(duì)嵌入式系統(tǒng)的開發(fā)模式和ARM都是只有耳聞,linux接觸過(guò)一個(gè)月左右。現(xiàn)在想起來(lái)有些后怕。 (3) 網(wǎng)上的資源,非常多。 提供一些我常用的。 http://www.uclinux.org/ uClinux的大本營(yíng)。 http://www.ucdot.org/ 里面有些技術(shù)文章非常不錯(cuò)。 http://www.linuxdevices.com/ http://www.linuxeden.com/ 這是國(guó)產(chǎn)的linux站點(diǎn)。 uclinux-dev@uclinux.org 這是uClinux的郵件列表,回答問(wèn)題的都是大牛,非常有幫助,記住把你的郵件設(shè)置成純文本格式。 申請(qǐng)是在: http://mailman.uclinux.org/mailman/listinfo/uclinux-dev web方式。 (4) 采用以上開發(fā)模式,軟件的可維護(hù)性,可移植性和可擴(kuò)展性都不錯(cuò)。 (5) 目前該CPU使用還是比較普遍,現(xiàn)貨沒(méi)問(wèn)題。 (6) 方案提供商的素質(zhì)嗎……..還算可以吧:-) 根據(jù)以上考慮和目前的開發(fā)情況,這套方案還是比較令人滿意。 今天先回家了,下回介紹具體開發(fā)步鄹吧。 2.開發(fā)環(huán)境的建立 先說(shuō)兩句廢話為和我以前一樣對(duì)操作系統(tǒng)(尤其是嵌入式操作系統(tǒng))迷惑的弟兄解釋些概念。因?yàn)榭偸怯腥嗽趩?wèn)是不是一定要用操作系統(tǒng),我的CPU能不能移植操作系統(tǒng),可以移植什么操作系統(tǒng),有了操作系統(tǒng)可不可以運(yùn)行某些程序。 從我的個(gè)人經(jīng)歷來(lái)講,這其實(shí)就是許多硬件出身的弟兄對(duì)操作系統(tǒng)這個(gè)東西有神秘感(和我一年前一樣)。說(shuō)白了,操作系統(tǒng)就是一段設(shè)計(jì)非常巧妙的程序,和你自己的程序從本質(zhì)講沒(méi)有區(qū)別,于是,以上問(wèn)題轉(zhuǎn)為,我是不是一定要用這段程序,我的CPU能不能運(yùn)行這段程序,可以跑什么樣的程序。這個(gè)程序可以跑,調(diào)用這個(gè)程序接口的另一個(gè)程序能不能跑! 答案也就變得簡(jiǎn)單,操作系統(tǒng)對(duì)任何一個(gè)CPU都不是必須的(對(duì)嵌入式系統(tǒng)更是如此),你可以自己編些程序在沒(méi)有操作系統(tǒng)的PC裸機(jī)上跑(BIOS就是這樣的),像玩C51一樣,(雖然奢侈的讓人有些心痛),或者移植UCOS到上面。另一方面,現(xiàn)代操作系統(tǒng)大多需要一些硬件的支持,(像保護(hù)模式的實(shí)現(xiàn)),反過(guò)來(lái)說(shuō),高端CPU中專門有針對(duì)支持操作系統(tǒng)的體系結(jié)構(gòu),這樣,許多操作系統(tǒng)的實(shí)現(xiàn)是挑剔硬件平臺(tái)的。其實(shí)其它程序也一樣,你編的程序使用的片上外設(shè)另一CPU上沒(méi)有,那這段程序就無(wú)法移植了。這就是話粗理不粗。書歸正傳,還是聊聊ARM+uClinux開發(fā)模式下開發(fā)環(huán)境的建立(其實(shí)下面說(shuō)到的東西不僅限于這種硬件平臺(tái)和操作系統(tǒng)) 很久以前就在介紹嵌入式系統(tǒng)開發(fā)的書上見(jiàn)過(guò)“交叉編譯環(huán)境”這詞,當(dāng)時(shí)覺(jué)得很玄,用了以后才知道,其實(shí)就是解決在誰(shuí)的地盤上用誰(shuí)的工具編誰(shuí)的代碼問(wèn)題。 編譯的最主要的工作就在將你的程序轉(zhuǎn)化成運(yùn)行該程序的CPU所能識(shí)別的機(jī)器代碼,不同的CPU有相應(yīng)的編譯器,另一方面。編譯器本身也是程序,當(dāng)然也要在某一個(gè)CPU平臺(tái)上運(yùn)行。于是交叉編譯的交叉點(diǎn)就在那個(gè)編譯器本身是CPU1上的一個(gè)程序,卻在為CPU2編譯代碼(整個(gè)一個(gè)吃里扒外!)。這么一想,以前用51和dsp的開發(fā)軟件(大部分都是IDE-集成開發(fā)環(huán)境)開發(fā)程序時(shí),都算是交叉編譯啦。當(dāng)然,假如在你的ARM系統(tǒng)上,操作系統(tǒng)已經(jīng)正常運(yùn)行,并且你的資源足夠多,你可以把PC機(jī)上運(yùn)行的ARM編譯工具移植到ARM上,然后所有該系統(tǒng)的應(yīng)用程序都直接在ARM系統(tǒng)上編譯,這就不算交叉編譯,但如果有條件這么作,程序的開發(fā)或者移植就方便多了,因?yàn)檎麄(gè)開發(fā)過(guò)程又回到在自己PC機(jī)上編應(yīng)用程序的那種模式了,那就是在自己的地盤上用自己的編譯器編自己的應(yīng)用程序。 與不使用操作系統(tǒng)的開發(fā)模式不同(此處的操作系統(tǒng)尤其指提供了專門的接口函數(shù)庫(kù)的操作系統(tǒng),目前的UCOS就不算),在目標(biāo)板(就是實(shí)現(xiàn)系統(tǒng)的板子)使用操作系統(tǒng)的開發(fā)模式下,交叉編譯環(huán)境中還需要該對(duì)應(yīng)該操作系統(tǒng)的庫(kù)。比如uClinux提供的uClibc。此時(shí),開發(fā)用的主機(jī)上不光要有目標(biāo)板 CPU所需的編譯工具,還要有對(duì)應(yīng)操作系統(tǒng)的庫(kù),又因?yàn)橐话銕?kù)文件還要在開發(fā)機(jī)上拿目標(biāo)CPU的編譯器重新編譯一下,所以還要把操作系統(tǒng)的原碼也放到開發(fā)機(jī)上。(唉,跟目標(biāo)板沒(méi)什么關(guān)系,卻要幫它背這么多東西,真是上輩子欠它的!!)。 雖然操作系統(tǒng)的接口庫(kù)至關(guān)重要,但大家似乎已經(jīng)淡忘了它的存在。這些多是因?yàn)榇蠹乙呀?jīng)遠(yuǎn)離了刀耕火種的年代(需要告訴編譯器需要的include路徑,lib路徑,以及l(fā)ib的名稱),集成的編譯環(huán)境讓我們編譯鏈接的所有繁瑣工作化作對(duì)BUILD按鈕的瀟灑一擊。而且不論是windows環(huán)境,還是 linux環(huán)境,都有環(huán)境變量去記錄這些參數(shù)。。但嘗試將/usr/lib目錄改一個(gè)名字,你就會(huì)知道你不能無(wú)視他們的存在,因?yàn)椴僮飨到y(tǒng)的功能都是通過(guò)這些庫(kù)來(lái)交給應(yīng)用層程序使用的。當(dāng)然如果你的系統(tǒng)不依靠任何操作系統(tǒng),像最原始的那種完全自己實(shí)現(xiàn)所有代碼,就只需要一個(gè)編譯工具,少了這些羅嗦事。 以上的東西一般時(shí)候是沒(méi)有必要仔細(xì)研究,但交叉環(huán)境下開發(fā)或移植比較大的程序時(shí),你可能就需要了解編譯器,鏈接器等開發(fā)工具的幾乎所有重要參數(shù)。 我在開發(fā)時(shí),主機(jī)完全使用的是linux,如果有條件,建議大家這樣作,linux的使用沒(méi)有想象的復(fù)雜(雖然我現(xiàn)在身邊還要放一本關(guān)于linux使用的書籍),而且開發(fā)程序可以先在主機(jī)上調(diào)通,然后用交叉編譯工具為目標(biāo)系統(tǒng)重新編譯一遍,可以這樣做是因?yàn)橹鳈C(jī)是linux,目標(biāo)系統(tǒng)跑 uClinux,兩個(gè)操作系統(tǒng)提供的應(yīng)用程序接口幾乎是一樣的,所以程序幾乎不用修改。 在我的系統(tǒng)上,建立基本的開發(fā)環(huán)境過(guò)程如下。 1) 安裝gnu開發(fā)工具鏈(是GNU開發(fā)的針對(duì)ARM CPU的一組編譯開發(fā)程序(是linux程序)。包括arm-elf-gcc,arm-elf-ld等 (2) 將uClinux源代碼源代碼解壓到相應(yīng)路徑下,按照編譯內(nèi)核的步鄹編譯一遍(此時(shí)使用的編譯工具已經(jīng)是上面提到的ARM編譯工具了,因?yàn)樗贏RM CPU上運(yùn)行,另外,和編譯linux內(nèi)核一樣,此時(shí)可以通過(guò)menuconfig來(lái)對(duì)內(nèi)核提供的功能進(jìn)行裁減 (3) 將庫(kù)(uClibc)解壓到相應(yīng)路徑下,用以上工具編譯一遍。 這樣最基本的環(huán)境就算搭建好了。 以上工作對(duì)于做過(guò)的人來(lái)說(shuō)比較簡(jiǎn)單,這里介紹一下幫助沒(méi)有使用或剛開始使用這種開發(fā)模式的弟兄們理清一下思路。 3.一般應(yīng)用程序的開發(fā) 因?yàn)槟繕?biāo)板上用uClinux,它提供的程序接口和linux下的基本一致,不一致的部分主要在于uClinux 不支持MMU(應(yīng)該說(shuō)是uClinux是為不帶MMU的cpu定制的),最明顯的就是fork函數(shù)要用vfork函數(shù)替代,這也是編程時(shí),感覺(jué)最不爽的一點(diǎn)(沒(méi)辦法,誰(shuí)讓咱們的CPU有生理缺陷)。另一個(gè)不易覺(jué)察的差異在于uClinux提供的庫(kù)uClibc是經(jīng)過(guò)裁減的。更適合于資源緊張的嵌入式系統(tǒng)(上回分解已經(jīng)說(shuō)了,應(yīng)用程序很大一部分是在和庫(kù)函數(shù)打交道,而且大家最終是鏈在一起,所以庫(kù)函數(shù)大了,你的程序也小不了)。 于是基于這種開發(fā)模式的應(yīng)用程序開發(fā)變成了linux下的程序開發(fā)。而且在實(shí)際中一般是編好了程序先在主機(jī)上拿主機(jī)平臺(tái)上的編譯器編譯并且調(diào)試一下(linux下的編譯器就是gcc了),當(dāng)然前提是被調(diào)試的程序中需要的硬件條件主機(jī)具備,例如我的程序中有一段是針對(duì)串口的,于是先在主機(jī)編一個(gè)串口程序,調(diào)通以后拿目標(biāo)板的編譯器重新編譯一下(如果看了上一章“交叉編譯環(huán)境”,這里就不會(huì)暈了),下載到目標(biāo)板上運(yùn)行,一般來(lái)說(shuō)就可以直接用了。 以上也是為什么我認(rèn)為開發(fā)嵌入式linux程序主機(jī)應(yīng)該選用linux環(huán)境。對(duì)于以前沒(méi)用過(guò)linux的人來(lái)說(shuō)(比如我),開發(fā)程序前應(yīng)該花3,4天時(shí)間熟悉linux環(huán)境,尤其是它的編輯器,用慣集成編譯環(huán)境的人有時(shí)連編譯器和編輯器的概念都模糊了,所以一般是直接進(jìn)入集成編譯環(huán)境,連寫帶編一氣呵成,殊不知有些集成編譯器提供的編輯器弱智的一塌胡涂,如果用熟了linux下的emacs,你就會(huì)發(fā)現(xiàn)他們之間的差距大概……要像我和蓋茨那么大吧。所以編程序時(shí)應(yīng)該選一款優(yōu)秀的編輯器,linux下,我當(dāng)然選emacs,雖然剛看見(jiàn)它的感覺(jué)是外表丑陋,使用復(fù)雜。但只要多用多練,對(duì)提高效率很有幫助。(將你的程序用兩個(gè)編輯器完成,一半是用emacs的,一半是不用emacs的,看看效果:-) 對(duì)具體的linux編程我就不板門弄斧了,需要提個(gè)醒的是咱硬件出身的人作軟件應(yīng)該養(yǎng)成良好的編程習(xí)慣,別讓作軟件的笑話咱。因?yàn)樽髁诵┚W(wǎng)絡(luò)應(yīng)用,所以介紹一些網(wǎng)絡(luò)編程時(shí)要用到的網(wǎng)站和書籍; < http://www.fanqiang.com/a4/b7/適合于網(wǎng)絡(luò)編程的入門。 還有IBM中國(guó)上關(guān)于linux的教程和文章,都是翻譯過(guò)來(lái)的,有很多寫非常不錯(cuò)。 其實(shí)類似的資源不計(jì)其數(shù),遇到問(wèn)題時(shí)應(yīng)該先到google上狂搜一圈。 重點(diǎn)想說(shuō)些關(guān)于編譯器的東西,不了解它,在交叉編譯環(huán)境下編譯程序就寸步難行了,這無(wú)非是因?yàn)榻徊婢幾g環(huán)境下目標(biāo)板編譯器所處的寄人籬下的悲慘環(huán)境。想想在linux下將myprogram.c編譯鏈接成應(yīng)用程序myprogram,最簡(jiǎn)單的一句gcc –o myprogram myprogram.c 就可以了。(其實(shí)在諸如VC下你也可以找到類似的命令,集成開發(fā)環(huán)境只不過(guò)替你來(lái)調(diào)用它了)。一切看起來(lái)天經(jīng)地義。 但試著把/usr/include路徑改一個(gè)名字(比如改成stupid_include),再這樣編譯一下,會(huì)發(fā)現(xiàn)程序中被< >引用的頭文件(比如#include gcc –I/usr/stupid_include –o myprogram myprogram.c 會(huì)發(fā)現(xiàn)錯(cuò)誤信息沒(méi)了,一切又恢復(fù)了往日的寧?kù)o,頓時(shí)明白,不用環(huán)境變量,通過(guò)參數(shù),同樣可以將這些信息告訴編譯器。 返回來(lái)說(shuō)說(shuō)你的目標(biāo)編譯器,雖然占用了人家的地盤,編譯器,頭文件,庫(kù)文件,一個(gè)都不少,但你要編一個(gè)程序編譯器照樣發(fā)暈,因?yàn)闆](méi)有環(huán)境變量告訴它自己需要的頭文件和庫(kù)文件在哪里。看來(lái)只有兩種辦法,一個(gè)是搶占了主機(jī)的環(huán)境變量改成自己的(整個(gè)兒一個(gè)土匪),或者在編譯時(shí)加上必要參數(shù)(還是這樣紳士一些),告訴編譯器需要的文件的位置。(除此之外,還有其他一些參數(shù)也是如此)。 從源程序到可執(zhí)行文件根據(jù)情況不同可能分好幾步,一般每一步可能都會(huì)有一個(gè)應(yīng)用程序?qū)崿F(xiàn),像gnu提供的arm開發(fā)工具鏈其實(shí)就是這么一組程序。提供從編譯到鏈接到格式轉(zhuǎn)化的全套服務(wù)。你可以用arm-elf-gcc命令一步到底直接產(chǎn)生可執(zhí)行文件(其實(shí)也是在自己的任務(wù)完成后調(diào)用下一個(gè)程序),也可以每一步加上自己的參數(shù),只作自己的事。 編譯器的主要參數(shù)的使用下次將程序的移植時(shí)再講。這里想說(shuō)一下編譯器產(chǎn)生應(yīng)用程序的幾個(gè)主要步鄹,講這個(gè)問(wèn)題的原因還是很多人無(wú)法區(qū)分諸如編譯和鏈接,不用問(wèn),這一切還是IDE集成開發(fā)環(huán)境惹的禍。有人會(huì)說(shuō),IDE招你惹你了,你老貶它。其實(shí)不然,首先以上說(shuō)的東西一般在IDE的project菜單下的option或build option中找到,只是一般不用管罷了。另一個(gè)方面,IDE就像是傻瓜照相機(jī),很多工作他都幫你完成了,使用簡(jiǎn)單。但如果要做攝影師的話,你就少不了要對(duì)每一個(gè)細(xì)節(jié)都了解。其實(shí)編譯程序也是一樣。(你可以對(duì)優(yōu)化,警告級(jí),宏定義等諸多選項(xiàng)進(jìn)行自己的選擇)。以下是幾個(gè)主要步鄹:(以下有些我也不確認(rèn),如發(fā)現(xiàn)問(wèn)題,請(qǐng)及時(shí)糾正。 (1) 預(yù)編譯。 主要工作就是處理所有#開頭的,包括頭文件。以前搞不清頭文件和可執(zhí)行文件有沒(méi)有什么聯(lián)系(因?yàn)榭偪匆?jiàn)兩個(gè)文件名字取一樣的),現(xiàn)在知道,他們之間沒(méi)有任何聯(lián)系。在預(yù)編譯結(jié)束后,頭文件的使命就結(jié)束了。在下一次介紹不同平臺(tái)程序移植時(shí)可以看到,預(yù)編譯有時(shí)非常有用。 (2) 編譯。編譯應(yīng)該是最主要的一步,就是將源文件生成CPU能識(shí)別的語(yǔ)言,一般是 后綴為.o的目標(biāo)文件,應(yīng)該說(shuō),此時(shí)的文件就已經(jīng)可以執(zhí)行了。當(dāng)然這個(gè)時(shí)候外部函數(shù)等外部符號(hào)都沒(méi)有引入,對(duì)于被編譯程序來(lái)說(shuō),這些外部符號(hào)還只是留一個(gè)倩影,壓根兒不知它在不在。你可以在你的程序里調(diào)用一個(gè)不存在的函數(shù),甚至都不用聲明,在編譯階段,很多編譯器只是給個(gè)警告。只有在鏈接時(shí)才會(huì)報(bào)錯(cuò)。(呵呵,夠弱智!) (3) 鏈接:鏈接才是清帳的時(shí)候,以前在程序里用到的外部符號(hào)都要把真正的東西交出來(lái)。你可以指定需要連接在一起的目標(biāo)文件,也可以告訴編譯器庫(kù)文件的名字和路徑(指定方法下次講)。編譯器會(huì)去找,需要注意的是,庫(kù)的指定需要注意順序。首先,如果不同的庫(kù)里有同名函數(shù),并且該函數(shù)被調(diào)用,那么在前面的就被鏈接進(jìn)去了,這一點(diǎn)對(duì)于頭文件路徑的指定也適用,如果你自己的頭文件和系統(tǒng)頭文件相同,并且你的頭文件路徑在系統(tǒng)頭文件路徑前面,你的頭文件就會(huì)代替頭文件。庫(kù)文件是由相應(yīng)的程序(linux下是ar命令)將需要被添加到庫(kù)里的目標(biāo)文件(該文件是編譯階段生成的)組織起來(lái)生成檔案文件,同時(shí)可以建立一個(gè)檢索,檢索內(nèi)容為所包含的目標(biāo)文件中定義的符號(hào)。也就是說(shuō),庫(kù)文件并不是必須的,但它為經(jīng)常使用的目標(biāo)文件中的函數(shù)提供了快速的檢索機(jī)制。 以上就是主要的步鄹,當(dāng)然除此之外,還有一些用于格式轉(zhuǎn)換的工具等。不一一介紹。知道編譯器的細(xì)節(jié)對(duì)于程序的開發(fā)和移植都是很有好處的。 程序開發(fā)過(guò)程中調(diào)試也是至關(guān)重要,因?yàn)榭梢韵仍谥鳈C(jī)上調(diào)試,所以可以使用linux下的gdb,(有點(diǎn)像dos 下的debug)。但是只是用到了皮毛,還有一個(gè)專用于宿主機(jī)模式的調(diào)試工具gdbserver,一直沒(méi)時(shí)間研究,希望用過(guò)的大俠多發(fā)些文章鋪路。 另外還會(huì)遇到如何作ramdisk,如何讓系統(tǒng)啟動(dòng)自己的程序,這些都太linux了,沒(méi)接觸過(guò)linux的人會(huì)暈,為了大家的健康,就不講了,遇到問(wèn)題可以給我email,大家一起討論。 4.不同平臺(tái)間程序的移植--簡(jiǎn)單程序的移植 研究程序移植的那兩周是最痛苦的兩周,沒(méi)有太多可以借鑒的東西,只能摸黑向前走,于是更加堅(jiān)定決心要整理些東西給后來(lái)的弟兄。不過(guò)話說(shuō)回來(lái),各位弟兄別被我前面說(shuō)的嚇倒,只要搞清你要作什么,程序移植其實(shí)是比較簡(jiǎn)單的事情。 首先列出一些問(wèn)題: (1) X86上運(yùn)行的程序能不能在51單片機(jī)上運(yùn)行,為什么,有沒(méi)有可能,如果可以,應(yīng)該做哪些工作才可以實(shí)現(xiàn)。 (2) 相同CPU平臺(tái),DOS的程序?yàn)槭裁纯梢栽趙indows下運(yùn)行,能不能在linux下運(yùn)行,為什么,作什么工作可能實(shí)現(xiàn)。 為什么可以移植程序,為什么要移植程序? 程序可以移植首先要感謝開發(fā)出高級(jí)語(yǔ)言的大牛們,記住,無(wú)論多么漂亮的代碼經(jīng)過(guò)編譯以后都要變成CPU可以識(shí)別的機(jī)器語(yǔ)言,而幾乎一千種CPU說(shuō)著一千種語(yǔ)言。為保證大家有共同語(yǔ)言,規(guī)定一種高級(jí)語(yǔ)言――高級(jí)語(yǔ)言。每一個(gè)CPU派出自己的翻譯――編譯器。這個(gè)翻譯精通兩國(guó)語(yǔ)言,高級(jí)語(yǔ)言和自己的語(yǔ)言。(由此已經(jīng)可以看出編譯工具在程序移植中的重要性)。只要程序沒(méi)有硬件上的約束,可以說(shuō)這種溝通是無(wú)極限的,甚至于不同操作系統(tǒng)平臺(tái)。(操作系統(tǒng)也是程序,也可以移植嘍)舉例:在x86的windows下用VC(或TC,BC)編一個(gè)c程序?qū)崿F(xiàn)i=i+1,絲毫不改就可以用51的C編譯器重新編譯并在 51單片機(jī)上運(yùn)行。一次小型的移植就結(jié)束了。 可以移植已有的程序還要感謝開放源代碼的弟兄,沒(méi)有這些C文件和H文件讓你重新編譯一下,怎么在你的CPU上跑?其實(shí)不止這些,后面還會(huì)看見(jiàn)開源組織的牛人專為程序可移植性所作的專門的工作。 那么為什么要移植程序? 問(wèn)這問(wèn)題就像問(wèn)地上有個(gè)錢包為什么要撿一樣,答案不言而喻。現(xiàn)成的東西為什么不要。當(dāng)然,移植程序可沒(méi)有撿錢包那么簡(jiǎn)單,尤其是第一次,后面會(huì)說(shuō)一些移植之前應(yīng)該考慮的問(wèn)題。(就像現(xiàn)在地上有個(gè)錢包也千萬(wàn)別上去就揣自己兜里,說(shuō)不定就是套)。另一方面,你給我你寫好的程序讓我拿去用,我還要考慮一下,或許里頭問(wèn)題多的還不如自己寫一個(gè)。我這里所說(shuō)的可移植的程序應(yīng)該是維護(hù)比較好,比較成熟的源代碼(像我后面的所說(shuō)的UCD-SNMP),目前的開放源代碼運(yùn)動(dòng)決不僅僅是把自己的程序公開就行了,而是有了一套成熟完整的版本控制,BUG報(bào)告和PATCH提交流程。這樣的代碼才有更大的使用價(jià)值。 什么時(shí)候可以考慮移植程序? 在基于嵌入式操作系統(tǒng)進(jìn)行開發(fā)時(shí),具有一定規(guī)模的程序都可以到網(wǎng)上查一查都沒(méi)有成熟的源代碼可用。前面已經(jīng)說(shuō)到,程序的移植最終只針對(duì)CPU,其實(shí)和操作系統(tǒng)沒(méi)什么關(guān)系,但另一方面,因?yàn)榇a中可能會(huì)使用一些庫(kù)函數(shù),這些庫(kù)會(huì)包括C語(yǔ)言標(biāo)準(zhǔn)庫(kù)和操作系統(tǒng)提供的API(應(yīng)用程序接口)庫(kù)。假設(shè)源代碼中只包括C標(biāo)準(zhǔn)庫(kù),那么該程序就可以跨操作系統(tǒng)去移植。例如hello world程序中使用了printf,因?yàn)樵摵瘮?shù)是C標(biāo)準(zhǔn)函數(shù),所以在X86上使用TC(BC或VC)可以直接編譯運(yùn)行,在ARM+uClinux平臺(tái)下也一樣,但如果程序中調(diào)用了vfork函數(shù),那么只有l(wèi)inux一脈相承的操作系統(tǒng)支持這種特殊服務(wù)了,在window或dos操作系統(tǒng)下無(wú)法直接編譯該程序了。只能找該操作系統(tǒng)支持的類似的功能來(lái)實(shí)現(xiàn)。再進(jìn)一步,硬件上的生理缺陷也會(huì)為移植帶來(lái)麻煩,S3C4510B不支持MMU,在其上運(yùn)行的 uClinux也不提供和MMU有關(guān)的服務(wù)(其實(shí)uClinux本身可以支持MMU),于是在移植前相關(guān)的函數(shù)(比如FORK)都要被替代掉(使用 VFORK)。好在uClinux和linux提供的應(yīng)用接口大部分還是相同的。所以這樣的工作還可以承受。 由上可知,如果是在各種嵌入式linux(除了uClinux以外,還有好幾種)平臺(tái)上開發(fā),那么針對(duì)該平臺(tái)以及l(fā)inux平臺(tái)上的源代碼都可以使用,但是要牢記他們之間的差異。在我的系統(tǒng)中需要實(shí)現(xiàn)網(wǎng)絡(luò)監(jiān)控,所以想使用snmp協(xié)議,該協(xié)議和http,ftp一樣屬于應(yīng)用層的成熟協(xié)議,專用于網(wǎng)絡(luò)管理。目前已經(jīng)有一些針對(duì)該協(xié)議成熟的代碼,最有名的是ucd-snmp,不光軟件本身功能強(qiáng)大,可移植性也比較好,在linux,unix等平臺(tái)上都可以移植,于是決定將它移植到ARM+uClinux平臺(tái)上(別看現(xiàn)在說(shuō)的這么輕松,當(dāng)時(shí)接這活時(shí)都有點(diǎn)哆嗦)。 簡(jiǎn)單總結(jié)一下,移植應(yīng)用程序的前提是有源代碼,移植的關(guān)鍵工具是編譯器,源代碼中和硬件平臺(tái)相關(guān)的東西越少越好(這里主要指使用了匯編,或做了針對(duì)自己平臺(tái)的事,比如將指針指向特定地址然后操作),另一方面,如果該程序是基于某個(gè)操作系統(tǒng)(利用了操作系統(tǒng)提供的特殊服務(wù),即API),要看自己的操作系統(tǒng)是否提供了相關(guān)服務(wù)。 下面簡(jiǎn)單列出一些我認(rèn)為移植時(shí)需要考慮的問(wèn)題: (1) 自己的操作系統(tǒng)的特點(diǎn)以及在當(dāng)前版本下支持的特性。 例如:uClinux不支持MMU,同樣就無(wú)法支持相應(yīng)的特性。 (2) 硬件資源。 因?yàn)榍度胧较到y(tǒng)資源比較緊張,硬件資源考慮必須要周全: (1) 軟件存儲(chǔ)空間的大小 這一般要等用目標(biāo)編譯器重新編譯完以后可能才會(huì)知道,所以只能大概估算,但千萬(wàn)不要看這個(gè)程序在linux下只有幾十k,就認(rèn)為程序很小,這是因?yàn)閘inux下程序多時(shí)使用動(dòng)態(tài)庫(kù),而在嵌入式系統(tǒng)中,很有可能是把用到的庫(kù)都鏈接在一起,所以程序的尺寸會(huì)大大增加。 (2) 程序運(yùn)行空間。. (3) 硬件以及相應(yīng)的驅(qū)動(dòng)是否完備 以上工作應(yīng)該盡量做,但有時(shí)事先無(wú)法把握,只能聽天由命了(有沒(méi)有搞錯(cuò)!!) 可能有人已經(jīng)要暈菜了,振奮一下大家,如果找到了好的源代碼(可移植性好),那么剩下的如要工作就是玩轉(zhuǎn)你的編譯器,只要你能順利的把源代碼用你的編譯器重新編譯一下。90%的工作就完成了(不是嗎) 上回已經(jīng)介紹了一些編譯器方面的東西,下面針對(duì)我的ARM編譯器的具體參數(shù)來(lái)講解一些編譯器主要參數(shù)的設(shè)置。 加入我已經(jīng)有了hello.c,在x86的linux平臺(tái)下編譯鏈接一下。 gcc –c hello.c 產(chǎn)生.o gcc –o hello hello.o 產(chǎn)生可執(zhí)行文件 上回說(shuō)過(guò),主機(jī)編譯器參數(shù)都有環(huán)境變量保存,所以看起來(lái)很簡(jiǎn)單。這里我故意分兩個(gè)步鄹。 下面看一下用我的編譯器編這個(gè)程序(心臟不好的先吃藥)。 arm-elf-gcc -Iroot/uClibc/include -msoft-float -mcpu=arm7tdmi -fomit-frame-pointer -fsigned-char -mcpu=arm7tdmi -Os –Wall -DEMBED -D_uclinux_ -c hello.c 這只是編譯,將參數(shù)逐一講解。 Arm-elf-gcc 是gnu的arm編譯工具 1)Include地址:參數(shù):-I 值:root/uClibc/include(這是在主機(jī)上我的uClinux的頭文件路徑) 用法:-I root/uClibc/include -I參數(shù)保證后面的頭文件路徑在搜索系統(tǒng)頭文件路徑前被搜索從而有可能替代系統(tǒng)的頭文件,如果有多個(gè)這樣的參數(shù),則搜索的順序是從左到右,然后是系統(tǒng)的頭文件。 2)-m 是針對(duì)CPU的選項(xiàng)。 -mcpu=arm7tdmi 說(shuō)明CPU類型 -msoft-float 產(chǎn)生包含浮點(diǎn)庫(kù)的輸出 -fsigned-char 讓char類型有符號(hào) -fomit-frame-pointer 對(duì)所有不需要幀指針的函數(shù)都不將其保存在寄存器中。 3) -Os –Wall -Wall:所有警告都顯示 Os:優(yōu)化尺寸,該選項(xiàng)使能所有所有不增加尺寸的O2優(yōu)化,并且進(jìn)一步根據(jù)尺寸優(yōu)化 4) = -DEMBED -D_uclinux_ -D: 將-Dmacro 后的macro定義為字符串1。 以下是鏈接: arm-elf-ld -L/root/uClibc/lib -L/usr/local/gnu/arm-elf/lib -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1 -elf2flt –o hello /root/uClibc/lib/crt0.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o hello.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o -lc -lgcc –lc 其中 1) 鏈接工具: arm-elf-ld 2) -L指明需要鏈接的庫(kù)的路徑,用法和-I一樣,自己的庫(kù)的路徑也可以在這里加入。 -L/root/uClibc/lib -L/usr/local/gnu/arm-elf/lib -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1 3) –o 后面緊跟生成的最終的文件名 4)/root/uClibc/lib/crt0.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o OBJECTS.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o 這是需要鏈接在一起的.o文件 5) -lc -lgcc –lc -l 后面緊跟的是需要鏈接的庫(kù)的名字,一般庫(kù)的名字是libxxx.a,使用時(shí)為-lxxx即可,不加lib和.a。還要注意位置,自己的庫(kù)文件應(yīng)該加在他的庫(kù)前面。 編譯通過(guò)后,移植就算完成了,對(duì)于比較小的源代碼都可以這樣,即先分析他的編譯選項(xiàng)(用到了那些頭文件,庫(kù)文件等),然后用自己的編譯器對(duì)照相應(yīng)參數(shù)重新編譯一下就行了。 當(dāng)然這只是簡(jiǎn)單程序的移植,復(fù)雜案例在下一次講吧。 |