學做嵌入式Linux系統V0.1

admin @ 2014-03-25 , reply:0

我也來學做嵌入式Linux系統V0.1(完整版)

呵呵,終於搞完了,把它發到一個貼子中來,做了一些修改……其中還有一些錯誤,希望大家指正,偶好修改!!!
——————————————————————————————————
《我也來學做嵌入式Linux系統V0.1》
作者:九賤
E-mail:kendo999@sohu.com
個人站點:www.skynet.org.cn
___________________________________________________

第一章 前言
目的
本文的目的,是講述嵌入式Linux系統的建立、開發的一般過程。製作一個小型的Linux的系統,可以移植至其它硬碟、軟盤、優盤、flash rom……

關於作者
九賤,E名kendo,喜歡網路入侵技術、防火牆、入侵檢測技術及網路技術,對Linux也頗感興趣,想認識有共同愛好的朋友。最近閑暇,把一些學過的東西寫下來,總結總結,以作備忘這需。已完成的有《網路入侵檢測設計與Snort2.2源碼分析》和這篇《我也來學做嵌入式Linux》。正在進行中的有《Windows防火牆技術實現大全》和《Linux防火牆實現及源碼分析》。大家可以在CU上,或者是到我的小站www.skynet.org.cn上與我交流

做一個嵌入式Linux系統究竟要做哪些工作
做一個嵌入式Linux系統究竟需要做哪些工作?也就是本文究竟要講述哪些內容?我先介紹一個脈絡,可以做為我們後面工作的一個總的提綱:
第一步、建立交叉編譯環境
沒有交叉開發經驗的讀者,可能一時很難接受這個概念。首先,要明白兩個概念:一般我們工作的機器,稱為開發機、主機;我們製作好的系統將要放到某台機器,如手機或另一台PC機,這台機我們稱為目標主機。
我們一般開發機上已經有一套開發工具,我們稱之為原生開發套件,我們一般就是用它們來寫程序,那麼,那什麼又是交叉編譯環境呢?其實一點也不神秘,也就是在開發機上再安裝一套開發工具,這套開發工具編譯出來的程序,如內核、系統工作或者我們自己的程序,是放在目標主機上運行的。
那麼或許有初學者會問,直接用原生開發工具為目標主機編譯程序不就完了?至少我當初是這麼想的。一般來說,我們的開發機都是X86平台,原生開發套件開發的工具,也針對 X86平台,而我們的目標主機可能是PowerPC、IXP、MIPS……所以,我們的交叉編譯環境是針對某一類具體平台的。
一般來講,交叉開發環境需要二進位工具程序、編譯器、C鏈接庫,嵌入式開發常用的這三類軟體是:
Binutils
Gcc
uClibc
當然,GNU包含的工具套件不僅於此,你還要以根據實際需要,進行選擇

第二步、編譯內核
開發工具是針對某一類硬體平台,內核同樣也是。這一步,我們需要用第一步中建立的工具,對內核進行編譯,對於有內核編譯經驗的人來說,這是非常簡單的;

第三步、建立根文件系統
也就是建立我們平常看到的bin、dev、proc……這一大堆目錄,以及一些必備的文件;另外,我們還需要為我們的目標系統安裝一些常用的工具軟體,如 ls、ifconfig……當然,一個辦法是找到這些工具的源代碼,用第一步建立的交叉編譯工具來編譯,但是這些軟體一是數量多,二是某些體積較大,不適合嵌入式系統,這一步,我們一般都是用busybox來完成的,包括系統引導軟體init;
最後,我們為系統還需要建立初始化的引導文件,如inittab……

第四步、啟動系統
在這一步,我們把建立好的目標、文件、程序、內核及模塊全部拷貝到目標機存儲器上,如硬碟。然後為系統安裝bootloader,對於嵌入式系統,有許多引導程序可供我們使用。不過它們許多都有硬體平台的限制。當然,如果你是工作在X86,可以直接用lilo來引導,事實上,本文就是採用的lilo。
做到這一步,將目標存儲設備掛上目標機,如果順利,就可以啟動系統了。
當然,針對某些特別的平台,不能像硬碟這樣拷貝了,需要讀卡器、燒錄……但是基本的方法是相通的!

第五步、優化和個性化系統
通過前四步,我們已經得到了一個可以正常工作的系統。在這一步里,就是發揮你想像的時候了……


本文的工作環境
項目根目錄/home/kendo/project ------>;我將它指定至PATH:$PRJROOT
子目錄及說明
目錄 內容
bootloader 目標板的引導載入程序,如lilo等
build-tools 建立交叉編譯平台的工具源碼
debug 調試工具及所有相關包
doc 項目中用到的所有文檔
images 編譯好的內核映像,以及根文件系統
kernel 各個版本的Linux內核源碼
rootfs 製作好的根文件系統
sysapps 目標板將要用到的系統應用系統,比如thttpd,udhcpd等
tmp 存放臨時文件
tools 編譯好的跨平台開發工具鏈以及C鏈接庫


工作的腳本
#!/usr/bin

export PROJECT=skynet
export PRJROOT=/home/${PROJECT}
export TARGET=i386-linux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PREFIX}/bin:/bin:/sbin:/usr/bin:/usr/sbin

cd $PRJROOT


第二章 建立交叉編譯環境
在CU中發表的另一篇同名的貼子里,我講述了一個全手工創建交叉編譯環境的方法。目前,創建交叉編譯環境,包括建立根文件,一般來講,有兩種方法:
手功創建
可以得到最大程序的個性化定製,缺點是過程繁雜,特別是極易出錯,注意這個“極”字,包括有經驗的開發人員;
自動創建
無它,方便而。

因為前一篇文章中,已經講述了全手工創建交叉編譯環境的一般性方法,本文就不打算再重複這個步驟了,感興趣的朋友,可以再去搜索那篇貼子,提醒一點的就是,在準備工具鏈的時候,要注意各個工具版本之間的搭配、每個工具需要哪些補丁,我建議你在google上針對這兩項搜索一下,準備一個清單,否則……
本章要講述的是自動創建交叉編譯環境的方法。目標,針對商業硬體平台,廠家都會為你提供一個開發包,我用過XX廠家的IXP425和MIPS的,非常地方便,記得我第一次接觸嵌入式開發,拿著這個開發包自動化創建交叉編譯環境、編譯內核、建立根文件系統、創建Ram Disk,我反覆做了三四次,結果還不知道自己究竟做了些什麼,呵呵,夠傻吧……
所以,建議沒有這方面經驗的讀者,還是首先嘗試一下手工創建的方法吧,而本章接下來的內容,是送給曾經被它深深傷害而不想再次去親歷這項工作而又想提高交率而又在通用平台上工作沒有商業開發包的朋友。

建立交叉開發工具鏈
準備工具:
buildroot-0.9.27.tar.tar
只需要一個軟體?對,其它的不用準備了,buildroot事實上是一個腳本與補丁的集合,其它需要用到的軟體,如gcc、uClibc,你只需在buildroot中指明相應的版本,它會自動去給你下載。
事實上,buildroot到網上去下載所需的所有工作是需要時間的,除非你的帶寬足夠,否則下載軟體時間或許會佔去80%,而我在做這項工作之間,所需的工作鏈全部都在我本地硬碟上,我解壓開buildroot后,新建dl文件夾,將所有工具源碼的壓縮包拷貝進去,呵呵,buildroot就不用去網上下載了。

我的軟體清單:
Linux-libc-headers-2.4.27.tar.bz2
Gcc-3.3.4.tar.bz2
binutils 2.15.91.0.2.tar.bz2
uClibc 0.9.27.tar.bz2
genext2fs_1.3.orig.tar.gz
ccache-2.3.tar.gz

將它拷貝到${PRJROOT}/build-tools下,解壓
[root@skynet build-tools]# tar jxvf buildroot-0.9.27.tar.tar
[root@skynet build-tools]#cd buildroot
配置它:
[root@skynet build-tools]#make menuconfig
Target Architecture (i386) --->; 選擇硬體平台,我的是i386
Build options --->; 編譯選項
這個選項下重要的是(${PRJROOT}/tools) Toolchain and header file location?編譯好的工具鏈放在哪兒?
如果你像我一樣,所有工具包都在本地,不需它到網上自動下載,可以把wget command選項清空;
Toolchain Options --->; 工具鏈選項
--- Kernel Header Options 頭文件它會自動去下載,不過應該保證與你將要用的內核是同一個版本;
[] Use the daily snapshot of uClibc? 使用最近的uClibc的snapshot
Binutils Version (binutils 2.15.91.0.2) --->; Binutils的版本
GCC compiler Version (gcc 3.4.2) --->; gcc 版本
[*] Build/install c++ compiler and libstdc++?
[ ] Build/install java compiler and libgcj? 支持的語言,我沒有選擇java
[ ] Enable ccache support? 啟用ccache的支持,它用於編譯時頭文件的緩存處理,用它來編譯程序,第一次會有點慢,但是以後的速度可就很理想了,呵呵……
--- Gdb Options 根據你的需要,選擇gdb的支持

Package Selection for the target --->;
這一項我沒有選擇任意一項,因為我打算根文件系統及busybox 等工具鏈創建成工,手工來做。
Target Options --->; 文件系統類型,根據實際需要選,我用的ext2;

配置完成後,編譯它:
[root@skynet build-tools]#make

這一項工作是非常花時間的,我的工具包全部在本地,也花去我一小時十三分的時間,如果全要下載,我估計網速正常也要多花一兩個鐘頭。

經過漫長的等待(事實上並不漫長,去打了幾把遊戲,很快過去了):
……
make[1]: Leaving directory `/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3'
touch -c /home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs
#-@find /home/skynet/build-tools/buildroot/build_i386/root/lib -type f -name \*.so\* | xargs /home/skynet/tools/bin/i386-linux-uclibc-strip --remove-section=.comment --remove-section=.note --strip-unneeded 2>;/dev/null || true;
/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs -i 503 -b 1056 \
-d /home/skynet/build-tools/buildroot/build_i386/root -q -D target/default/device_table.txt /home/skynet/build-tools/buildroot/root_fs_i386.ext2

大功告成!!!

清點戰利品
讓我來看看它究竟做了哪些事情吧:
[root@skynet skynet]# cd tools
[root@skynet tools]# ls
bin bin-ccache i386-linux i386-linux-uclibc include info lib libex!ec man usr

bin:所有的編譯工具,如gcc,都在這兒了,只是加了些指定的前綴;
bin-ccache:如果在Toolchain optaion中沒有選擇對ccache的支持,就沒有這一項了;
i386-linux:鏈接文件;實際指向include
i386-linux-uclibc:uclibc的相關工具;
include:供交叉開發工具使用的頭文件;
info:gcc 的info文件;
lib:供交叉開發工具使用的鏈接庫文件;
……

現在可以把編譯工具所在目錄XXX/bin添加至PATH了

測試工具鏈
如果你現在寫一個程序,用i386-linux-gcc來編譯,運行的程序會告訴你:
./test: linked against GNU libc
因為程序運行庫會尋到默認的/lib:/usr/lib上面去,而我們目前的uclibc的庫並不在那裡(雖然對於目標機來講,這是沒有錯的),所以,也只能暫時靜態編譯,試試它能否工作了。當然,你也可以在建好根文件系統后,試試用chroot……

第三章 編譯內核
本章的工作,是為目標機建立一個合適的內核,對於建立內核,我想有兩點值得考慮的:
1、功能上的選擇,應該能夠滿足需要的情況下,盡量地小;
2、小不是最終目的,穩定才是;

所以,最好編譯內核前有一份目標機硬體平台清單以及所需功能清單,這樣,才能更合理地裁減內核。

準備工具
Linux內核源碼,我選用的是Linux-2.4.27.tar.bz2

編譯內核
將Linux-2.4.27.tar.bz2拷貝至${PRJROOT}/kernel,解壓
#cd linux-2.4.27
//配置
# make ARCH=i386 CROSS_COMPILE=i386-linux- menuconfig
//建立源碼的依存關係
# make ARCH=i386 CROSS_COMPILE=i386-linux- clean dep
//建立內核映像
# make ARCH=i386 CROSS_COMPILE=i386-linux- bzImage
ARCH指明了硬體平台,CROSS_COMPILE指明了這是交叉編譯,且編譯器的名稱為i386-linux-XXX,這裡沒有為編譯器指明路徑,是因為我前面已將其加入至環境變數PATH。

又是一個漫長的等待……
OK,編譯完成,673K,稍微大了點,要移到其它平台,或許得想辦法做到512以下才好,回頭來想辦法做這個工作。

安裝內核
內核編譯好后,將內核及配置文件拷貝至${PRJROOT}/images下。
# cp arch/i386/boot/bzImage ${PRJROOT}/images/bzImage-2.4.27-rmk5
# cp vmlinux ${PRJROOT}/images/vmlinux-2.4.27-rmk5
# cp System.map ${PRJROOT}/images/System-2.4.27-rmk5
# cp .config ${PRJROOT}/images/2.4.27-rmk5

我採用了後綴名的方式重命名,以便管理多個不同版本的內核,當然,你也可以不用這樣,單獨為每個版本的內核在images下新建對應文件夾也是可行的。

安裝內核模塊
完整內核的編譯后,剩下的工作就是建立及安裝模塊了,因為我的內核並沒有選擇模塊的支持(這樣擴展性差了一點,但是對於我的系統來說,功能基本上定死了,這樣影響也不太大),所以,剩下的步驟也省去了,如果你還需要模塊的支持,應該:
//建立模塊
#make ARCH=i386 CROSS_COMPILE=i386-linux- modules
//安裝內核模塊至${PRJROOT}/images
#make ARCH=i386 CROSS_COMPILE= i386-linux- \
>;INSTALL_MOD_PATH=${PRJROOT}/images/modules-2.4.18-rmk5 \
>;modules_install

最後一步是為模塊建立依存關係,不能使用原生的depmod來建立,而需要使用交叉編譯工具。需要用到busybox中的depmod.pl腳本,很可惜,我在busybox1.0.0中,並沒有找到這個腳本,所以,還是借用了busybox0.63中scripts中的depmod.pl。
將depmod.pl拷貝至${PREFIX}/bin目錄中,也就是交叉編譯工具鏈的bin目錄。
#depmod.pl \
>;-k ./vmlinux –F ./System.map \
>;-b ${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules >; \
>;${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules/2.4.27-rmk5/modules.dep

註:後面討論移植內核和模塊內容時,我只會提到內核的拷貝,因為我的系統並沒有模塊的支持。如果你需要使用模塊,只需按相同方法將其拷貝至相應目錄即可。

附,內核編譯清單
附,內核選擇:
內核編譯記錄:
Code maturity level options 不選
Loadable module support 不選
Processor type and features 根據實際,選擇處理器類型
General setup --->;
[*] Networking support
[*] PCI support
(Any) PCI access mode
[*] PCI device name database
[*] System V IPC
[*] Sysctl support
(ELF) Kernel core (/proc/kcore) format
[*] Kernel support for ELF binaries
[*] Power Management support

Memory Technology Devices (MTD) --->; MTD設備,我用CF卡,不選

Parallel port support --->; 不選
Plug and Play configuration --->; 我的系統用不著即插即用,不選

Block devices --->;
[*] Loopback device support
[*] RAM disk support
(4096) Default RAM disk size (NEW)
[*] Initial RAM disk (initrd) support

Multi-device support (RAID and LVM) --->; 不選

Networking options --->; 基本上都選了

ATA/IDE/MFM/RLL support --->; 用了默認的

Telephony Support --->; 不選

SCSI support --->; 不選

Fusion MPT device support --->; 不選

I2O device support --->; 不選

Network device support --->; 根據實際情況選擇

Amateur Radio support --->; 不選

IrDA (infrared) support --->; 不選

ISDN subsystem --->; 不選

Old CD-ROM drivers (not SCSI, not IDE) --->; 不選

Input core support --->; 不選

Character devices --->;
[*] Virtual terminal
[*] Support for console on virtual terminal
[*] Standard/generic (8250/16550 and compatible UARTs) serial support
[*] Support for console on serial port

Multimedia devices --->; 不選

File systems --->;
[*] Kernel automounter version 4 support (also supports v3)
[*] Virtual memory file system support (former shm fs)
[*] /proc file system support
[*] Second extended fs support

Console drivers --->;
[*] VGA text console 調試時接顯示器用

剩下三個都不要
Sound --->;
USB support --->;
Kernel hacking --->;

第四章 建立根文件系統
1、建立目錄
構建工作空間時,rootfs文件夾用來存放根文件系統,
#cd rootfs
根據根文件系統的基本結構,建立各個對應的目錄:
# mkdir bin dev etc lib proc sbin tmp usr var root home
# chmod 1777 tmp
# mkdir usr/bin usr/lib usr/sbin
# ls
dev etc lib proc sbin tmp usr var
# mkdir var/lib var/lock var/log var/run var/tmp
# chmod 1777 var/tmp

對於單用戶系統來說,root和home並不是必須的。
準備好根文件系統的骨架后,把前面建立的文件安裝到對應的目錄中去。
2、拷貝鏈接庫
把uclibc的庫文件拷貝到剛才建立的lib文件夾中:
# cd ${PREFIX}/lib
[root@skynet lib]# cp *-*.so ${PRJROOT}/rootfs/lib
[root@skynet lib]# cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib

3、 拷貝內核映像和內核模塊
因為沒有模塊,所以拷貝模塊就省了,
新建boot目錄,把剛才建立好的內核拷貝過來
# cd /home/kendo/control-project/daq-module/rootfs/
# mkdir boot
# cd ${PRJROOT}/images
# cp bzImages-2.4.18-rmk5 /home/kendo/control-project/daq-module/rootfs/boot

4、 建立/dev下邊的設備文件
在linux中,所有的的設備文件都存放在/dev中,使用mknod命令創建基本的設備文件。
mknod命令需要root許可權,不過偶本身就是用的root用戶,本來是新建了一個用戶專門用於嵌入式製作的,不過後來忘記用了……
# mknod -m 600 mem c 1 1
# mknod -m 666 null c 1 3
# mknod -m 666 zero c 1 5
# mknod -m 644 random c 1 8
# mknod -m 600 tty0 c 4 0
# mknod -m 600 tty1 c 4 1
# mknod -m 600 ttyS0 c 4 64
# mknod -m 666 tty c 5 0
# mknod -m 600 console c 5 1
基本的設備文件建立好后,再創建必要的符號鏈接:
# ln -s /proc/self/fd fd
# ln -s fd/0 stdin
# ln -s fd/1 stdout
# ln -s fd/2 stderr
# ls
console fd mem null random stderr stdin stdout tty tty0 tty1 ttyS0 zero

設備文件也可以不用手動創建,聽說RedHat /dev下的腳本MAKEDEV 可以實現這一功能,不過沒有試過……

基本上差不多了,不過打算用硬碟/CF卡來做存儲設備,還需要為它們建立相關文件,因為我的CF在目標機器上是CF-to-IDE,可以把它們等同來對待,先看看Redhat 下邊had的相關屬性:
# ls -l /dev/hda
brw-rw---- 1 root disk 3, 0 Jan 30 2003 /dev/hda
# ls -l /dev/hda1
brw-rw---- 1 root disk 3, 1 Jan 30 2003 /dev/hda1
對比一下,可以看出,had類型是b,即塊設備,主編號為3,次編號從0遞增,根限位是
rw-rw----,即660,所以:
# mknod -m 660 hda b 3 0
# mknod -m 660 hda1 b 3 1
# mknod -m 660 hda2 b 3 2
# mknod -m 660 hda3 b 3 3


5、添加基本的應用程序
未來系統的應用程序,基本上可以分為三類:
 基本系統工具,如ls、ifconfig這些……
 一些服務程序,管理工具,如WEB、Telnet……
 自己開發的應用程序

這裡先添加基本的系統工具,有想過把這些工具的代碼下載下來交叉編譯,不過實在是麻煩,用BusyBox,又精簡又好用……
將busybox-1.00.tar.gz下載至sysapps目錄下,解壓:
#tar zxvf busybox-1.00.tar.gz
#cd busybox-1.00
//進入配置菜單
#make TARGET_ARCH=i386 CROSS=i386-linux- PREFIX=${PRJROOT}/rootfs menuconfig
//建立依存關係
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs dep
//編譯
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs
//安裝
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs install

# cd ${PRJROOT}/rootfs/bin
# ls
addgroup busybox chown delgroup echo kill ls mv ping rm sleep
adduser chgrp cp deluser grep ln mkdir netstat ps rmdir umount
ash chmod date dmesg hostname login mount pidof pwd sh vi
一下子多了這麼多命令……
配置busybox的說明:
A、如果編譯時選擇了:
Runtime SUID/SGID configuration via /etc/busybox.conf
系統每次運行命令時,都會出現“Using fallback suid method ”
可以將它去掉,不過我還是在/etc為其建了一個文件busybox.conf搞定;
B、 [*] Do you want to build BusyBox with a Cross Compiler? (i386-linux-gcc) Cross Compiler prefix
這個指明交叉編譯器名稱(其實在編譯時的命令行已指定過了……)
C、安裝選項下的(${PRJROOT}/rootfs) BusyBox installation prefix,這個指明了編譯好后的工具的安裝目錄。
D、靜態編譯好還是動態編譯好?即是否選擇
[ ] Build BusyBox as a static binary (no shared libs)
動態編譯的最大好處是節省了寶貴空間,一般來說都是用動態編譯,不過我以前動態編譯出過問題(其實是庫的問題,不關busybox的事),出於慣性,我選擇了靜態編譯,為此多付出了107KB的空間。
E、其它命令,根據需要,自行權衡。


6、系統初始化文件
內核啟動時,最後一個初始化動作就是啟動init程序,當然,大多數發行套件的Linux都使用了與System V init相仿的init,可以在網上下載System V init套件,下載下來交叉編譯。另外,我也找到一篇寫得非常不錯的講解如何編寫初始化文件的文件,bsd-init,回頭附在後面。不過,對於嵌入式系統來講,BusyBox init可能更為合適,在第6步中選擇命令的時候,應該把init編譯進去。
#cd ${PRJROOT}/rootfs/etc
#vi inittab
我的inittal文件如下:
#指定初始化文件
::sysinit:/etc/init.d/rcS
#打開一個串口,波特率為9600
::respawn:/sbin/getty 9600 ttyS0
#啟動時執行的shell
::respawn:/bin/sh
#重啟時動作
::restart:/sbin/init
#關機時動作,卸載所有文件系統
::shutdown:/bin/umount -a –r

保存退出;

再來編寫rcS腳本:
#mkdir ${PRJROOT}/rootfs/etc/init.d
#cd ${PRJROOT}/rootfs/etc/init.d
#vi rcS
我的腳本如下:
#!/bin/sh

#Set Path
PATH=/sbin:/bin
export PATH

syslogd -m 60
klogd

#install /proc
mount -n -t proc none /proc

#reinstall root file system by read/write mode(need: /etc/fstab)
mount -n -o remount,rw /

#reinstall /proc
mount -n -o remount,rw -t proc none /proc

#set lo ip address
ifconfig lo 127.0.0.1

#set eth0 ip address
#當然,這樣子做只是權宜之計,最後做的應該是在這一步引導網路啟動腳本,像RedHat
#那樣,自動讀取所有指定的配置文件來啟動
ifconfig eth0 192.168.0.68 netmask 255.255.255.0

#set route
#同樣的,最終這裡應該是運行啟動路由的腳本,讀取路由配置文件
route add default gw 192.168.0.1

#還差一個運行服務程序的腳本,哪位有現成的么?
#網卡/路由/服務這三步,事實上可以合在一步,在rcS這一步中,做一個循環,運行指定啟動目錄下的所有腳,先將就著這麼做吧,確保系統能夠正常啟動了,再來寫這個腳本。

#set hostname
hostname MyLinux

保存退出。

編寫fstab文件
#vi fstab
我的fstab很簡單:
/dev/hda1 / ext2 defaults 1 1
none /proc proc defaults 0 0

第五章 讓MyLinux能夠啟動
前一章,我們把編譯好的內核、應用程序、配置文件都拷貝至rootfs目錄對應的子目錄中去了,這一步,就是把這些文件移植至目標機的存儲器。這裡,我是先另外拿一塊硬碟,掛在我的開發機上做的測試,因為我的本本用來寫文檔,PC機用來做開發機,已經沒有另外的機器了……但是本章只是講述一個一般性的過程,並不影響你直接在目標主機上的工作。
因為以後目標機識別硬碟序號都是hda,而我現在直接掛上去,則會是hdb、hdc……這樣,安裝lilo時有點麻煩(雖然也可以實現)。所以我想了另一個辦法:
 把新硬碟掛在IDE0的primary上,進入linux后,會被認為是had;
 原來主機的裝Redhat的硬碟,我將它從IDE0的primary上變到了IDE1 的primary,因為它的lilo早已裝好,基本上不影響系統的使用;

分區和格式化
BIOS中改為從第二個硬碟啟動;也就是從我原來開發機啟動,新的硬碟被識別成了had。
#fdisk /dev/hda
用d參數刪除已存在的所有分區
用n參數新建一個分區,也是就/dev/hda1
格式化
#mkfs.ext2 /dev/hda1

安裝bootloader
因為我是X86平台,所以直接用了lilo,如果你是其這平台,當然,有許多優秀的bootloader供你選擇,你只需查看其相應的說明就可以了。
編譯lilo配置文件,我的配置文件名為target.lilo.conf,置於${PRJROOT}/rootfs/etc目錄。內容如下所示:
boot=/dev/hda
disk=/dev/hda
bios=0x80
image=/boot/bzImage-2.4.18-rmk5
label=Linux
root=/dev/hda1
append="root=/dev/hda1"
read-only

//新建文件夾,為mount做新準備
#mkdir /mnt/cf
//把目標硬碟mount上來
#mount –t ext2 /dev/hdc1 /mnt/cf
回到rootfs
#cd ${PRJROOT}/rootfs
拷貝所有文件至目標硬碟
#cp –r * /mnt/cf

這樣,我們所有的文件都被安裝至目標硬碟了,當然,它還不能引導,因為沒有bootloader。使用如下命令:
# lilo -r /mnt/cf -C etc/target.lilo.conf
Warning: LBA32 addressing assumed
Added Linux *
-r :改變根目標為/mnt/cf ,這樣配置文件其實就是/mnt/cf/etc/target.lilo.conf,也就是我們先前建立的文件。
當然,完成這一步,需要lilo22.3及以後版本,如果你的版本太舊,比如Redhat9.0自帶的,就會出現下面的信息:
#lilo –r /mnt/cf –C etc/target.lilo.conf
Fatal: open /boot/boot.b: No such file or directory
這時,你需要升級你的lilo,或者重新安裝一個。

啟動系統
#umount /mnt/cf
#reboot

將BIOS改為從IDE0啟動,也就是目標硬碟。如果一切順利,你將順利進入一個屬於你的系統。

回頭再來看看我們的工作空間吧
[root@skynet lib]# df /dev/hda1
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/hda1 3953036 1628 3750600 1% /mnt/cf

總共花去了我1628KB的空間,看來是沒有辦法放到軟盤裡邊去了^o^,不過一味求小,並不是我的目標。

[root@skynet skynet]# ls ${PRJROOT}
bootloader build-tools debug doc images kernel rootfs sysapps tmp tools
這幾個目錄中的文件,呵呵,與本文一開頭規劃的一樣

[root@skynet skynet]# ls build-tools/
buildroot buildroot-0.9.27.tar.tar
包含了buildroot源碼及壓縮包,事實上buildroot下邊還包括了GNU其它工具的源碼、編譯文件等諸多內容,是我們最重要的一個文件夾,不過到現在它已經沒有多大用處了,如果你喜歡,可以將它刪除掉(不建議)。

[root@skynet skynet]# ls images
2.4.18-rmk5 bzImage-2.4.18-rmk5 System-2.4.18-rmk5 vmlinux-2.4.18-rmk5
內核映像及配置文件等,如果你有模塊,因為還有相應的目錄

[root@skynet skynet]# ls kernel/
linux-2.4.27 linux-2.4.27.tar.bz2
內核源碼及壓縮包

[root@skynet skynet]# ls rootfs/
bin boot dev etc home lib linuxrc proc root sbin tmp usr var
製作好的根文件系統,重中之重,注意備份……

[root@skynet skynet]# ls sysapps/
busybox-1.00 busybox-1.00.tar.gz
busybox-1.00源碼包,或許你還要繼續添加/刪除一些命令……

[root@skynet skynet]# ls tools
bin i386-linux i386-linux-uclibc include info lib man
這個也很重要,我們製作好的交叉開發工具鏈。如果你要繼續開發程序,這個目錄重要性就很高了。

其它目錄暫時是空的。


第六章 完善MyLinux
關於進一步的調試,你可以在開發機上使用chroot /mnt/cf /bin/sh這樣的命令,以使我們在目標根文件系統上工作。

支持多用戶
因為我在編譯busybox時,已經將它的多用戶那一大堆命令編譯了進來。現在關鍵是的要為其建立相應的文件;
進入原來的開發機,進入rootfs目錄,切換根目錄
#chroot rootfs/ /bin/sh
A、 建立/etc/passwd文件,我的文件內容如下:
root:x:0:0:root:/root:/bin/bash
B、 建立/etc/group文件,我的文件內容如下:
root:x:0:
bin:x:1:
sys:x:2:
kmem:x:3:
tty:x:4:
tape:x:5:
daemon:x:6:
disk:x:7:
C、 為root建立密碼
#passwd root

試試用addgroup/addusr……這堆命令。然後重啟,從目標硬碟上啟動;從console口,9600登陸試試(因為我在inittab中啟用了ttyS0,我未來的目標機,是沒有顯卡的,需要從console口或SSH進去管理)
MyLinux login: root
Password:

BusyBox v1.00 (2004.10.10-04:43+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ #
成功了……

增加WEB Server
Busybox裡邊有httpd選項,不過我編譯時並沒有選擇,所以還是自己來安裝。我使用的軟體是thttpd-2.25b.tar.gz,將它移至sysapps目錄下。
[root@skynet sysapps]# tar zxvf thttpd-2.25b.tar.gz
[root@skynet sysapps]# cd thttpd-2.25b
//配置
[root@skynet thttpd-2.25b]# CC=i386-linux-gcc ./configure --host=$TARGET
……
i386-linux-gcc -static htpasswd.o -o htpasswd -lcrypt
make[1]: warning: Clock skew detected. Your build may be incomplete.
make[1]: Leaving directory `/home/skynet/sysapps/thttpd-2.25b/extras'
//拷貝至根文件目錄
[root@skynet thttpd-2.25b]# cp thttpd ${PRJROOT}/rootfs/usr/sbin
//trip處理
[root@skynet thttpd-2.25b]# i386-linux-strip ${PRJROOT}/rootfs/usr/sbin/thttpd

剩下的,就發揮各人的想像吧……


獨孤九賤 2005-11-1 02:34

繼續補完

不好意思,最近工作太忙,好久沒有來搞這個東東了,加之心愛的手機丟了,心情又不太好,不過一切都過去了,繼續來將它補充完整。

修改啟動腳本
在前面寫rcS啟動腳本中。當時只是為了系統能夠正常地啟動,在啟動網卡/路由/服務等時,有如下語句:
——————————————————————————————————————————————
#set lo ip address
ifconfig lo 127.0.0.1

#set eth0 ip address
#當然,這樣子做只是權宜之計,最後做的應該是在這一步引導網路啟動腳本,像RedHat
#那樣,自動讀取所有指定的配置文件來啟動
ifconfig eth0 192.168.0.68 netmask 255.255.255.0

#set route
#同樣的,最終這裡應該是運行啟動路由的腳本,讀取路由配置文件
route add default gw 192.168.0.1
……
———————————————————————————————————————————————
這樣配置的最大壞處就是不能根據配置文件自定義,每次開機都定死了,現在來修改它,將這段語句刪除之,換成如下語句:
for i in /etc/start/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue

echo "Running $i ."
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
echo "Done $i ."
echo
done
解釋一下,這段語句的作用,就是啟動/etc/start/目錄下,所有以S開頭的腳本文件,可以啟動兩類,以sh結尾或沒有 sh後綴的。
這樣,我們在/etc/目錄下再新建一目錄start/,這裡面就是我們啟動時需要的腳本的。先來啟動網卡。

修改網卡配置文件
我是根據Red hat的作法,把網卡配置放在/etc/sysconfig/network-scripts目錄下,類似於ifcfg-ethXX這樣子,它們的語法是:
DEVICE=eth0
BOOTPROTO=static
BROADCAST=88.88.88.255
IPADDR=88.88.88.44
NETMASK=255.255.255.0
NETWORK=88.88.88.0
ONBOOT=yes
好,建立這些目錄和文件,我共有兩個文件ifcfg-ethXX。回到/etc/start目錄,建立網卡的啟動腳本S01interface:
#!/bin/sh

. /etc/sysconfig/network
#enable ip_forword
echo >1 /proc/sys/net/ipv4/ip_forward

#enable syn_cookie
echo >1 /proc/sys/net/ipv4/tcp_syncookies

#enable loopback interface
/sbin/ifconfig lo 127.0.0.1

#eanble ethernet interface
/usr/sbin/bootife

#set hostname
if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]
then
HOSTNAME=localhost
fi

/bin/hostname ${HOSTNAME}

請注意這個腳本文件,有兩個地方:
1、包含了另一個配置文件/etc/sysconfig/network,在這裡,我也是照抄了redhat,我的/etc/sysconfig/network這個文件的內容如下:
NETWORKING=yes
HOSTNAME=skynet
GATEWAY=88.88.88.2
2、在啟動網卡時,我使用了
#eanble ethernet interface
/usr/sbin/bootife
bootife 是我自己寫的一個C程序,作用是讀取/etc/sysconfig/network-scripts/下面的ifcfg-ethXX文件,並配置之,本來這裡就該用shell來完成更合適一點,無奈,偶shell功底實在差了一點(剛學幾天),就是想從Redhat中照抄過來,反覆試了幾次也沒有成功。所以被逼無奈用C來完成之,後面我會附上我的C的源碼,也希望哪位大哥能夠寫一段Shell的程序代替它,放上來大家共享之。

修改路由啟動文件
同樣的,我在/etc/start下,建立新腳本S02route.sh,它的作用是啟動所有配置的靜態路由:
#!/bin/bash

. /etc/sysconfig/network
# Add non interface-specific static-routes.

if [ -f /etc/sysconfig/static-routes ]
then
grep "eth*" /etc/sysconfig/static-routes | while read ignore args ; do
# echo "/sbin/route add -"$args
/sbin/route add -$args
done
fi

#Add defalut gw
/sbin/route add default gw ${GATEWAY}

OK,啟動時讀取的配置文件是/etc/sysconfig/static-routes ,它的語法和Redhat是一樣的,請參照建立此文件。

啟動服務程序
新建啟動腳本S03server:
#!/bin/sh
#------------------------------------------------------------------
#-- Source
#-- Author(s) : kendo
#-- Email: [email]kendo999@sohu.com[/email]
#-- http://www.skynet.org.cn
#-- 2005/10/31
#------------------------------------------------------------------

. /etc/sysconfig/bootserver

if [ "$enable_httpd" = 1 ] ; then
. /etc/scripts/httpd.sh $1
fi

if [ "$enable_adsl" = 1 ] ; then
……
fi

if [ "$enable_udhcpd" = 1 ] ; then
……
fi

很簡單,根據相應變數的值,調用相應的腳本。
1、這些啟動標誌變數,我定義在了/etc/sysconfig/bootserver當中,其內容如下:
#start server on system boot
#1:yes 0:no
enable_httpd=1
enable_adsl=1
enable_udhcpd=1

2、每種服務對應的腳本,我都放在了/etc/scripts下面。這些腳本,取決於你打算使用哪些服務程序了。腳本的來源,可以自己編寫,有可能其源碼中自帶有,也可以到網上查找……我就不再一一贅述了,

OK,基本上,腳本的修改就完成了,下一步,將是建立RamDisk。

——————————————————————————————————————————————
附,讀取網卡配置文件,啟動網卡的C源碼:
/************************************************************************
** author:kendo
** date:2005/10/26

***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

#define NETCFGDIR "/etc/sysconfig/network-scripts/"

struct _ifcfg{
char device[8];
char bootproto[8];
char br[16];
char netmask[16];
char ip[16];
char network[16];
int onboot;
};

void ParseKey(struct _ifcfg *ifcfg,char *key,char *value)
{
if(!strcmp(key,"DEVICE"))
{
strcpy(ifcfg->device,value);
}
else if(!strcmp(key,"BOOTPROTO"))
{
strcpy(ifcfg->bootproto,value);
}
else if(!strcmp(key,"BROADCAST"))
{
strcpy(ifcfg->br,value);
}
else if(!strcmp(key,"IPADDR"))
{
strcpy(ifcfg->ip,value);
}
else if(!strcmp(key,"NETMASK"))
{
strcpy(ifcfg->netmask,value);
}
else if(!strcmp(key,"NETWORK"))
{
strcpy(ifcfg->network,value);
}
else if(!strcmp(key,"ONBOOT"))
{
ifcfg->onboot=(strcmp(value,"yes") ? 0 : 1);
}
}

int main(int argc,char **argv)
{
FILE *fp;
DIR *dir;
int i;
char filename[50],buf[80];
char *index,*key,*value,*p;
struct _ifcfg *ifcfg;
struct dirent *ptr;

ifcfg=(struct _ifcfg *)malloc(sizeof(struct _ifcfg));
memset(ifcfg,0,sizeof(struct _ifcfg));

dir=opendir(NETCFGDIR); /*打開腳本目錄*/
while((ptr=readdir(dir))!=NULL) /*讀取所有文件*/
{
if(strncmp(ptr->d_name,"ifcfg-eth",9)) /*這裡,只啟動了乙太網卡^o^*/
{
continue;
}
memset(filename,0,sizeof(filename));
sprintf(filename,"%s%s",NETCFGDIR,ptr->d_name);
if((fp=fopen(filename,"r"))==NULL) /*打開配置文件*/
{
continue;
}

while(!feof(fp))
{
memset(buf,0,sizeof(buf));
if(fgets(buf,80,fp)!=NULL) /*逐行讀取分析*/
{
p=strchr(buf,'n');
if(p)
{
*p='';
}
index=buf;
key=strtok(index,"="); /*讀取配置變數*/
value=strtok(NULL,"="); /*讀取變數的值*/
ParseKey(ifcfg,key,value); /*分析之,存入結構ifcfg中*/
}
}
/*構建相應的命令*/
memset(buf,0,80);
strcpy(buf,"/sbin/ifconfig");

if(ifcfg->onboot)
{
sprintf(buf,"%s %s %s netmask %s broadcast %s",
buf,
ifcfg->device,
ifcfg->ip,
ifcfg->netmask,
ifcfg->br);
/*直接調用system來實現,當然也可以自己通過ioctl來設置,相應源碼,我以前在c/c++版發過*/
system(buf);
}
}
free(ife);
return 0;
}

platinum 2005-11-1 02:52

[code]
memset(buf,0,80);
strcpy(buf,"/sbin/ifconfig");

if(ifcfg->onboot)
{
sprintf(buf,"%s %s %s netmask %s broadcast %s",
buf,
ifcfg->device,
ifcfg->ip,
ifcfg->netmask,
ifcfg->br);
/*直接調用system來實現,當然也可以自己通過ioctl來設置,相應源碼,我以前在c/c++版發過*/
system(buf);
[/code]
兩個問題
1、從這段代碼看,實際調用了 /sbin/ifconfig 來完成網卡的設置,那麼,這個程序是否必須用 root 來執行?
2、為何不用 system("command") 呢?

獨孤九賤 2005-11-1 03:03

回復 19樓 platinum 的帖子

1、 ifconfig本身運行,應該不需要root吧?而至於在shell中的運行許可權,要看看相應的許可權位了,事實上它已經能夠在我的系統中很好的運行了,測試過很多次的。不過現在我的系統,其實沒有用這種方法的,我是自己封裝了一個網卡管理的庫,也就是重寫了ifconfig,不過要把這些代碼發上來,太麻煩了,所以,就用了解system簡單了一點。

2、我不是很理解“為何不用 system("command") 呢?”這句話的含義,我用的是system(buf);你說的是不是為什麼要去構建一個buf,而不是直接用system("/sbin/ifconfig ethXX……")?清楚一點……^o^


獨孤九賤 2005-11-16 01:44

繼續工作,交叉編譯SNMP

一般系統都會有SNMP的支持,下載了net-snmp-5.1.3.1,先看看INSTALL和FAQ文檔(因為以前從來沒有碰過這個東東,見笑了……),按照說明,在原生主機上安裝了一回,安裝完成後,發現在指定安裝目錄下主要包括了幾塊文件:
bin:SNMP的一些功能腳本和程序;
sbin:主要的代理程序和trap程序:snmpd和snmptrap
include/lib:自身兼容及第三方開發所需的頭文件及庫文件;
share:主要是MIB文件;

然後回到安裝目錄下,運行./configuare --help,仔細查看了其安裝編譯選項,因為我定位的小型的系統,只需具備基本的SNMP功能即可,所以:
那些bin目錄下的功能程序也不需要,對應--disable-applications
bin下的腳本也是不需要的,對應:--disable-scripts
用戶手冊也不需要:--disable-manuals
關閉ipv6支持:--disable-ipv6
還有一個--enable-mini-agent選項,說明是編譯出一個最小化的snmpd,比較有趣,試試先。
對於交叉編譯,還需要用--host指明目標平台。

OK,看完了幫助說明,開始編譯了:
1、配置,根據以上確定的選項:
[root@skynet root]# CC=i386-linux-gcc ./configure --host=$TARGET --enable-mini-agent --disable-ipv6 --with-endianness=little --disable-applications --disable-manuals --disable-scripts --disable-ucd-snmp-compatibility

CC指明了編譯器;--host指明了我的目標平台,這個環境變數在我前面定義的devedaq腳本中。
還算順利,繼續編譯它:
[root@skynet net-snmp-5.1.3.1]# make LDFLAGS="-static"

呵呵,因為沒有裝lib庫,所以我用了-static選項,指明是靜態編譯;

3、安裝
安裝就需要指明安裝路徑了,路徑可以在.config的時候指定,因為那個時候,那串東東太長了,我在install時指定也不遲:
#make prefix=${TARGET_PREFIX} exec_prefix=${TARGET_PREFIX} install

4、檢查一下:
[root@skynet net-snmp-5.1.3.1]# ls -l ${TARGET_PREFIX}/sbin
total 2120
-rwxr-xr-x 1 root root 2164301 Nov 16 09:22 snmpd

snmpd就是我們要的代理主程序了,大約靜態編譯有2M。

[root@skynet net-snmp-5.1.3.1]# ls -l ${TARGET_PREFIX}/bin
total 4380
-rwxr-xr-x 2 root root 391980 Oct 14 2004 ar
-rwxr-xr-x 2 root root 581228 Oct 14 2004 as
……

呵呵,那堆程序和腳本沒有安裝,如snmpwalk……

ls ${TARGET_PREFIX}/lib
ls -l ${TARGET_PREFIX}/include


看看我們需要的mib文件:
[root@skynet net-snmp-5.1.3.1]# ls ${TARGET_PREFIX}/share/snmp
mib2c.access_functions.conf mib2c.column_defines.conf mib2c.int_watch.conf mib2c.old-api.conf
mib2c.array-user.conf mib2c.column_enums.conf mib2c.iterate_access.conf mib2c.scalar.conf
mib2c.check_values.conf mib2c.conf mib2c.iterate.conf mibs
mib2c.check_values_local.conf mib2c.create-dataset.conf mib2c.notify.conf snmpconf-data

5、移植
基本完成了,因為snmpd太大了點,對它進行strip處理:
先備個份:
[root@skynet net-snmp-5.1.3.1]# cp ${TARGET_PREFIX}/sbin/snmpd ${TARGET_PREFIX}/sbin/snmpd.bak
[root@skynet net-snmp-5.1.3.1]# i386-linux-strip ${TARGET_PREFIX}/sbin/snmpd
[root@skynet net-snmp-5.1.3.1]# ls -l ${TARGET_PREFIX}/sbin/snmpd
-rwxr-xr-x 1 root root 503300 Nov 16 09:30 /home/skynet/tools/i386-linux/sbin/snmpd

經過處理后,還有近500KB了。

因為只有SNMP agent功能,即snmpd程序,其它的都可以忽略。用了靜態編譯,lib下邊那些libnetsnmp文件都可以不需要了,程序運行
需要MIB庫,也就是share下的內容,把這兩個東東拷到rootfs相應的目錄中去:
[root@skynet net-snmp-5.1.3.1]# cp ${TARGET_PREFIX}/sbin/snmpd ${PRJROOT}/rootfs/usr/sbin
[root@skynet net-snmp-5.1.3.1]# mkdir -p ${PRJROOT}/rootfs/usr/local/share
[root@skynet net-snmp-5.1.3.1]# cp -r ${TARGET_PREFIX}/share/snmp ${PRJROOT}/rootfs/usr/local/share
[root@skynet net-snmp-5.1.3.1]# cp EXAMPLE.conf ${PRJROOT}/rootfs/usr/local/share/snmp/snmpd.conf

最後一步是把安裝目錄下的配置文件範例拷到snmpd啟動時默認的搜索目錄中去。

6、測試
打開snmpd.conf看看:
[root@skynet net-snmp-5.1.3.1]# vi ${PRJROOT}/rootfs/usr/local/share/snmp/snmpd.conf
有如下語句:
# sec.name source community
com2sec local localhost COMMUNITY
com2sec mynetwork NETWORK/24 COMMUNITY
定義了兩個用戶,本地及網路的,以及它們的通讀密鑰,按自己的需要修改一下,如:
# sec.name source community
com2sec local 127.0.0.1 public
com2sec mynetwork 0.0.0.0 public

後面是定義用戶的用戶組等一大堆東東,事實上不用修改它們了。運行它:
[root@skynet net-snmp-5.1.3.1]# chroot ${PRJROOT}/rootfs /bin/sh


BusyBox v1.00 (2004.10.13-06:32+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

/ # snmpd
/ # exit
在我們自己的根文件系統環境下運行它,然後退出來。用ps查看:
#ps -aux
……
root 32270 0.0 0.3 1212 936 ? S 09:38 0:00 snmpd

[root@skynet net-snmp-5.1.3.1]# netstat -anu
……
udp 0 0 0.0.0.0:161 0.0.0.0:*

呵呵,已經成功啟動了。用一個SNMP管理軟體試試,可以成功地獲取到信息。OK!

總結一下:
1、主程序+MIB庫大了點,共計約2M,不過我確實沒有辦法再小了,而且一味求小,也不是我的目的。
2、功能稍微簡單了些,只有agent,如果需要,可以類似地把其它程序加上去就可以了。
3、第一次玩net-snmp,還是有點生疏,比如我靜態編譯二進位程序,並不需要include/lib下的文件,但是如何關閉它們呢?我試過--disable-ucd-snmp-compatibility,不過好像不是這個選項……下次改進了……

獨孤九賤 2005-11-16 05:38

繼續工作,使用ramdisk

前提:內核編譯時得選相應的支持選項,前文已有敘述。

1、rootfs中的/boot文件夾刪除;
2、建立ramdisk:
使用dd命令建立一個空的文件系統映像:
# dd if=/dev/zero of=images/initrd.img bs=1k count=8192

大小8192K,用/dev/zero對其初始化;

利用剛才的空的文件系統映像,建立文件系統並安裝它,使用了mke2fs命令:
# /sbin/mke2fs -F -v -m0 images/initrd.img

新建一個臨時文件夾做mount之用:
# mkdir tmp/initrd
把建好的文件系統mount上來:
#mount -o loop images/initrd.img tmp/initrd
把根文件系統拷貝過來:
#cp -av rootfs/* tmp/initrd
# umount tmp/initrd

壓縮:
# gzip -9 <images/initrd.img > images/initrd.bin

這樣,就得到了images/initrd.bin

把目標盤mount上來:
#mount -t ext2 /dev/hda1 /mnt/cf
新建一個/boot
#mkdir /mnt/cf/boot

把剛才建立的ramdisk鏡像拷過來。然後把內核文件bzImage-2.4.27-rmk5也拷進去。
這樣,boot文件夾裡邊有兩個文件
initrd.bin
bzImage-2.4.27-rmk5

這個時候還不能安裝lilo,因為lilo的配置文件中有/dev/hda……這樣的東東,而目標盤上還沒有……所以,臨時建一個:
#mkdir /mnt/cf/dev
#cp -rf ${PRJROOT}/rootfs/dev/hda* /mnt/cf/dev

修改${PRJROOT}/rootfs/etc/target.lilo.conf,我的配置文件如下:
boot=/dev/hda
disk=/dev/hda
bios=0x80

image=/boot/bzImage-2.4.27-rmk5
initrd=/boot/initrd.bin
root=/dev/hda1
append="root=/dev/hda1"
# label=MyLinux
read-only

相比以前的,只是加了一句:initrd=/boot/initrd.bin,另外把label去掉了,因為否則lilo會報怨說語法錯誤。

好了,可以安裝lilo了。以前我們的語句是:
lilo -r /mnt/cf -C etc/target.lilo.conf
現在我們的目標盤上沒有etc這個目錄了,更不用說target.lilo.conf,可以藉助於工程目錄中的了原文件,當然,我在目標硬碟上新建了/dev,然後把target.lilo.conf拷過去,還是用這句命令安裝lilo。

這樣,整個系統就完成了。

總結:還是想把kernel與文件系統都做到initrd.bin中,不過現在還沒有找到可行的辦法,大家也可以跟貼討論如何來做,偶也好學學……




[admin via 研發互助社區 ] 學做嵌入式Linux系統V0.1已經有3416次圍觀

http://cocdig.com/docs/show-post-42921.html