嵌入式Linux應(yīng)用程序開發(fā)59_第1頁
嵌入式Linux應(yīng)用程序開發(fā)59_第2頁
嵌入式Linux應(yīng)用程序開發(fā)59_第3頁
嵌入式Linux應(yīng)用程序開發(fā)59_第4頁
嵌入式Linux應(yīng)用程序開發(fā)59_第5頁
已閱讀5頁,還剩66頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第5章

嵌入式Linux應(yīng)用程序開發(fā)

5.1開發(fā)環(huán)境的建立進(jìn)行項(xiàng)目開發(fā)前,首先要做的是搭建一套基于Linux操作系統(tǒng)的應(yīng)用開發(fā)環(huán)境,一般由目標(biāo)板和宿主機(jī)所構(gòu)成。目標(biāo)板用于運(yùn)行操作系統(tǒng)和系統(tǒng)應(yīng)用軟件,而目標(biāo)板所用到的操作系統(tǒng)的內(nèi)核編譯、應(yīng)用程序的開發(fā)和調(diào)試則需要通過宿主機(jī)來完成。開發(fā)環(huán)境對硬件沒有特殊的要求,但是為了雙方之間建立連接關(guān)系,關(guān)鍵的接口包括串口、以太網(wǎng)口和USB口等是必不可少的。嵌入式Linux的開發(fā)環(huán)境傳統(tǒng)的嵌入式開發(fā)環(huán)境需要單片機(jī)的仿真器,包含C語言、匯編語言、調(diào)試工具等的集成開發(fā)環(huán)境IDE和實(shí)時(shí)操作系統(tǒng)等,整個(gè)開發(fā)系統(tǒng)建立起來至少需要幾萬元。由于Flash技術(shù)的發(fā)展,特別是一些CPU可以用JTAG接口下載調(diào)試,故仿真器已可以省去。隨著標(biāo)準(zhǔn)化的推廣,JTAG調(diào)試工具變得越來越簡單、越來越通用。一些BDM調(diào)試工具已經(jīng)簡單到只需在PC機(jī)并行口和單片機(jī)的JTAG接口之間加一級5V到3.3V的電平轉(zhuǎn)換,這使得自制調(diào)試工具變得越來越容易。軟件方面,Linux下的自由軟件GNUgcc可以完成幾乎所有知名CPU,以及DSP的交叉C編譯和調(diào)試,故可以省去IDE。個(gè)人用Linux開發(fā)嵌入式應(yīng)用程序,可以在自己的PC機(jī)上安裝一套Linux操作系統(tǒng),使用Linux中的XWindows打開若干個(gè)窗口用于編譯、下載、調(diào)試等。如果整個(gè)研發(fā)小組由多名工程師組成,常采用的辦法是:用1臺(tái)PC機(jī)運(yùn)行Linux作為服務(wù)器;開發(fā)小組中每個(gè)成員都通過局域網(wǎng)用Telnet登錄到這臺(tái)Linux服務(wù)器上去;被開發(fā)的目標(biāo)板也掛在網(wǎng)上。在服務(wù)器的Linux環(huán)境下,各個(gè)工程師用GNUgcc編譯生成目標(biāo)代碼,再用FTP傳回到自己的PC機(jī)上,然后通過串行口或網(wǎng)絡(luò)下載到目標(biāo)機(jī)上。利用Cygwin建立模擬環(huán)境交叉編譯環(huán)境的建立交叉編譯就是在一個(gè)平臺(tái)上生成可以在另一個(gè)平臺(tái)上執(zhí)行的代碼。在宿主機(jī)上對即將運(yùn)行在目標(biāo)機(jī)上的應(yīng)用程序進(jìn)行編譯,生成可在目標(biāo)機(jī)上運(yùn)行的代碼格式。交叉編譯環(huán)境是一個(gè)由編譯器、連接器和解釋器組成的綜合開發(fā)環(huán)境。交叉編譯工具主要包括針對目標(biāo)系統(tǒng)的編譯器gcc、目標(biāo)系統(tǒng)的二進(jìn)制工具binutils、目標(biāo)系統(tǒng)的標(biāo)準(zhǔn)c庫glibc和目標(biāo)系統(tǒng)的Linux內(nèi)核頭文件。在建立交叉編譯環(huán)境之前,首先需要下載包括binutils、gcc、glibc及Linux內(nèi)核在內(nèi)的源代碼,盡量選用較新版本,glibc和內(nèi)核源代碼的版本必須與目標(biāo)機(jī)上實(shí)際使用的版本保持一致,并設(shè)定shell變量PREFIX指定可執(zhí)行程序的安裝路徑。(1)編譯binutils。運(yùn)行configure文件,并使用--prefix=$PREFIX參數(shù)指定安裝路徑,使用--target=arm-linux參數(shù)指定目標(biāo)機(jī)類型,然后執(zhí)行makeinstall。(2)配置Linux內(nèi)核頭文件。執(zhí)行makemrproper進(jìn)行清理工作,然后執(zhí)行makeconfigARCH=arm(或makemenuconfig/xconfigARCH=arm)進(jìn)行配置。一定要在命令行中使用ARCH=arm指定CPU架構(gòu),因?yàn)槟J(rèn)架構(gòu)為主機(jī)的CPU架構(gòu),這一步需要根據(jù)目標(biāo)機(jī)的實(shí)際情況進(jìn)行詳細(xì)的配置。配置完成之后,需要將內(nèi)核頭文件拷貝到安裝目錄:cp-dRinclude/asm-arm$PREFIX/arm-linux/include/asmcp-dRinclude/linux$PREFIX/arm-linux/include/linux(3)第一次編譯gcc。configure的運(yùn)行參數(shù)設(shè)置:--prefix=$PREFIX--target=arm-linux--disable-threads--disable-shared--enable-languages=c執(zhí)行makeinstall,將生成一個(gè)最簡單的gcc。由于編譯整個(gè)gcc是需要目標(biāo)機(jī)的glibc庫的,它現(xiàn)在還不存在,因此需要首先生成一個(gè)最簡單的gcc,它只需要具備編譯目標(biāo)機(jī)glibc庫的能力即可。(4)交叉編譯glibc。由于這一步驟生成的代碼是針對目標(biāo)機(jī)cpu的,因此它屬于一個(gè)交叉編譯過程。又因?yàn)樵撨^程要用到Linux內(nèi)核頭文件,默認(rèn)路徑為$PREFIX/arm-linux/sys-linux,因而需要在$PREFIX/arm-linux中建立一個(gè)名為sys-linux的軟連接,使其指向內(nèi)核頭文件所在的include目錄;也可以在接下來要執(zhí)行的configure命令中使用--with-headers參數(shù)指定linux內(nèi)核頭文件的實(shí)際路徑。configure的運(yùn)行參數(shù)設(shè)置如下,因?yàn)槭墙徊婢幾g,所以要將編譯器變量CC設(shè)為arm-linux-gcc:CC=arm-linux-gcc./configure--prefix=$PREFIX/arm-linux--host=arm-linux--enable-add-ons最后,按以上配置執(zhí)行configure和makeinstall,glibc的交叉編譯過程就算完成了,這里需要指出的是,glibc的安裝路徑設(shè)置為$PREFIXARCH=arm/arm-linux,如果此處設(shè)置不當(dāng),第二次編譯gcc時(shí)可能找不到glibc的頭文件和庫。(5)第二次編譯gcc。運(yùn)行configure,參數(shù)設(shè)置為--prefix=$PREFIX--target=arm-linux--enable-languages=c,c++。運(yùn)行makeinstall。到此為止整個(gè)交叉編譯環(huán)境就完全生成了。建立一個(gè)交叉編譯工具鏈?zhǔn)且粋€(gè)相當(dāng)復(fù)雜的過程,為了節(jié)省時(shí)間,網(wǎng)上有一些編譯好的可用的交叉編譯工具鏈可以下載。本書所附光盤也帶有編譯好的交叉編譯工具鏈arm-linux-toolchains.tgz,只需簡單地解壓縮即可使用:tarxvzfarm-linux-toolchains.tgz–C/arm9假設(shè)工具鏈解壓縮到目錄/arm9。解壓完畢后把工具鏈目錄加入到環(huán)境變量PATH中即可。交叉編譯工具做完后,簡單驗(yàn)證一下。首先用文字輸入軟件建立一個(gè)helloworld.c文件:#include

<stdio.h>int

main(void){

printf("hello

world\n");

return

0;}然后在命令行執(zhí)行:$arm-linux-gcc

helloworld.c

-o

helloworld$file

helloworld如果輸出以下信息,說明成功建立了編譯工具。helloworld:

ELF

32-bit

LSB

executable,

ARM,

version

1,

dynamically

linked

(uses

shared

libs),

not

stripped5.2Linux及開發(fā)工具的使用GNU提供的編譯工具包括匯編器as、C編譯器gcc、C++編譯器g++、鏈接器ld和二進(jìn)制轉(zhuǎn)換工具objcopy?;贏RM平臺(tái)的工具分別為arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux-objcopy。GNU的所有開發(fā)工具都可以從上下載,基于ARM的工具可以從獲得。GNU的編譯器功能非常強(qiáng)大,共有上百個(gè)操作選項(xiàng),這也是這類工具讓初學(xué)者頭痛的原因。不過,實(shí)際開發(fā)中只需要用到有限的幾個(gè),大部分可以采用默認(rèn)選項(xiàng)。GNU工具的開發(fā)流程如下:編寫C、C++語言或匯編源程序,用gcc或g++生成目標(biāo)文件,編寫鏈接腳本文件,用鏈接器生成最終目標(biāo)文件(elf格式),用二進(jìn)制轉(zhuǎn)換工具生成可下載的二進(jìn)制代碼。Linux常用命令用戶在命令行輸入命令后,由外殼進(jìn)行解釋。外殼是一種命令解釋器,提供了用戶和操作系統(tǒng)之間的交互接口。外殼是面向命令行的,而XWindow則是圖形界面。外殼解釋的命令送往操作系統(tǒng)執(zhí)行。外殼可以執(zhí)行Linux的系統(tǒng)內(nèi)部命令,也可以執(zhí)行應(yīng)用程序。用戶還可以利用外殼編程,執(zhí)行復(fù)雜的命令程序。Linux提供幾種外殼程序以供選擇。常用的有Bourne外殼、C外殼和Korn外殼。各個(gè)外殼都能提供基本的功能,又有其各自的特點(diǎn)。Bourne外殼是由StevenBourne編寫的,是UNIX的默認(rèn)外殼。Bourne外殼的外殼編程能力很強(qiáng),但是不能處理命令的用戶交互特征,bash是Bourne外殼的增強(qiáng)版。C外殼是由加利福尼亞大學(xué)伯克利分校的BillJoy編寫的。能提供Bourne外殼所不能處理的用戶交互特征,如命令補(bǔ)全、命令別名、歷史命令替換等。C外殼的編程能力不如Bourne外殼,但由于它的語法和C語言類似,所以C程序員將發(fā)現(xiàn)C外殼很順手。Korn外殼是由DaveKorn編寫的。Korn外殼融合了C外殼和Bourne外殼的優(yōu)點(diǎn),并與Bourne外殼完全兼容。Korn外殼的效率很高,其命令交互界面和編程交互界面都很不錯(cuò)。Bash是大多數(shù)Linux系統(tǒng)的默認(rèn)外殼。其克服了Bourne外殼的缺點(diǎn),又和Bourne外殼完全兼容。1.登錄和退出Linux啟動(dòng)后,給出login命令,等待用戶登錄。Login:<輸入用戶名>Password:<輸入密碼>使用logout命令退出外殼。2.更改賬號密碼使用passwd命令來設(shè)置新用戶的口令。在設(shè)置口令之后,賬號即能正常工作。語法:passwdOldpassword:<輸入舊密碼>Newpassword:<輸入新密碼(最好為6~8字,英文字母與數(shù)字混合)>Retypenewpassword:<再輸入一次密碼>3.聯(lián)機(jī)幫助系統(tǒng)上幾乎每條命令都帶有相關(guān)的Manpage。在有困難時(shí),可以立刻找到文件。語法:man命令例如,如果使用ls命令時(shí)遇到困難,可以輸入:manls4.遠(yuǎn)程登錄用來連接到其他機(jī)器執(zhí)行工作。在Linux上,由于對TCP/IP協(xié)議的完全支持,用戶可以很容易的從Linux主機(jī)連接其他的計(jì)算機(jī)系統(tǒng)。語法:rlogin主機(jī)名[-l用戶名]例如:rlogindoc-luser使用user賬號登錄到工作站doc中。語法:telnet主機(jī)名或telnetIP地址例如:telnet515.列出文件或目錄語法:ls[-atFlgR][name]其中name是文件名或目錄名。ls命令用來瀏覽文件與目錄,對于每個(gè)目錄,該命令將列出其中所有的子目錄與文件。6.改變工作目錄語法:cd[name]其中name是目錄名、路徑或目錄縮寫。cd除了有切換目錄的功能外,還有一個(gè)功能就是,不管在哪個(gè)目錄內(nèi),只要輸入cd命令,不加任何參數(shù),即可回到用戶目錄內(nèi)。7.復(fù)制文件語法:cp[-r]源地址目的地址帶目錄的拷貝,相當(dāng)于DOS內(nèi)的xcopy。8.移動(dòng)或更改文件、目錄名稱語法:mv源地址目的地址可以為文件或目錄改名,也可以將文件由一個(gè)目錄移入另一個(gè)目錄。9.建立新目錄語法:mkdir目錄名10.刪除目錄語法:rmdir目錄名或rm目錄名11.刪除文件語法:rm文件名12.列出當(dāng)前所在的目錄位置語法:pwd13.查看文件內(nèi)容語法:cat文件名14.分頁查看文件內(nèi)容語法:more文件名或cat文件名|more15.查看目錄所占磁盤容量語法:du[-s]目錄16.文件傳輸(1)拷貝文件或目錄至遠(yuǎn)程工作站。語法:rcp[-r]源地址目的主機(jī)名:目的地址。(2)自遠(yuǎn)程工作站拷貝文件或目錄。語法:rcp[-r]源主機(jī)名:源地址目的地址。(3)本地工作站與遠(yuǎn)程工作站之間的文件傳輸,必須擁有遠(yuǎn)程工作站的賬號及密碼,才可進(jìn)行傳輸工作。語法:ftp主機(jī)名或ftpip地址。17.文件權(quán)限的設(shè)定(1)改變文件或目錄的讀、寫、執(zhí)行權(quán)限。語法:chmod[-R]modenamename:文件名或目錄名。mode:3位8進(jìn)制數(shù)字或rwx的組合。r-read(讀),w–write(寫),x–execute(執(zhí)行),u–user(當(dāng)前用戶),g-group(組),o-other(其他用戶)。(2)改變文件或目錄的所有權(quán)。語法:chown[-R]用戶名name18.檢查自己所屬的工作組名稱語法:groups19.改變文件或目錄工作組所有權(quán)語法:chgrp[-R]工作組名name20.改變文件或目錄的最后修改時(shí)間語法:touchname21.文件的鏈接同一文件,可擁有一個(gè)以上的名稱,也就是把一個(gè)文件進(jìn)行鏈接。語法:ln老文件名新文件名22.文件中字符串的查找語法:grepstringfile23.查尋文件或命令的路徑語法:whereiscommand顯示命令的路徑。語法:whichcommand顯示命令的路徑,及使用者所定義的別名。語法:whatiscommand顯示命令功能的摘要。語法:findsearch-path-namefilename-print搜尋指定路徑下某文件的路徑。24.比較文件或目錄的內(nèi)容語法:diff[-r]name1name225.文件打印輸出用戶可用.login文件中的setenvPRINTER命令來設(shè)定打印機(jī)名。26.一般文件的打印語法:lpr[-P打印機(jī)名]文件名27.ptroff文件的打印語法:ptroff[-P打印機(jī)名][-man][-ms]文件名28.打印機(jī)控制命令(1)檢查打印機(jī)狀態(tài)、打印作業(yè)順序號和用戶名。語法:lpq[-P打印機(jī)名](2)刪除打印機(jī)內(nèi)的打印作業(yè)(用戶僅可刪除自己的打印作業(yè))。語法:lprm[-P打印機(jī)名]用戶名或作業(yè)編號29.進(jìn)程控制(1)查看系統(tǒng)中的進(jìn)程。語法:ps[-aux](2)結(jié)束或終止進(jìn)程。語法:kill[-9]PIDPID:利用ps命令所查出的進(jìn)程號。(3)在后臺(tái)執(zhí)行進(jìn)程的方式。語法:命令&(4)查看正在后臺(tái)中執(zhí)行的進(jìn)程。語法:jobs(5)結(jié)束或終止后臺(tái)中的進(jìn)程。語法:kill%nn:利用jobs命令查看出的后臺(tái)作業(yè)號30.外殼變量(1)查看外殼變量的設(shè)定值。語法:set查看所有外殼變量的設(shè)定值。語法:echo$變量名顯示指定的外殼變量的設(shè)定值。(2)設(shè)定外殼變量。語法:setvar=value(3)刪除外殼變量。語法:unsetvar31.環(huán)境變量(1)查看環(huán)境變量的設(shè)定值。語法:setenv查看所有環(huán)境變量的設(shè)定值。語法:echo$NAME顯示指定的環(huán)境變量NAME的設(shè)定值。(2)設(shè)定環(huán)境變量。語法:setenvNAMEword(3)刪除環(huán)境變量。語法:unsetenvNAME32.別名(1)查看所定義的命令的別名語法:alias查看自己目前定義的所有命令,以及所對應(yīng)的別名。語法:aliasname查看指定的name命令的別名。(2)定義命令的別名。語法:aliasname'commandline'(3)刪除所定義的別名。語法:unaliasname33.歷史命令(1)設(shè)定命令記錄表的長度。語法:sethistory=n(2)查看命令記錄表的內(nèi)容。語法:history(3)使用命令記錄表。語法:!!重復(fù)執(zhí)行前一個(gè)命令。語法:!nn:命令記錄表的命令編號。語法:!string重復(fù)前面執(zhí)行過的以string為起始字符串的命令。(4)顯示前一個(gè)命令的內(nèi)容。語法:!!:p(5)更改前一個(gè)命令的內(nèi)容并執(zhí)行。語法:^oldstring^newstring將前一個(gè)命令中oldstring的部分改成newstring并執(zhí)行。34.文件的壓縮(1)壓縮文件。語法:compress文件名壓縮文件語法:compressdir目錄名壓縮目錄(2)解壓縮文件。語法:uncompress文件名解壓縮文件語法:uncompressdir目錄名解壓縮目錄35.管道命令的使用語法:命令1|命令2將命令1的執(zhí)行結(jié)果送到命令2,作為命令2的輸入。36.輸入/輸出控制(1)標(biāo)準(zhǔn)輸入的控制。語法:命令<文件將文件作為命令的輸入。例如:mail-s"mailtest"weisongzhou@hotmail<file1將文件file1當(dāng)做信件的內(nèi)容,主題名稱為mailtest,送給收信人。(2)標(biāo)準(zhǔn)輸出的控制。語法:命令>文件將命令的執(zhí)行結(jié)果送至指定的文件中。例如:ls-l>list將執(zhí)行“l(fā)s-l”命令的結(jié)果寫入文件list中。37.查看系統(tǒng)中的用戶語法:who或finger語法:w語法:finger用戶名或finger用戶名@域名38.改變用戶名語法:su用戶名例如:suuser進(jìn)入用戶user的賬號,passwrod:<輸入用戶user的密碼>39.查看用戶名語法:whoami查看登錄時(shí)的用戶名。語法:whoami查看當(dāng)前的用戶名。若已執(zhí)行過su命令,則顯示出此用戶的用戶名。40.檢查遠(yuǎn)程系統(tǒng)是否正常語法:ping主機(jī)名或pingIP地址例如:pingdocgcc編譯器的使用gcc最基本的用法是:gcc[options]file...其中的option是以“-”開始的各種選項(xiàng),file是相關(guān)的文件名。在使用gcc的時(shí)候,必須要給出必要的選項(xiàng)和文件名。gcc的整個(gè)編譯過程分別是:預(yù)處理,編譯,匯編和鏈接。gcc編譯器幾個(gè)最常用的選項(xiàng):

-o表示要求編譯器生成指定文件名的可執(zhí)行文件;

-c表示只要求編譯器進(jìn)行編譯,而不要進(jìn)行鏈接,生成以源文件的文件名命名但把其后綴由.c或.cc變成.o的目標(biāo)文件;

-g要求編譯器在編譯的時(shí)候提供以后對程序進(jìn)行調(diào)試的信息;

-E表示編譯器對源文件只進(jìn)行預(yù)處理就停止,而不做編譯、匯編和鏈接;

-S表示編譯器只進(jìn)行編譯,而不做匯編和鏈接;

-O是編譯器對程序提供的編譯優(yōu)化選項(xiàng),在編譯的時(shí)候使用該選項(xiàng),可以使生成的執(zhí)行文件的執(zhí)行效率提高;

-Wall指定產(chǎn)生全部的警告信息。$gcc-ohellohello.cgcc編譯器就會(huì)生成一個(gè)hello的可執(zhí)行文件。在hello.c的當(dāng)前目錄下執(zhí)行./hello就可以看到程序的輸出結(jié)果,在屏幕上打印出“Hellotheworld”的字符串來。GNU編譯器生成的目標(biāo)文件默認(rèn)格式為elf(executivelinkedfile)格式,這是Linux系統(tǒng)所采用的可執(zhí)行鏈接文件的通用文件格式。elf格式由若干個(gè)段(section)組成,如果沒有特別指明,由標(biāo)準(zhǔn)c源代碼生成的目標(biāo)文件中包含以下段:

.text(正文段)包含程序的指令代碼,

.data(數(shù)據(jù)段)包含固定的數(shù)據(jù),如常量,字符串等,

.bss(未初始化數(shù)據(jù)段)包含未初始化的變量和數(shù)組等。Makefile文件和Make命令Makefile文件描述了目標(biāo)文件之間的依賴關(guān)系,以及指定編譯過程中使用的工具。一個(gè)工程中的源文件不計(jì)其數(shù),按其類型、功能、模塊分別放在若干個(gè)目錄中。Makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作。Makefile就像一個(gè)Shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。Makefile帶來的好處就是“自動(dòng)化編譯”,一旦寫好,只需要一個(gè)Make命令,整個(gè)工程完全自動(dòng)編譯,極大地提高了軟件開發(fā)的效率。Makefile的作用是根據(jù)配置的情況,構(gòu)造出需要編譯的源文件列表,然后分別編譯,并把目標(biāo)代碼鏈接到一起,最終形成可執(zhí)行的二進(jìn)制文件。Makefile中一般包含如下內(nèi)容:

需要由make工具創(chuàng)建的項(xiàng)目,通常是目標(biāo)(target)文件和可執(zhí)行文件。

要?jiǎng)?chuàng)建的項(xiàng)目依賴于哪些文件。

創(chuàng)建每個(gè)項(xiàng)目時(shí)需要運(yùn)行的命令。例如:從上面的例子可以看出,第一行指定example.o為目標(biāo),并且依賴于example.c和example.h文件。隨后的行指定了如何從目標(biāo)所依賴的文件建立目標(biāo)。當(dāng)example.c或example.h文件在編譯之后又被修改,則make工具可自動(dòng)重新編譯example.o,如果在前后兩次編譯之間,example.c和example.h均沒有被修改,而且example.o還存在的話,就沒有必要重新編譯。1.Makefile中的變量頂層Makefile定義并向環(huán)境中輸出了許多變量,為各個(gè)子目錄下的Makefile傳遞一些信息。有些變量,比如,SUBDIRS,不僅在頂層Makefile中定義并且賦初值,而且在arch/arm/Makefile還做了擴(kuò)充。常用的變量有以下幾類:

版本信息版本信息有VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION和KERNELRELEASE等變量,用來定義當(dāng)前內(nèi)核的版本。比如,VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXTRAVERSION=-rmk7,共同構(gòu)成內(nèi)核的發(fā)行版本KERNELRELEASE:2.4.18-rmk7。

CPU體系結(jié)構(gòu):ARCH在頂層Makefile的開頭,用ARCH定義目標(biāo)CPU的體系結(jié)構(gòu),比如,ARCH:=arm。許多子目錄的Makefile中,要根據(jù)ARCH的定義選擇編譯源文件的列表。

路徑信息:TOPDIR和SUBDIRSTOPDIR定義了Linux內(nèi)核源代碼所在的根目錄。例如,各個(gè)子目錄下的Makefile通過$(TOPDIR)/Rules.make就可以找到Rules.make的位置。SUBDIRS定義了一個(gè)目錄列表,在編譯內(nèi)核或模塊時(shí),頂層Makefile就是根據(jù)SUBDIRS來決定進(jìn)入哪些子目錄。SUBDIRS的值取決于內(nèi)核的配置,在頂層Makefile中SUBDIRS賦值為kerneldriversmmfsnetipclib;根據(jù)內(nèi)核的配置情況,在arch/*/Makefile中擴(kuò)充了SUBDIRS的值,可參考arch/arm/Makefile的例子。

內(nèi)核組成信息:HEAD,CORE_FILES,NETWORKS,DRIVERS,LIBS。Linux內(nèi)核文件vmlinux是由以下規(guī)則產(chǎn)生的:vmlinux:$(CONFIGURATION)init/main.oinit/version.olinuxsubdirs$(LD)$(LINKFLAGS)$(HEAD)init/main.oinit/version.o\--start-group\$(CORE_FILES)\$(DRIVERS)\$(NETWORKS)\$(LIBS)\--end-group\-ovmlinux可以看出,vmlinux是由HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS和LIBS組成的。這些變量(如HEAD)都是用來定義鏈接生成vmlinux所需的目標(biāo)文件和庫文件列表。其中,HEAD在arch/arm/Makefile中定義,用來確定最先鏈接進(jìn)vmlinux的文件列表。比如,對于ARM系列HEAD的定義為:HEAD:=arch/arm/kernel/head-$(PROCESSOR).o\表明head-$(PROCESSOR).o和init_task.o需要最先被鏈接到vmlinux中。PROCESSOR為armv或armo,取決于目標(biāo)CPU。

編譯信息:CPP,CC,AS,LD,AR,CFLAGS,LINKFLAGS在Rules.make中定義的是編譯的通用規(guī)則,具體到特定的場合,需要明確給出編譯環(huán)境,編譯環(huán)境就是在以上的變量中定義的。針對交叉編譯的要求,定義了CROSS_COMPILE。比如:CROSS_COMPILE

=

arm-linux-CC

=

$(CROSS_COMPILE)gccLD

=

$(CROSS_COMPILE)ld由于CROSS_COMPILE定義了交叉編譯器前綴arm-linux-,表明所有的交叉編譯工具都是以arm-linux-開頭的,所以在各個(gè)交叉編譯器工具之前,都加入了$(CROSS_COMPILE),以組成一個(gè)完整的交叉編譯工具文件名,比如,arm-linux-gcc。CFLAGS定義了傳遞給C編譯器的參數(shù)。LINKFLAGS是鏈接生成vmlinux時(shí),由鏈接器使用的參數(shù)。LINKFLAGS在arm/*/Makefile中定義,比如:#

arch/arm/Makefile

配置變量CONFIG_*.config文件中有許多的配置變量等式,用來說明用戶配置的結(jié)果。例如,CONFIG_MODULES=y表明用戶選擇了Linux內(nèi)核的模塊功能。.config被頂層Makefile包含后,就形成許多的配置變量,每個(gè)配置變量具有確定的值:y表示本編譯選項(xiàng)對應(yīng)的內(nèi)核代碼被靜態(tài)編譯進(jìn)Linux內(nèi)核;m表示本編譯選項(xiàng)對應(yīng)的內(nèi)核代碼被編譯成模塊;n表示不選擇此編譯選項(xiàng);如果沒有賦值,那么配置變量的值為空。Rules.make定義了所有Makefile共用的編譯規(guī)則。Linux把共用的編譯規(guī)則統(tǒng)一放置到Rules.make中,并在各自的Makefile中通過語句“includeRules.make”包含Rules.make。這樣就避免了在多個(gè)Makefile中重復(fù)同樣的規(guī)則。Rules.make文件定義了許多變量,最為重要的是編譯、鏈接列表變量。

O_OBJS、L_OBJS、OX_OBJS和LX_OBJS:這些變量代表的是本級目錄下需要編譯進(jìn)Linux內(nèi)核vmlinux的目標(biāo)文件列表,其中OX_OBJS和LX_OBJS中的“X”表明目標(biāo)文件使用了EXPORT_SYMBOL輸出符號。

M_OBJS和MX_OBJS:定義本級目錄下需要被編譯成可裝載模塊的目標(biāo)文件列表。同樣,MX_OBJS中的“X”表明目標(biāo)文件使用了EXPORT_SYMBOL輸出符號。

O_TARGET和L_TARGET:每個(gè)子目錄下都有一個(gè)O_TARGET或L_TARGET,Rules.make首先從源代碼編譯生成O_OBJS和OX_OBJS中所有的目標(biāo)文件,然后使用$(LD)-r把它們鏈接成一個(gè)O_TARGET或L_TARGET。O_TARGET以.o結(jié)尾,而L_TARGET以.a結(jié)尾。3.子目錄Makefile子目錄Makefile用來控制本級目錄下源代碼的編譯規(guī)則。#

Makefile

for

the

linux

kernel.#

All

of

the

(potential)

objects

that

export

symbols.#

This

list

comes

from

'grep

-l

EXPORT_SYMBOL

*.[hc]'.#

Object

file

lists.obj-y

:=obj-m

:=obj-n

:=obj-

:=#

Files

that

are

both

resident

and

modular:

remove

from

modular.obj-m

:=

$(filter-out

$(obj-y),

$(obj-m))#

Translate

to

Rules.make

lists.L_OBJS

:=

$(sort

$(filter-out

$(export-objs),

$(obj-y)))LX_OBJS

:=

$(sort

$(filter

$(export-objs),

$(obj-y)))M_OBJS

:=

$(sort

$(filter-out

$(export-objs),

$(obj-m)))MX_OBJS

:=

$(sort

$(filter

$(export-objs),

$(obj-m)))include

$(TOPDIR)/Rules.make4.makemake是一個(gè)命令工具,是一個(gè)解釋Makefile中指令的命令工具。一般來說,大多數(shù)的IDE都有這個(gè)命令,比如:Delphi的make,VisualC++的nmake,以及Linux下GNU的make。make命令執(zhí)行時(shí),需要一個(gè)Makefile文件,以告訴make命令怎么去編譯和鏈接程序。一般來說,最簡單的就是直接在命令行下輸入make命令,make命令會(huì)找當(dāng)前目錄的Makefile來執(zhí)行,一切都是自動(dòng)的。GNU的make工作時(shí)的執(zhí)行步驟入下:(1)讀入所有的Makefile。(2)讀入被include的其他Makefile。(3)初始化文件中的變量。(4)推導(dǎo)隱含規(guī)則,并分析所有規(guī)則。(5)為所有的目標(biāo)文件創(chuàng)建依賴關(guān)系鏈。(6)根據(jù)依賴關(guān)系,決定哪些目標(biāo)要重新生成。(7)執(zhí)行生成命令。1~5步為第一個(gè)階段,6~7步為第二個(gè)階段。第一個(gè)階段中,如果定義的變量被使用了,那么,make會(huì)把其展開在使用的位置。但make并不會(huì)完全馬上展開,make使用的是拖延戰(zhàn)術(shù),如果變量出現(xiàn)在依賴關(guān)系的規(guī)則中,那么僅當(dāng)這條依賴關(guān)系被決定要使用了,變量才會(huì)在其內(nèi)部展開。配置文件在Linux內(nèi)核中,配置命令有多種方式:Make configMake oldconfig

scripts/Configure

Make menuconfig

scripts/Menuconfig

Make xconfig

scripts/tkparse

以字符界面配置(makeconfig)為例,頂層Makefile調(diào)用scripts/Configure,按照arch/arm/config.in來進(jìn)行配置。命令執(zhí)行完后產(chǎn)生文件.config,其中保存著配置信息。下一次再做makeconfig將產(chǎn)生新的.config文件,原.config被改名為.config.old。下面對配置文件所支持的配置語句做簡要的說明。1.頂層菜單mainmenu_name

/prompt/

/prompt/

其中/prompt/是一串提示符,mainmenu_name設(shè)置最高層菜單的名字,只在makexconfig時(shí)才會(huì)顯示。2.詢問語句bool

/prompt/

/symbol/hex

/prompt/

/symbol/

/word/int

/prompt/

/symbol/

/word/string

/prompt/

/symbol/

/word/tristate

/prompt/

/symbol/詢問語句首先顯示一串提示符/prompt/,等待用戶輸入,并把輸入的結(jié)果賦給/symbol/所代表的配置變量。不同的詢問語句的區(qū)別在于它們接受的輸入數(shù)據(jù)類型不同,比如,bool接受布爾類型(y或n),hex接受16進(jìn)制數(shù)據(jù)。有些詢問語句還有第三個(gè)參數(shù)/word/,用來給出默認(rèn)值。3.定義語句define_bool

/symbol/

/word/define_hex

/symbol/

/word/define_int

/symbol/

/word/define_string

/symbol/

/word/define_tristate

/symbol/

/word/不同于詢問語句的等待用戶輸入,定義語句顯式的給配置變量/symbol/賦值/word/。4.依賴語句dep_bool

/prompt/

/symbol/

/dep/

...dep_mbool

/prompt/

/symbol/

/dep/

...dep_hex

/prompt/

/symbol/

/word/

/dep/

...dep_int

/prompt/

/symbol/

/word/

/dep/

...dep_string

/prompt/

/symbol/

/word/

/dep/

...dep_tristate

/prompt/

/symbol/

/dep/

...與詢問語句類似,依賴語句也是定義新的配置變量。不同的是,配置變量/symbol/的取值將依賴于配置變量列表/dep/…。這就意味著,被定義的配置變量所對應(yīng)功能的取舍取決于依賴列表所對應(yīng)功能的選擇。5.選擇語句choice

/prompt/

/word/

/word/choice語句首先給出一串選擇列表,供用戶選擇其中一種。比如,LinuxforARM支持多種基于ARM核的CPU,Linux使用choice語句提供一個(gè)CPU列表,供用戶選擇:choice

‘ARM

system

type’

\"Anakin

CONFIG_ARCH_ANAKIN

\……SA1100-based

CONFIG_ARCH_SA1100

6.if語句if

[

/expr/

]

then/statement/

...

fi

if

[

/expr/

]

then

/statement/

...

else

/statement/

...

fi

if語句對配置變量進(jìn)行判斷,并做出不同的處理。判斷條件/expr/可以是單個(gè)配置變量或字符串,也可以是帶操作符的表達(dá)式。操作符有:=,!=,-o,-a等。7.菜單塊(menublock)語句mainmenu_option

next_commentcomment

'…endmenu8.Source語句source

/word//word/是文件名,source的作用是調(diào)入新的文件。Vi編輯器的使用1.工作模式Vi是“Visualinterface”的簡稱,可以執(zhí)行輸出、刪除、查找、替換、塊操作等眾多文本操作,而且用戶可以根據(jù)自己的需要對其進(jìn)行定制。Vi有三種基本工作模式:命令行模式、文本輸入模式和末行模式。

命令行模式任何時(shí)候,不管用戶處于何種模式,只要按一下鍵,即可使Vi進(jìn)入命令行模式;我們在shell環(huán)境(提示符為$)下輸入啟動(dòng)命令“vi”,進(jìn)入編輯器時(shí),也是處于該模式下。在該模式下,用戶可以輸入各種合法的Vi命令,用于管理自己的文檔。此時(shí)從鍵盤上輸入的任何字符都被當(dāng)做編輯命令來解釋,若輸入的字符是合法的Vi命令,則Vi在接受用戶命令之后完成相應(yīng)的動(dòng)作。但需注意的是,所輸入的命令并不在屏幕上顯示出來。若輸入的字符不是Vi的合法命令,Vi會(huì)響鈴報(bào)警。

文本輸入模式在命令模式下輸入插入命令i、附加命令a、打開命令o、修改命令c、取代命令r或替換命令s都可以進(jìn)入文本輸入模式。在該模式下,用戶輸入的任何字符都被Vi當(dāng)做文件內(nèi)容保存起來,并將其顯示在屏幕上。在文本輸入過程中,若想回到命令模式下,按Esc鍵即可。

末行模式在命令模式下,用戶按“:”鍵即可進(jìn)入末行模式下,此時(shí)Vi會(huì)在顯示窗口的最后一行,顯示一個(gè)“:”作為末行模式的提示符,等待用戶輸入命令。多數(shù)文件管理命令都是在此模式下執(zhí)行的。末行命令執(zhí)行完后,Vi自動(dòng)回到命令模式。2.進(jìn)入在提示符“$”后鍵入Vi和想要編輯的文件名,便可進(jìn)入Vi。例如:如果只鍵入Vi,而不帶文件名,也可以進(jìn)入Vi,之后在光標(biāo)處鍵入文件內(nèi)容。進(jìn)入Vi后,首先進(jìn)入的就是命令模式,進(jìn)入Vi時(shí),用戶不僅可以指定一個(gè)待編輯的文件名,而且還有許多附加操作。如果希望在進(jìn)入Vi之后,光標(biāo)處于文件中特定的某行上,可在Vi后加上選項(xiàng)+n,其中n為指定的行數(shù)。例如:3.退出在命令模式中,連按兩次大寫字母Z,若當(dāng)前編輯的文件曾被修改過,則Vi保存該文件后退出,返回到shell;若當(dāng)前編輯的文件沒被修改過,則Vi直接退出,返回到shell。在末行模式下,輸入命令::wVi保存當(dāng)前編輯文件,但并不退出,而是繼續(xù)等待用戶輸入命令。在使用w命令時(shí),可以再給編輯文件起一個(gè)新的文件名。:w

newfile此時(shí)Vi將把當(dāng)前文件的內(nèi)容保存到指定的newfile中,而原有文件保持不變。在末行模式下,輸入命令::q系統(tǒng)退出Vi返回到shell。若在用此命令退出Vi時(shí),編輯文件沒有被保存,則Vi在顯示窗口的最末行顯示如下信息:No

write

since

last

change

(use

!

to

overrides)提示用戶該文件被修改后沒有保存,然后Vi并不退出,繼續(xù)等待用戶命令。若用戶就是不想保存被修改后的文件而要強(qiáng)行退出Vi時(shí),可使用命令::q!Linux下C語言編程1.文件讀寫當(dāng)需要打開一個(gè)文件進(jìn)行讀寫操作的時(shí)候,可以使用系統(tǒng)調(diào)用函數(shù)open。文件操作完成后,調(diào)用close函數(shù)關(guān)閉文件。int

open(const

char

*pathname,int

flags);

int

open(const

char

*pathname,int

flags,mode_t

mode);

int

close(int

fd);

open函數(shù)有兩個(gè)形式,在此只介紹第一種方式。其中pathname是我們要打開的文件名,包含路徑名稱,默認(rèn)是在當(dāng)前路徑下。Flags取值可以是下面的一個(gè)值或者是幾個(gè)值的組合。

O_RDONLY:以只讀的方式打開文件;

O_WRONLY:以只寫的方式打開文件;

O_RDWR:以讀寫的方式打開文件;

O_APPEND:以追加的方式打開文件;

O_CREAT:創(chuàng)建一個(gè)文件;要采用open的第二種形式;

O_EXEC:使用該選項(xiàng)時(shí),如果使用了O_CREAT而且文件已經(jīng)存在,就會(huì)發(fā)生一個(gè)錯(cuò)誤;

O_NOBLOCK:以非阻塞的方式打開一個(gè)文件;

O_TRUNC:如果文件已經(jīng)存在,則刪除文件的內(nèi)容。如果打開文件成功,open會(huì)返回一個(gè)文件描述符。以后對文件的所有操作就可以對這個(gè)文件描述符進(jìn)行操作了。當(dāng)操作完成以后,要關(guān)閉文件時(shí),只要調(diào)用close就可以了,其中fd是要關(guān)閉的文件描述符。文件打開后,就可以調(diào)用函數(shù)read和write進(jìn)行文件的讀寫。ssize_t

read(int

fd,

void

*buffer,size_t

count);

ssize_t

write(int

fd,

const

void

*buffer,size_t

count);

fd是要進(jìn)行讀寫操作的文件描述符,buffer是要寫入文件內(nèi)容或讀出文件內(nèi)容的內(nèi)存地址,count是要讀寫的字節(jié)數(shù)。對于普通的文件,read從指定的文件(fd)中讀取count個(gè)字節(jié)到buffer緩沖區(qū)中,同時(shí)返回count。如果read讀到了文件的結(jié)尾或者被一個(gè)信號所中斷,返回值會(huì)小于count。如果是由信號中斷引起返回,而且沒有讀出數(shù)據(jù),read會(huì)返回1,且設(shè)置errno為EINTR。當(dāng)程序讀到了文件結(jié)尾的時(shí)候,read會(huì)返回0。write從buffer中寫count字節(jié)到文件fd中,成功時(shí)返回實(shí)際所寫的字節(jié)數(shù)。#define

BUFFER_SIZE

1024

int

main(int

argc,char

**argv)

{

int

from_fd,to_fd;int

bytes_read,bytes_write;

char

buffer[BUFFER_SIZE];

char

*ptr;

if(argc!=3)

{

fprintf(stderr,"Usage:%s

fromfile

tofile

a",argv[0]);

exit(1);

}

if((from_fd=open(argv[1],O_RDONLY))==-1)

{

fprintf(stderr,"Open

%s

Error:%s

",argv[1],strerror(errno));

exit(1);

}

if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)

{

fprintf(stderr,"Open

%s

Error:%s

",argv[2],strerror(errno));

exit(1);

}

while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))

{

if((bytes_read==-1)&&(errno!=EINTR))

break;

else

if(bytes_read>0)

{

ptr=buffer;

while(bytes_write=write(to_fd,ptr,bytes_read))

{

if((bytes_write==-1)&&(errno!=EINTR))break;

else

if(bytes_write==bytes_read)

break;

else

if(bytes_write>0)

{

ptr+=bytes_write;

bytes_read-=bytes_write;

}

}

if(bytes_write==-1)break;

}

}

close(from_fd);

close(to_fd);

exit(0);

}2.文件屬性文件具有各種各樣的屬性,除了上面提到的文件權(quán)限以外,文件還有創(chuàng)建時(shí)間、大小等屬性。有時(shí)候要判斷文件是否可以進(jìn)行某種操作,此時(shí)可以使用access函數(shù)。int

access(const

char

*pathname,int

mode);

其中,pathname是文件名稱,mode是所要判斷的屬性??梢匀∫韵轮祷蛘呤瞧浣M合:

R_OK文件可以讀;

W_OK文件可以寫;

X_OK文件可以執(zhí)行;

F_OK文件存在。當(dāng)測試成功時(shí),函數(shù)返回0;如果有一個(gè)條件不符時(shí),返回1。如果要獲得文件的其他屬性,可以使用函數(shù)stat或者

stat(const

char

*file_name,struct

stat

*buf);

int

fstat(int

filedes,struct

stat

*buf);

struct

stat

{

dev_t

st_dev;

/*

設(shè)備

*/

ino_t

st_ino;

/*

節(jié)點(diǎn)

*/

mode_t

st_mode;

/*

模式

*/

nlink_t

st_nlink;

/*

硬連接

*/

uid_t

st_uid;

/*

用戶ID

*/

gid_t

st_gid;

/*

組ID

*/

dev_t

st_rdev;

/*

設(shè)備類型

*/

off_t

st_off;

/*

文件字節(jié)數(shù)

*/

unsigned

long

st_blksize;

/*

塊大小

*/

unsigned

long

st_blocks;

/*

塊數(shù)

*/

time_t

st_atime;

/*

最后一次訪問時(shí)間

*/

time_t

st_mtime;

/*

最后一次修改時(shí)間

*/

time_t

st_ctime;

/*

最后一次改變時(shí)間(指屬性)*/

};stat用來判斷沒有打開的文件,而fstat用來判斷打開的文件。使用最多的屬性一般是st_mode,通過該屬性可以判斷給定的文件是一個(gè)普通文件,還是一個(gè)目錄或者連接??梢允褂孟旅鎺讉€(gè)宏來判斷:

S_ISLNK(st_mode),是否是一個(gè)連接;

S_ISREG,是否是一個(gè)常規(guī)文件;

S_ISDIR,是否是一個(gè)目錄;

S_ISCHR,是否是一個(gè)字符設(shè)備;

S_ISBLK,是否是一個(gè)塊設(shè)備;

S_ISFIFO,是否是一個(gè)FIFO文件;

S_ISSOCK,是否是一個(gè)SOCKET文件。3.目錄文件的操作在編寫程序的時(shí)候,有時(shí)候想得到當(dāng)前的工作路徑。C庫函數(shù)提供了getcwd來解決這個(gè)問題:char

*getcwd(char

*buffer,size_t

size);

如果提供一個(gè)size大小的buffer,getcwd會(huì)把當(dāng)前的路徑拷貝到buffer中,如果buffer太小,函數(shù)會(huì)返回1和一個(gè)錯(cuò)誤號。Linux提供了大量的目錄操作函數(shù),下面列出了幾個(gè)比較簡單和常用的函數(shù):int

mkdir(const

char

*path,mode_t

mode);DIR

*opendir(const

char

*path);

struct

dirent

*readdir(DIR

*dir);

void

rewinddir(DIR

*dir);

int

closedir(DIR

*dir);

mkdir就是創(chuàng)建一個(gè)目錄,opendir打開一個(gè)目錄為以后讀做準(zhǔn)備,readdir讀一個(gè)打開的目錄,rewinddir是用來重讀目錄的,closedir是關(guān)閉一個(gè)目錄。下面的程序有一個(gè)輸入?yún)?shù),如果這個(gè)參數(shù)是一個(gè)文件名,輸出這個(gè)文件的大小和最后修改的時(shí)間;如果是一個(gè)目錄,輸出這個(gè)目錄下所有文件的大小和修改時(shí)間。static

int

get_file_size_time(const

char

*filename)

{

struct

stat

statbuf;

if(stat(filename,&statbuf)==-1)

{

printf("Get

stat

on

%s

Error:%s

",

filename,strerror(errno));

return(-1);

}

if(S_ISDIR(statbuf.st_mode))return(1);

if(S_ISREG(statbuf.st_mode))

printf("%ssize:%ldbytesmodifiedat%s",filename,statbuf.st_size,ctime(&statbuf.st_mtime));return(0);

}

int

main(int

argc,char

**argv)

{

DIR

*dirp;

struct

dirent

*direntp;

int

stats;

if(argc!=2)

{

printf("Usage:%s

filename

a",argv[0]);

exit(1);

}

if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1);

if((dirp=opendir(argv[1]))==NULL)

{

printf("Open

Directory

%s

Error:%s

",

argv[1],strerror(errno));

exit(1);

}

while((direntp=readdir(dirp))!=NULL)if(get_file_size_time(direntp-closedir(dirp);

exit(1);

}

5.3引導(dǎo)程序的移植在掌握GNU開發(fā)工具后,就需要對系統(tǒng)的引導(dǎo)程序進(jìn)行移植了。嵌入式的引導(dǎo)程序是與處理器體系結(jié)構(gòu)緊密聯(lián)系的,是嵌入式系統(tǒng)開發(fā)的難點(diǎn)之一,同時(shí)也是系統(tǒng)運(yùn)行的一個(gè)基本前提條件,沒有這段和硬件緊密相連的代碼,多么精悍的內(nèi)核也發(fā)揮不了作用。嵌入式Linux的引導(dǎo)機(jī)理分析一個(gè)嵌入式軟件系統(tǒng)通??梢苑譃橐龑?dǎo)程序BootLoader、操作系統(tǒng)內(nèi)核、文件系統(tǒng)和用戶應(yīng)用程序4個(gè)層次。1.BootLoader工作原理BootLoader是系統(tǒng)加電啟動(dòng)運(yùn)行的第一段軟件代碼。通過PC的體系結(jié)構(gòu)我們可以知道,PC機(jī)中的引導(dǎo)加載程序由BIOS(其本質(zhì)就是一段固件程序)和位于硬盤MBR中的引導(dǎo)程序一起組成。BIOS在完成硬件檢測和資源分配后,將硬盤MBR中的引導(dǎo)程序讀到系統(tǒng)的RAM中,然后將控制權(quán)交給引導(dǎo)程序。引導(dǎo)程序的主要運(yùn)行任務(wù)是將內(nèi)核映像從硬盤上讀到RAM中,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行,也即開始啟動(dòng)操作系統(tǒng)。由于在嵌入式系統(tǒng)中,通常并沒有像BIOS那樣的固件程序(有的嵌入式系統(tǒng)也會(huì)內(nèi)嵌一段短小的啟動(dòng)程序),因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由BootLoader來完成。對于一個(gè)嵌入式系統(tǒng)來說,可能有的包括操作系統(tǒng),有的小型系統(tǒng)也可以只包括應(yīng)用程序,但是在這之前都需要BootLoader為它準(zhǔn)備一個(gè)正確的環(huán)境。通常,BootLoader是依賴于硬件而實(shí)現(xiàn)的,特別是在嵌入式領(lǐng)域,為嵌入式系統(tǒng)建立一個(gè)通用的BootLoader是很困難的。但是可以歸納一些通用的概念出來,以便簡化特定BootLoader的設(shè)計(jì)與實(shí)現(xiàn)。簡單地說,BootLoader是在操作系統(tǒng)內(nèi)核或用戶應(yīng)用程序運(yùn)行之前運(yùn)行的一段小程序。通過這段小程序,可以初始化硬件設(shè)備、建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的狀態(tài),為最終調(diào)用操作系統(tǒng)內(nèi)核或用戶應(yīng)用程序準(zhǔn)備好正確的環(huán)境。大多數(shù)BootLoader都包含兩種不同的操作模式:“啟動(dòng)加載”模式和“下載”模式,這兩種模式的區(qū)別僅對于開發(fā)人員才有意義。從最終用戶的角度看,BootLoader的作用是用來加載操作系統(tǒng),并不存在所謂的啟動(dòng)加載模式與下載工作模式的區(qū)別。啟動(dòng)加載(BootLoading)模式:這種模式也稱為“自主”(Autonomous)模式。也即BootLoader從目標(biāo)機(jī)上的某個(gè)固態(tài)存儲(chǔ)設(shè)備上將操作系統(tǒng)加載到RAM中運(yùn)行,整個(gè)過程并沒有用戶的介入。這種模式是BootLoader的正常工作模式,在嵌入式產(chǎn)品發(fā)布的時(shí)候,BootLoader顯然必須工作在這種模式下。下載(Downloading)模式:在這種模式下,目標(biāo)機(jī)上的BootLoader將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)下載文件,比如,下載內(nèi)核映像和根文件系統(tǒng)映像等。從主機(jī)下載的文件通常首先被BootLoader保存到目標(biāo)機(jī)的RAM中,然后再被BootLoader寫到目標(biāo)機(jī)上的FLASH類固態(tài)存儲(chǔ)設(shè)備中。BootLoader的這種模式通常在第一次安裝內(nèi)核與根文件系統(tǒng)時(shí)被使用;此外,以后的系統(tǒng)更新也會(huì)使用BootLoader的這種工作模式。工作于這種模式下的BootLoader通常都會(huì)向它的終端用戶提供一個(gè)簡單的命令行接口。最常見的情況是,目標(biāo)機(jī)上的BootLoader通過串口與主機(jī)之間進(jìn)行文件傳輸,傳輸協(xié)議通常是xmodem/ymodem/zmodem協(xié)議中的一種。但是,由于串口傳輸?shù)乃俣仁怯邢薜?,因此通過以太網(wǎng)連接并借助TFTP協(xié)議來下載文件是個(gè)更好的選擇。但是,在通過以太網(wǎng)連接和TFTP協(xié)議來下載文件時(shí),因?yàn)橹鳈C(jī)方必須有一個(gè)軟件用來的提供TFTP服務(wù),所以操作相對復(fù)雜。2.BootLoader的啟動(dòng)過程從操作系統(tǒng)的角度看,BootLoader的總目標(biāo)就是正確地調(diào)用內(nèi)核來執(zhí)行。BootLoader的啟動(dòng)過程大多數(shù)分為階段1和階段2。階段1主要包含依賴于CPU體系結(jié)構(gòu)的硬件初始化代碼,而且通常都是用匯編語言來實(shí)現(xiàn)的,以達(dá)到短小精悍的目的。這個(gè)階段通常包括以下步驟:(1)硬件設(shè)備初始化。這是BootLoader開始就執(zhí)行的操作,其目的是為階段2的執(zhí)行,以及隨后內(nèi)核的執(zhí)行準(zhǔn)備好基本的硬件環(huán)境。(2)為加載BootLoader的階段2準(zhǔn)備RAM空間。為了獲得更快的執(zhí)行速度,通常把階段2加載到RAM空間來執(zhí)行。(3)拷貝BootLoader階段2的代碼到RAM空間中。(4)設(shè)置好堆棧。(5)跳轉(zhuǎn)到階段2的C程序入口點(diǎn)。BootLoader的階段2通常用C語言來實(shí)現(xiàn),這樣可以實(shí)現(xiàn)更復(fù)雜的功能,而且代碼會(huì)具有更好的可讀性和可移植性。通常包括以下步驟:(1)初始化本階段要使用到的硬件設(shè)備。(2)檢測系統(tǒng)內(nèi)存映射。(3)將內(nèi)核映像和根文件系統(tǒng)映像從Flash上讀到RAM空間中。(4)為內(nèi)核設(shè)置啟動(dòng)參數(shù)。(5)調(diào)用內(nèi)核。3.BootLoader代碼分析下面對引導(dǎo)程序2410INIT.S進(jìn)行分析,以加深對BootLoader的理解。在第一階段完成依賴于體系結(jié)構(gòu)硬件初始化的代碼,包括禁止看門狗、禁止中斷、初始化各控制寄存器拷貝自身到RAM等。

…… IMPORTMain AREAInit,CODE,READONLY ENTRY …… b ResetHandler ……ResetHandler ldr r0,=WTCON ldr r1,=0x0 str r1,[r0] ldr r0,=INTMSK ldr r1,=0xffffffff str r1,[r0] ldr r0,=INTSUBMSK ldr r1,=0x7ff str r1,[r0] …… ldr r0,=LOCKTIME ldr r1,=0xffffff str r1,[r0] …… ldr r0,=SMRDATA ldr r1,=BWSCON add r2,r0,#52 0 ldr r3,[r0],#4 str r3,[r1],#4 cmp r2,r0 bne %B0

bl InitStacks ldr r0,=HandleIRQ ldr r1,=IsrIRQ str r1,[r0] ;拷貝自身代碼到RAM ldr r0,=|Image$$RO$$Limit| ; ldr r1,=|Image$$RW$$Base| ; ldr r3,=|Image$$ZI$$Base| cmp r0,r1 beq %F21 cmp r1,r3 ldrcc r2,[r0],#4 strcc r2,[r1],#4 bcc %B12 ldr r1,=|Image$$ZI$$Limit| mov r2,#03 cmp r3,r1 strcc r2,[r3],#4 bcc %B3 …… bl Main b . …… END第二階段通常用C語言實(shí)現(xiàn),包括內(nèi)存管理單元初始化、時(shí)鐘設(shè)置、端口設(shè)置和串口初始化等。voidIsr_Init(void){rINTMOD=0x0;//工作在IRQ模式rINTMSK=BIT_ALLMSK;//屏蔽中斷rINTSUBMSK=BIT_SUB_ALLMSK;//屏蔽子中斷}voidMain(void){MMU_Init(); //MMU初始化ChangeClockDivider(1,1);//設(shè)置時(shí)鐘除法器->1:2:4ChangeMPllValue(0xa1,0x3,0x1);//時(shí)鐘值FCLK=202.8MHzPort_Init(); Isr_Init();Uart_Init(0,115200);Uart_Select(0);while(1){ …… Uart_Printf("\n\nSMDK2410Board(MCUS3C2410)ExampleProgramVer1.0(20020521)FCLK=%dHz\n\n",FCLK); ……}}與一般的C語言程序一樣,第二階段程序從Main()函數(shù)開始。因?yàn)樗捎玫腟3C2410X處理器內(nèi)含存儲(chǔ)器管理單元MMU,所以首先需要進(jìn)行存儲(chǔ)器管理單元的初始化。緊接著設(shè)置系統(tǒng)工作主時(shí)鐘,若使用USB設(shè)備則USB時(shí)鐘也需要做相應(yīng)初始化。其次對端口工作狀態(tài)進(jìn)行設(shè)定。然后設(shè)定處理器工作模式,以及中斷控制。最后初始化串口,與宿主機(jī)建立聯(lián)系,以利于顯示調(diào)試信息。啟動(dòng)成功后,可以執(zhí)行主程序。VIVI簡介VIVI是韓國Mizi公司開發(fā)的BootLoader,可用于ARM9處理器的引導(dǎo)。VIVI利用串行通信為用戶提供接口。為連接VIVI,首先利用串口電纜連接宿主機(jī)和目標(biāo)板,然后在主機(jī)上運(yùn)行串口通信程序,并在目標(biāo)板上正確設(shè)置VIVI以支持串口。正確連接后,就可以由串口通信程序顯示提示信息,提示信息的最后一行如下所示:PressReturntostarttheLINUXnow,anyotherkeyforvivi.VIVI也有前面說過的兩種工作模式,啟動(dòng)模式可以在一段時(shí)間后自行啟動(dòng)Linux內(nèi)核,這是VIVI的默認(rèn)方式。出現(xiàn)上述信息后,如果按除回車鍵外的任意鍵,即可進(jìn)入下載模式,出現(xiàn)“vivi>”提示符。在下載模式下,VIVI為用戶提供了一個(gè)命令行接口,通過該接口可以使用VIVI提供的一些命令。1.load命令將二進(jìn)制文件載入到Flash或者RAM,命令格式:load<media_type>[<partname>|<addr><size>]<x|y|z>其中命令行參數(shù)<media_type>描述裝載位置,有flash和ram兩種選項(xiàng);參數(shù)[<partname>]或[<addr><size>]描述裝載的地址,如果有提前定義的mtd分區(qū)信息,可以只輸入分區(qū)名稱,否則需要指定地址和大??;參數(shù)<x|y|z>確定文件的傳輸協(xié)議,常采用的選項(xiàng)“x”用來指定采用xmodem協(xié)議。例如:vivi>loadflashkernelx,裝載壓縮映像文件zImage到flash存儲(chǔ)器中,地址是kernel分區(qū),并采用xmodem傳輸協(xié)議。也可以指定地址和大小,例如:vivi>loadflash0x800000xc0000x。2.part命令操作MTD分區(qū)信息,比如,顯示、增加、刪除、復(fù)位、保存MTD分區(qū)等。

partshow:顯示mtd分區(qū)信息。

partadd<name><offset><size><flag>:增加新的mtd分區(qū),其中<name>為新mtd分區(qū)名稱,<offset>是mtd器件的偏移,<size>表示mtd分區(qū)的大小,<flag>表示分區(qū)類型,可選項(xiàng)有JFFS2、LOCKED和BONFS。

partdel<partname>:刪除一個(gè)mtd分區(qū)。

partreset:恢復(fù)mtd分區(qū)為默認(rèn)值。

partsave:在flash中永久保存參數(shù)值和分區(qū)信息。3.param命令用來設(shè)置或者察看參數(shù)。例如:改變“l(fā)inuxcommandline”,使用vivi>paramsetlinux_cmd_line"youwish."。也可以改變引導(dǎo)程序啟動(dòng)的時(shí)間,使用vivi>paramsetboot_delay100000實(shí)現(xiàn)。4.boot命令用來引導(dǎo)存儲(chǔ)在flash存儲(chǔ)器或者ram中的linux內(nèi)核。命令格式:boot<media_type>[<partname>|<addr><size>]參數(shù)<media_type>設(shè)定存儲(chǔ)linux內(nèi)核映像的位置,可選項(xiàng)有ram、nor和smc。參數(shù)[<partname>]或[<addr><size>]描述存儲(chǔ)內(nèi)核的地址,如果有提前定義的mtd分區(qū)信息,可以只輸入分區(qū)名稱,否則需要指定地址和大小。例如:vivi>bootnor0x80000表示從flash存儲(chǔ)器中讀出linux內(nèi)核,偏移是0x80000。5.flash命令存儲(chǔ)器管理命令,例如:flasherase[<partname>|<offset><size>],表示擦除flash存儲(chǔ)器。VIVI的代碼分析與移植1.a(chǎn)rch此目錄包括了所有VIVI支持的目標(biāo)板的子目錄,本書附帶的代碼中只包含S3C2410X。2.Documentation存放了許多文檔,非常詳細(xì),主要是VIVI的使用指南。3.drivers其中包括了引導(dǎo)內(nèi)核所需的MTD設(shè)備和串口驅(qū)動(dòng)程序。MTD目錄下分maps、nand和nor三個(gè)目錄,實(shí)現(xiàn)對NandFlash和NorFlash的讀寫控制。Serial目錄下的文件實(shí)現(xiàn)對串口的控制,并支持xmodem和ymodem協(xié)議。4.include頭文件的公共目錄,其中的定義了處理器的一些寄存器,以及NANDFlash的一些寄存器等。定義了與目標(biāo)板相關(guān)的資源配置參數(shù),修改波特率、引導(dǎo)參數(shù)和物理內(nèi)存映射等參數(shù)就可適用于自己的目標(biāo)板。5.initinit目錄,這個(gè)目錄只有和兩個(gè)文件。與普通的C程序一樣,VIVI將從main函數(shù)開始執(zhí)行。6.lib一些平臺(tái)公共的接口代碼,比如,里的udelay()和mdelay()。7.scripts主要在配置時(shí)用到,存放了配置所需的腳本文件,如Menuconfig和Configure文件,以方便對VIVI的配置。

VIVI的運(yùn)行也可以分為兩個(gè)階段。在第一階段完成含有依賴于CPU體系結(jié)構(gòu)硬件初始化的代碼,利用匯編語言完成。第二階段是用C語言完成的。在跳轉(zhuǎn)進(jìn)main()函數(shù)之前,利用匯編語言編寫了一段trampoline程序作為階段2可執(zhí)行鏡像的執(zhí)行入口點(diǎn)。之后可以在trampoline中用處理器的跳轉(zhuǎn)指令進(jìn)入main()函數(shù)中去執(zhí)行。當(dāng)main()函數(shù)返回時(shí),CPU就進(jìn)行復(fù)位。Trampoline程序的源代碼如下:ldr sp,DW_STACK_START mov fp,#0 mov a2,#0 bl main mov pc,#FLASE_BASE

main()函數(shù)是在Flash中還是RAM中執(zhí)行,與選擇NandFlash還是NorFlash啟動(dòng)有關(guān)。VIVI從NandFlash中啟動(dòng)時(shí),在VIVI配置的時(shí)候定義宏CONFIG_S3C2410_NAND_BOOT以編譯以下代碼段。#ifdefCONFIG_S3C2410_NAND_BOOTblcopy_myselfldrr1,=on_the_ramaddpc,r1,#0nopnop1:b1b@infiniteloopon_the_ram:#endif該代碼首先是將VIVI自身復(fù)制到RAM中,并跳轉(zhuǎn)到RAM中去執(zhí)行。芯片復(fù)位時(shí),NandFlash中最前面的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論