#############review embedded linux application############
嵌入式linux應用:
一、嵌入式綜述,基本概念:
1.GPL:串連了它的庫就要一樣GPL;
LGPL:修改了,才要LGPL;
總的來說,GPL在保護自由軟體上更加嚴格些。
2.ARM LINUX:特指在ARM平台上啟動並執行LINUX移植版本,帶MMU的;
uCLinux:一個全新的linux,而不是linux的移植版本,不帶MMU,對記憶體空間不進行保護,多個進程共用一個Runspace;
RT-Linux/RTAL:支援硬即時的linux發行版本,它的基本思想是將kernel當作一個優先順序最低的即時任務。
3.補丁檔案:
命名規則:patch-x.y.z-rmkn.gz--x.y.z是linux kernel版本號碼,n是補丁版本號碼;
pre-patch-x.y.z-rmkn.gz -- 是alpha or beta的,通常不穩定;
diff-2.4.18-rmk7-pxa3.gz -- 一看名字diff就知道了,是對打完補丁的kernel再打補丁,注意,一定要先打patch 再打 diff.
4.作業系統分層模型:
/********************************************/
User program and application libraries
^
|
C libary
^
|
kernel
/********************************************/
5 GNU = GNU'S NOT UNIX ,它要實現一個新的作業系統。
二、嵌入式LINUX開發工具:
1.GCC -- GNU Compiler Collective.的編譯參數:
-c:產生.o檔案;
-D:後面加宏名;
-E:只運行C的先行編譯器cpp.
-I:後面接包含的標頭檔所在路徑。
-L:接函數庫搜尋路徑。
-l:串連時需要串連的庫檔案名稱。
-O(n):最佳化層級;
-MM:輸出源檔案的依賴關係;
-fPIC:產生位置無關代碼,一般在產生共用庫時用。
-shared:產生支援動態共用程式庫的執行檔案;
-static:不支援動態共用程式庫,把函數庫內容靜態串連到執行檔案中,(可執行檔將很大)。
2.binutils -- 一組二進位工具集合,輔助GCC。
ar:建立歸檔檔案。e.g:做靜態庫時用。
as:GNU彙編器。 主要用來編譯GCC -S產生的彙編代碼,然後產生一個二進位代碼,存放到object檔案中。
ld:連接器,編譯的最後一步了。主要是將目標檔案和歸檔檔案串連到一起,然後重定位元據,並串連符合引用,最終形成一個可執行檔。
nm:列出目標檔案的符號。
objcopy:將目標檔案的內容複寫到另一個目標檔案,並可以改變檔案類型;
e.g: objcopy a.out -O binary a.bin
file a.out -- elf,not stripped. 在PC上跑,要有作業系統的支援。
file a.bin -- data. 可以直接在板子上裸奔。
objdump -- 彙編人的最愛:display one or complex object file,並可以用選項來控制其顯示資訊。e.g: objdump -S a.out > a.txt 看ELF檔案的彙編代碼;但是a.out已經是一個ld過的檔案了,所以彙編code 可能很多,所以我一般這樣做:
gcc -c a.c
objdump -S a.o > a.txt
readelf:顯示可執行檔的一些資訊,例如HEAD資訊。
strip:瘦身命令;
3.交叉編譯器 --Cross-Compiler
arm-linux-gcc:編譯出可以在ARM上的LINUX系統下跑的ELF檔案;
arm-elf-gcc: 編譯出在沒有MMU上的uClinux上跑的FLAT的ELF檔案;
tar xzvf arm-linux-gcc-2.95.3.tar.gz -C Dir
rpm -ivh ....rpm
卸載:rpm -e arm-linux-gcc
安裝目錄一般都在/usr/local/bin or /usr/local/arm-linux/bin下
查看安裝情況:which arm-linux-gcc
e.g:
arm-elf-gcc -elf2flt main.c -o helloworld /*產生flat的elf可執行檔*/
armm-linux-gcc main.c -o helloworld
三、啟動代碼 -- bootloader
1.硬體啟動後第一個執行的軟體程式,固化在ROM中,加電自啟動。
2.What is bootloader do?
1) 初始化 SDRAM ,建立堆棧環境,為跳轉到 C 語言代碼的入口做好準備。
2) 初始化 UART ,建立目標板和調試主機之間的通訊。
3) 實現一個基於串口輸入的命令直譯器,提供一些基本的使用者命令。
其實 bootloader 初始化的任務還有很多,但我覺得這三個任務是比較重大的。
這裡結合 lumit4510 的代碼,我給出了一個經過簡化的 bootloader ( l-boot ) ,
只包含了 startup.s 和 main.c 兩個程式,一個是彙編代碼,一個是 C 語言入口。
可以很直觀的看出,系統運行到 C 語言之前,需要經過哪些必要的初始化步驟。
ResetEntry
|
++++ SYS_RST_HANDLER
|
++++ InitSystem
|
++++ InitMemory
|
++++++++++++++++++++
|
++++ Copy ROM to RAM
|
++++ Set up SVC stack
|
++++ Remap Memory
|
++++ Goto main ( in main.c )
/*初始化硬體。
載入、執行、偵錯工具和作業系統核心。*/
3.二個階段和主要功能:
第一個階段:用彙編寫的,依賴於CPU體繫結構的代碼。
初始化硬體;
設定RAM空間,建立堆棧環境;
拷貝stage2到RAM空間;
跳轉到stage2的C入口;
第二階段:用C寫的,這樣可以實現一些複雜的功能,而且代碼會有更好的可讀性和可移植性。
1)初始化本階段常用的硬體裝置。
2)記憶體映射 -- mmap;
3)將kernel image檔案和根檔案系統image從flash讀到RAM空間;
4)為核心設定啟動參數;
5)調用核心;
bootloader工作結束,退出曆史舞台,以後就是kerner的天下了。
4.為什麼都要用Main函數當入口,因為在調用MAIN前會做這麼多事兒:
1)Copies RO and RW execution regions from their Load Addresses to their Execution Addresses.
2)Zeroes BSS regions.
3)Set up stack and heap.
4)Initialize referenced library functions.
5)Set up argc and agrv for main().
6)Calls main()
四、核心配置、編譯、剪下、移植
1.directory是kernel源檔案所在目錄
patch -p1 -d directory < ....patch
or
cat ../..patch | patch -p1 -d directory
2.配置kerner:
make menuconfig
config.in -- 添加menu中的選項;
.config -- 對menu配置完後,將配置結果儲存在這裡;
autoconf.h -- 最後,唯一真正去編譯的就是它。
3.uClinux 的核心編譯和linux核心編譯的區別,比linux多做哪幾個事:
1)make lib_only
2)make user_only
3)make romfs.img
4.ELF檔案:
1)一種為linux系統所採用的通用檔案格式,支援動態串連。
檔案開始為頭資訊,描述了各section的相關資訊。包括串連地址和執行地址。
有三種類型:
.o目標檔案;
.so共用檔案;
.elf可執行檔。
2)一個ELF檔案的可執行程式segment一般包括3個section:
.text -- 唯讀代碼區;
.data -- 可讀寫的資料區;
.bss -- 在記憶體空間裡於.data section相鄰,為可讀寫的未初始化的資料區;
FLAT格式:
對ELF的頭資訊做了簡化,一般在uCLinux上用。
arm-linux-gcc -elf2flt hello.c
BIN格式:
二進位檔案,不包含地址定位資訊;
總結:ELF,FLAT都是elf的,都要在作業系統上跑,FLAT可以用在uCLinux上,是因為uCLinux沒有MMU.
5.uCLinux:
1)核心載入方式:
可以在flash上執行,也可以在RAM中解壓核心後執行,支援位置無關代碼;
2)ROMFS作為其根檔案系統:
代碼體積減小。
不支援動態塊擦除。如果系統需要動態儲存的資料,那就要採用RAM-Disk + EXT2作檔案系統了。
3)使用了FLAT格式的ELF檔案,更節省空間的。
4)對glibc,應用程式庫都作了精簡。 對使用者程式採用靜態串連形式,代碼可固化。
6.對uCLinux源碼目錄下的images/下的檔案說明:
1)romfs.img -- 是通過tools/romfs-inst.sh產生的romfs目錄和其下的檔案,然後用genromfs打包成一個檔案romfs.img.
可以放在flash中,也可以放在sdram中,但是編譯核心時要在driver/block/blkmem.c裡指定地址,以便核心找到它。
2)linux.txt:編譯後核心的.text段,一般放在flash中,唯讀。
linux.data:編譯後核心中的.data段,一般在sdram空間,讀寫。
3)image.bin: 由上面三個檔案順序串連而產生 =linux.txt + linux.data + romfs.img.
7.uCLinux的image.rom是一個壓縮過的核心,這個檔案可以直接燒到flash。= linux產生的zImage.
image.ram -- 解壓image.rom到sdram中啟動並執行elf格式的linux核心。
可以形象的說:image.rom = gunzip program + gzipped(image.ram)
8.arm linux 編譯後產生的二種核心鏡像。
vmlinux;沒有壓縮的原始linux kernel 鏡像;elf format;pc;
zImage;壓縮後的linux kernel image; bin format; 裸奔;
五、根檔案系統 -- RootFS
1.ROMFS檔案系統:
1)相對於EXT2檔案系統更節省空間的:
kernel支援romfs需要的代碼少;
romfs建立檔案系統超級塊需要更少的空間。
2)romfs是唯讀檔案系統,所以系統同時需要虛擬盤(ramdisk)來支援臨時檔案和資料檔案的儲存。
RAM肯定要採用EXT2檔案系統。
2.JFFS2檔案系統、YAFFS檔案系統:
1)JFFS2:
主要是針對NorFlash設計的。
--(Jounaling Flash Filesystem 2)記錄檔系統。
2)YAFFS (Yet Another Flash Filesystem)
主要針對NANDFlash設計。
適用於大容量的儲存。
它們都是為嵌入式存放裝置設計的檔案系統:
支援損耗平衡,修改少量的資料時,只是附加,而不是重寫整個扇區。
提供了更好的崩潰/掉電安全保護。
3.NFS檔案系統:
/etc/init.d/nfs start | restart
modprobe nfs //?????????
mount -t nfs 192.168.0.33:/usr/src/ /mnt
4.Initrd -- 對romfs剪裁後的放在RAM中的最小的檔案系統。
製作:
1)dd if=/dev/zero of=initrd.img bs=1024 count=500 /*dd 用指定大小的塊拷貝一個檔案*/
2)mke2fs -m0 -F initrd.img /*用EXT2格式化*/
3)mkdir rom ram
4)mount initrd.img ram/ -o loop
5)mount romfs.img rom/ -o loop
6)mkdir ram/bin ram/sbin ram/dev ram/etc ram/var ram/lib ram/probe ram/usr ram/tmp
7)cp -a rom/etc/* ram/etc/
8)cp -a rom/bin/* ram/bin/
9)cp -a rom/bin/init ram/bin/init
10)mknod ram/dev/ttyS0 c 4 64 /*串口裝置*/
11)mknod ram/dev/console c 5 1
12)umount ram
umount rom
BUSYBOX -- 瑞士軍刀;
ram disk製作完成,可以用ftp方式將initrd.img下載到開發板上。有了kernel,在加上現在做得檔案系統,我們就完成了一個自己的uCLinux作業系統了,哈哈。
5.核心啟動參數:
作用:不用重新編譯核心,在啟動時改變核心行為。
具體參數可參見:Documentation/kernel-parametes.txt
常用舉例:
1) root=/dev/ram0 rw /*root=根檔案系統所在裝置,rw-讀寫方式載入根檔案系統*/
init=/linuxrc /*核心初始化末尾將啟動並執行腳步,預設值是/sbin/init*/
console=ttyS0,115200n8 console=tty0 /*啟動資訊的控制台*/
ramdisk_size=8192
cachepolicy=writethrough
2)
root=/dev/nfs ip=bootp console=ttyS0,115200n8
六、應用程式開發 -- Applications
1.arm-elf-gcc main.c -elf2flt
arm-linux-gcc main.c
2.make -c subdir /*將make command傳遞到分別的subdir下執行其有的Makefile*/