bootloader和RO,RW,ZI在ARMC語言中
熟悉x86體系結(jié)構(gòu)的朋友肯定知道,x86平臺(tái)上bootloader是由BIOS和位于硬盤MBR中的OS Bootloader(比如Lilo和Grub)組成的。BIOS完成硬件的檢測和資源的分配后,將硬盤MBR中的bootloader讀到系統(tǒng)RAM中,之后此bootloader就會(huì)開始進(jìn)行主導(dǎo),將內(nèi)核搬到內(nèi)存中以及進(jìn)行一些必要的初始化工作,之后跳到內(nèi)核的入口地址來執(zhí)行,這樣內(nèi)核就開始啟動(dòng),也就是系統(tǒng)就啟動(dòng)起來了。
這里不得不插入一個(gè)話題,通過上面的介紹,細(xì)心的朋友就會(huì)產(chǎn)生一個(gè)疑問:為什么要有bootloader?既然bootloader只是作硬件的初始化并將內(nèi)核引導(dǎo)起來,那為什么不直接將這段代碼加到內(nèi)核中,直接啟動(dòng)內(nèi)核就完成所有的工作?實(shí)際上要將bootloader與內(nèi)核整合在一起是完全可以做到的,但是如果這樣作的話,內(nèi)核就會(huì)失去他的通用性和靈活性,并且將bootloader與內(nèi)核分開會(huì)更有利于開發(fā)和管理,將啟動(dòng)過程中與平臺(tái)硬件相關(guān)的代碼集合成bootloader,內(nèi)核就可以集中處理那些平臺(tái)通用的部分了(當(dāng)然實(shí)際上并沒有這么嚴(yán)格的劃分,內(nèi)核中還是會(huì)有一些平臺(tái)相關(guān)的代碼,不過已經(jīng)算是比較通用的了)。
=============================================================================================================================================================================
一個(gè)ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量;RO就是readonly,
RW是程序中的已初始化變量; RW就是read/write,
(2) ARM映像文件的組成
所謂ARM映像文件就是指燒錄到ROM中的bin文件,也成為image文件。以下用Image文件來稱呼它。Image文件包含了RO和RW數(shù)據(jù)。之所以Image文件不包含ZI數(shù)據(jù),是因?yàn)閆I數(shù)據(jù)都是0,沒必要包含,只要程序運(yùn)行之前將ZI數(shù)據(jù)所在的區(qū)域一律清零即可。包含進(jìn)去反而浪費(fèi)存儲(chǔ)空間。
Q:為什么Image中必須包含RO和RW?
A:因?yàn)镽O中的指令和常量以及RW中初始化過的變量是不能像ZI那樣“無中生有”的。
實(shí)際上,ROM中的指令至少應(yīng)該有這樣的功能:
1. 將RW從ROM中搬到RAM中,因?yàn)镽W是變量,變量不能存在ROM中。
2. 將ZI所在的RAM區(qū)域全部清零,因?yàn)閆I區(qū)域并不在Image中,所以需要程序根據(jù)編譯器給出的ZI地址及大小來將相應(yīng)得RAM區(qū)域清零。ZI中也是變量,同理:變量不能存在ROM中.在程序運(yùn)行的最初階段,RO中的指令完成了這兩項(xiàng)工作后C程序才能正常訪問變量。否則只能運(yùn)行不含變量的代碼。
下面我將給出幾個(gè)例子,最直觀的來說明RO,RW,ZI在C中是什么意思。
1; RO
看下面兩段程序,他們之間差了一條語句,這條語句就是聲明一個(gè)字符常量。因此按照我們之前說的,他們之間應(yīng)該只會(huì)在RO數(shù)據(jù)中相差一個(gè)字節(jié)(字符常量為1字節(jié))。
Prog1:
#include
void main(void)
{
;
}
Prog2:
#include
const char a = 5;
void main(void)
{
;
}
Prog1編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog2編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
以上兩個(gè)程序編譯出來后的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data兩類數(shù)據(jù)。他們的唯一區(qū)別就是Prog2的RO Data比Prog1多了1個(gè)字節(jié)。這正和之前的推測一致。如果增加的是一條指令而不是一個(gè)常量,則結(jié)果應(yīng)該是Code數(shù)據(jù)大小有差別。
2; RW
同樣再看兩個(gè)程序,他們之間只相差一個(gè)“已初始化的變量”,按照之前所講的,已初始化的變量應(yīng)該是算在RW中的,所以兩個(gè)程序之間應(yīng)該是RW大小有區(qū)別。
Prog3:
#include
void main(void)
{
;
}
Prog4:
#include
char a = 5;
void main(void)
{
;
}
Prog3編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
可以看出Prog3和Prog4之間確實(shí)只有RW Data之間相差了1個(gè)字節(jié),這個(gè)字節(jié)正是被初始化過的一個(gè)字符型變量“a”所引起的。
再看兩個(gè)程序,他們之間的差別是一個(gè)未初始化的變量“a”,從之前的了解中,應(yīng)該可以推測,這兩個(gè)程序之間應(yīng)該只有ZI大小有差別。
Prog3:
#include
void main(void)
{
;
}
Prog4:
#include
char a;
void main(void)
{
;
}
Prog3編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
編譯的結(jié)果完全符合推測,只有ZI數(shù)據(jù)相差了1個(gè)字節(jié)。這個(gè)字節(jié)正是未初始化的一個(gè)字符型變量“a”所引起的。
注意:如果一個(gè)變量被初始化為0,則該變量的處理方法與未初始化華變量一樣放在ZI區(qū)域。
即:ARM C程序中,所有的未初始化變量都會(huì)被自動(dòng)初始化為0。
總結(jié): 1; C中的指令以及常量被編譯后是RO類型數(shù)據(jù)。
2; C中的未被初始化或初始化為0的變量編譯后是ZI類型數(shù)據(jù)。
3; C中的已被初始化成非0值的變量編譯后市RW類型數(shù)據(jù)。
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
(5) Image$$??$$Limit 的含義
=0x0c100000+Tatal RO size+1
= 0x0c200000+0x37(4的倍數(shù),0到55,共56個(gè)單元)
vivi是mizi開發(fā)的用于s3c241x/s3c244x 的linux bootloader,友善之臂移植了USB 下載功能后就成了現(xiàn)在看到的supervivi;u-boot是一個(gè)廣泛用于ARM平臺(tái)的bootloader, 目前也支持s3c241x/s3c244x,可以用來啟動(dòng)Linux;Eboot是WinCE平臺(tái)下的bootloader。uboot就是通過usb來下載os image文件的bootloader; eboot就是通過ethernet下載os image的bootloader。
而嵌入式平臺(tái)上就跟x86不一樣了,但是很類似,而且因?yàn)椴煌钠脚_(tái)架構(gòu)本身的特點(diǎn),每種平臺(tái)對應(yīng)的bootloader做得事情會(huì)有所不同,相對x86平臺(tái),一般不會(huì)有bios(但是這些都不是絕對的,有一些平臺(tái)也會(huì)有內(nèi)嵌類似bios的啟動(dòng)程序),整個(gè)系統(tǒng)的引導(dǎo)加載都由存放在flash,rom等存儲(chǔ)設(shè)備特定位置的bootloader來完成。如arm平臺(tái)中的2410,2440,bootloader存在在flash中的0x0的地方,板子加電后,系統(tǒng)會(huì)將bootloader的最前面的4k代碼通過硬件邏輯自動(dòng)的裝載到SRAM中,之后從SRAM中的0開始執(zhí)行,在這4k的程序中會(huì)完成基本的硬件的初始化,將完整的bootloader搬到內(nèi)存中,并跳轉(zhuǎn)到ram中的bootloader來進(jìn)行繼續(xù)執(zhí)行。
回到之前所說的,bootloader啟動(dòng)起來之后,通常會(huì)有兩種操作模式:
啟動(dòng)加載模式就是一上電,bootloader進(jìn)行相關(guān)的初始化之后就馬上把內(nèi)核啟動(dòng)起來,注意關(guān)鍵的地方在整個(gè)過程中沒有用戶的參與,這種其實(shí)也就是bootloader的默認(rèn)處理,一般的產(chǎn)品設(shè)計(jì)ok進(jìn)行最后的發(fā)布時(shí),就會(huì)處于此種狀態(tài)。
下載模這種模式,大家肯定非常熟悉,就是大家在進(jìn)行開發(fā)的時(shí)候所處的環(huán)境,我們經(jīng)常使用的tftp, erase, cp.b等命令將相關(guān)的bin,img文件燒到板子上,這種情況下其實(shí)就是處于bootloader的執(zhí)行環(huán)境下,所以一定意義來說,大多的bootloader其實(shí)就是一個(gè)嵌入式操作系統(tǒng),只是它的功能不強(qiáng),不像linux的結(jié)構(gòu)那么復(fù)雜,而且也不會(huì)支持多進(jìn)程多線程處理。
bootloader種類和分類
這里的分類實(shí)際上是依據(jù)上面的bootloader的操作模式來進(jìn)行劃分的,根據(jù)一個(gè)系統(tǒng)是否支持上面的下載模式我們這里將bootloader劃分為bootloader和monitor(這不是我劃分的,恩,是從別人的文章中引述過來的,不過我覺得他說的很有道理),這里”bootloader”是指只是引導(dǎo)設(shè)備與執(zhí)行主程序的固件,而”monitor”是指不僅擁有bootloader功能的,還能夠進(jìn)入下載模式的固件。
(1)、ARM程序的組成
此處所說的“ARM程序”是指在ARM系統(tǒng)中正在執(zhí)行的程序,而非保存在ROM中的bin映像(image)文件,這一點(diǎn)清注意區(qū)別。
ZI是程序中的未初始化的變量;ZI就是zero;
(3)ARM程序的執(zhí)行過程
從以上兩點(diǎn)可以知道,燒錄到ROM中的image文件與實(shí)際運(yùn)行時(shí)的ARM程序之間并不是完全一樣的。因此就有必要了解ARM程序是如何從ROM中的image到達(dá)實(shí)際運(yùn)行狀態(tài)的。
3; ZI
(4) ROM主要指:NAND Flash,Nor Flash
對于剛學(xué)習(xí)ARM的人來說,如果分析它的啟動(dòng)代碼,往往不明白下面幾個(gè)變量的含義:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。
當(dāng)把程序編寫好以后,就要進(jìn)行編譯和鏈接了,在ADS1.2中選擇MAKE按鈕,會(huì)出現(xiàn)一個(gè)Errors and Warnings的對話框,在該欄中顯示編譯和鏈接的結(jié)果,如果沒有錯(cuò)誤,在文件的最后應(yīng)該能看到Image component sizes,后面緊跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各個(gè)項(xiàng)目的字節(jié)數(shù),最后會(huì)有他們的一個(gè)統(tǒng)計(jì)數(shù)據(jù):
Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028
Tatal RO size (Code+ RO Data) 184571 (180.25kB)
Tatal RW size(RW Data+ ZI Data) 17081(16.68 kB)
Tatal ROM size(Code+ RO Data+ RW Data) 184624(180.30 kB)
后面的字節(jié)數(shù)是根據(jù)用戶不同的程序而來的,下面就以上面的數(shù)據(jù)為例來介紹那幾個(gè)變量的計(jì)算。
在ADS的Debug Settings中有一欄是Linker/ARM Linker,在output選項(xiàng)中有一個(gè)RO base選項(xiàng),
假如RO base設(shè)置為0x0c100000,后面的RW base 設(shè)置為0x0c200000,然后在Options選項(xiàng)中有Image entry point ,是一個(gè)初始程序的入口地址,設(shè)置為0x0c100000 。
有了上面這些信息我們就可以完全知道這幾個(gè)變量是怎么來的了:
|Image$$RO$$Base| = Image entry point =RO base =0x0c100000 ;表示程序代碼存放的起始地址
|Image$$RO$$Limit|=程序代碼起始地址+代碼長度+1
= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1
= 0x0c12d0fc
|Image$$RW$$Base| = 0x0c200000=RW base 地址指定
|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53
=0x0c200037
|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038
|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028
=0x0c200038 + 0x4284
=0x0c2042bc
也可以由此計(jì)算:
|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081
=0x0c200000+0x42b9+3(要滿足4的倍數(shù))
=0x0c2042bc
評論