星期五, 6月 08, 2007

親手打造 Floppy Linux 環境

--------------------------------------------------------------------------
標題:[OS Project-1] 親手打造 Floppy Linux 環境
作者:黃敬群,國立成功大學資訊工程系
文件版次:0.1
最後修訂日期:Nov 14, 2001
版權聲明:允許在保留作者出處及本聲明之前提下,以任何人類可讀之形式自由散佈
--------------------------------------------------------------------------

[OS Project-1] 親手打造 Floppy Linux 環境

黃敬群

■ 前言

撰寫本文的目的,就是希望能夠將筆者自行製作的 Floppy-Linux Distro (目前版本為
0.2pre2) 是如何一步一步建構出來,透過書面紀錄的方式,從最簡單的「開機片」,
逐漸修改製作方式、植入若干工具,到討論改進方式等體驗「親手打造」的過程,對需
要製作包含常用工具之 Rescue Disk 者,或是有心從事 embedded Linux 者,如此的建
構過程或許能提供些幫助。本文以 [如何製作簡易 Floppy Liunx] 一文為主軸,輔以各
種延伸功能 (如網路、editor 等) 進行擴充。因才疏學淺之故,諸多謬誤在所難免,還
請不吝指正。


■ 讀者背景

1. 對 UNIX 指令稍有涉獵
2. 曾經親手編譯過 Linux Kernel


■ 開機過程簡述

尚未提及如可製作 Floppy Linux 前,首先要瞭解整個系統開機的過程,為了普及性,
這邊以最容易取得的 PC 個人電腦討論對象,處理器是 Intel 80486 以上等級。

當啟動電源之際,電腦的 BIOS 就會找尋有無系統開機磁片,此時會有兩種狀況產生:

第一種狀況:找到系統開機磁片 --

此時就會從系統開機磁片中的第 0 磁區、第 0 磁柱載入可開機磁區

另一種狀況:找不到系統開機磁片 --

BIOS 就會找尋硬碟的 MBR (Master Boot Record),並且執行記錄在 MBR 上
的開機載入程式 (Boot Loader) 進行開機

無論是從軟碟開機也好,還是從硬碟開機也好,首先 OS Loader (就 Linux 來講就可
能是 LILO -- LInux LOader) 會載入 Linux Kernel,而 Kernel 一旦起動後,第一件
事就是切入保護模式 (protected mode),此時,所有的硬體交由 Kernel 來控制,也
意味掙脫 BIOS 的羈絆。

當 Kernel 載入完畢後,便開始初始化系統所有硬體設備。而當所有的硬體初始化的動
作也告完成之際,系統將嘗試掛載 (mount) root filesystem。Root filesystem 就是
被掛上當作 "/" 目錄的 filesystem (以下簡稱 fs),當然,如果無法正確 mount 上
root fs,巧婦難為無米之炊的 Linux Kernel 也只好秀個訊息:

VFS: Unable to mount root fs on XXX

然後就會停止運作 (halt),這邊的 XXX 是指那種 fs,這部份的訊息,可以參考 kernel
source 中 fs/super.c 的程式碼。

當 root filesystem 成功的掛載後,就會去執行 init 這個程式。

init 會檢查 /etc/inittab,找出該檔中標明 sysinit 這一行,並執行該 script,在
Redhat Linux 上為 rc.sysinit,於是 rc.sysinit 肩負系統的初始化的大任,不外乎
有以下任務:

. 呼叫 /sbin/initlog 紀錄系統初始化過程
. 設定 path、hostname 等資訊
. banner 畫面:一般我們看到 "Welcome to RedHat Linux" 的訊息
. Mount /proc
. Load system font
. Configure kernel parameters
. Set the system clock
. Load keymap
. Start up swapping、turn on swap
. Remount the root filesystem read-write、Clear mtab
. Finding module dependencies、Load modules
. Check filesystems

當 rc.sysinit 執行完畢,控制權立即移轉回 init 手中,進入預設 runlevel:

. 若內定的 runlevel 為 3:

init 執行 /sbin/mingetty 啟動 virtual console,並且以 "login:" 提示讓使
用者登入,以完成開機。登入後系統會提供一個 shell 給使用者,就可以使用
Linux 。

. 若 runlevel 為 5:

在開啟 virtual console 後,init 會再執行 xdm 啟動 X window system,讓使
用者以 xdm 界面登入。

以上就是 Linux 開機的過程。


■ 起點:開機片

Floppy Linux 的製作方式相當多,在不訴諸商業 Embedded Linux Toolkit (如
BlueCat 等) 的情況下,可由 Linux Distribution 內提供製作 Boot/Rescure Disk 的
script 下手。筆者以手頭這台安裝 Mandrake Linux 8.0的例子來說,就是
/sbin/mkbootdisk,請以 root 身份執行:

# /sbin/mkbootdisk

可看到使用方式:

usage: mkbootdisk [--version] [--noprompt] [--mkinitrdargs ]
[--appendargs ] [--device ]
[--verbose -v] [--compact]
(ex: mkbootdisk --device /dev/fd1 2.0.31)

依據指示 [Press to continue],不消多久就建構出一個 Floppy Linux,咱們
來看看這張 floppy 有什麼內容:

# mount -t ext2 /dev/fd0 /mnt/floppy/
# tree /mnt/floppy/

/mnt/floppy/
-- boot
-- boot.021C
-- boot.b
-- map
`-- message ========> LILO 啟動後秀出的訊息
-- dev
-- fd0H1440
`-- hda6
-- etc
`-- lilo.conf =========> lilo 組態設定
-- initrd.img =========> initrd image
-- lost+found
`-- vmlinuz-2.4.3-20mdk =========> Linux Kernel

回顧 [開機過程簡述] 一節所提,開機片的啟動過程如下:

BIOS --> LILO --> Kernel --> init --> linuxrc --> mount Hard Disk
(root fs)

mount 上 Hard Disk (筆者的系統是 hda6) 後,控制權就交給硬碟中的 Linux,這片
開機片也功成身退。

接著,讓我們一步接一步的探究,首先對 lilo 開刀:

# cat /mnt/floppy/etc/lilo.conf

boot=/dev/fd0H1440
timeout=100
message=/boot/message
prompt
image=/vmlinuz-2.4.3-20mdk
label=linux
root=/dev/hda6
append=""
initrd=/initrd.img

initrd.img 立刻吸引我們的目光,這是由 mkinitrd 工具程式所建構。以 Mandrake
Distribution 內附的 Linux Kernel (vmlinuz-2.4.3-20mdk) 來說,算是「一般化的
kernel」,裡頭並不包含任何 SCSI 卡、CD-ROM,或是其他的驅動程式,而在必要時,
才以 module 方式載入上述驅動程式,如此的需求,mkinitrd 就因應而生,mkinitrd
會自動載入 /etc/conf.modules 檔案裡的的設定項目,如此一來,便能方便地讓
kernel 管理 module 形式的驅動程式。

initrd 可以在載入核心前就先載入 RAM disk,也可載入壓縮過的檔案系統檔,如果您
留意 Linux Distribution 安裝的過程中,有 [Rescue] 的選項,就是提供 rescue.gz
這個壓縮過的檔案系統檔 (可以在安裝光碟中找到),裡面包含些工具程式。

我們可以由此當作出發點,稍微修改,使開機片脫離 Hard Disk 的牽連:大體上,只
要想辦法讓 kernel 能載入 rescue.gz 進 RAMDisk 再 mount 成 root fs 就行了。根
據 initrc 的文件, 要在 initrd 的 /linuxrc 內寫入這個程序

首先,修改開機片的 lilo.conf 檔,指示 kernel 把 RAMDisk 當 root,並以 Read-
write 模式掛上(不然 kernel 不會 re-mount)

# cat /mnt/floppy/etc/lilo.conf

boot=/dev/fd0H1440
message=./boot/message
delay=0
initrd=rescue.gz =======> 第一處修改:initrd
image=vmlinuz =======> 換成光碟中的 Kernel,這樣整個系統才
放得下)
label=linux
ramdisk=49152 =======> 指定 RAMDisk 大小
root=/dev/ram =======> 把 root fs 指定為 /dev/ram

迫不及待想來測試吧,實驗結果可以發現,的確可以載入 rescue.gz,也執行了
/linuxrc,但卻無法 mount root fs,why?

原因很簡單,initrd 其實並非在 /dev/ram 之上,在它出現時並沒有載入核心, 所以
根本沒有 device file,更別說要 mount 了,如何解決呢?其實不難,善用 mkfs、
cp 等指令,並且寫進 /linuxrc 中:

# cat /mnt/floppy/linuxrc

#!/bin/sh
echo "Making RAM Disk..."
/sbin/mkfs.minix /dev/ram 1440 =======> 採用 Minix 的 fs,可以增加使用容量
echo "Duplicating disk..."
/bin/mount -t minix /dev/ram /mnt
/bin/mkdir /mnt/mnt
/bin/cp -afx bin boot cdrom dev etc lib proc root sbin tmp usr var /mnt
/bin/umount /mnt

注意到上面,我們採用 Minix fs,由於 Minix fs 設計上比 ext2 簡易許多,相對來說
,所佔的空間就比採用 ext2 fs 小多了,但是,為了符合此需求,Kernel 必須重新編
譯,在 [File System] 項目要加選 [Minix File System]。

就這樣,一片不折不扣的 Floppy Linux 於是生焉,啟動後,就可以利用原本 Rescue
Disk 上的工具,進行操作,不過呢,此時的我們,頂多動到 OS Loader 以及 Booting
程序,其他如 root fs 的建構還是沒有太大的斬獲。

在下一個小節,我們要來實作 Floppy Linux 其他項目。


■ 進化:簡易版 Floppy Linux

在此必須說明本節的目標:製作的簡易版 Floppy Linux,是一個功能非常精簡的 Linux
,只支援軟碟、並不支援硬碟及光碟,沒有網路功能,可以執行簡單的 shell 程式及一
些常用的工具程式。

接下來,你應該準備一些發展 Floopy Linux 的 source package 及工具程式有:

. Linux Kernel source
. BuzyBox [匯集常用 UNIX 指令於單一執行檔的工具集]
. syslinux [Linux Kernel Loader,可以讀取 FAT 及 Ext2 fs]

於是,這個 Floppy Linux 的開機過程與上述開機過程稍有不同:將 LILO 更換成
syslinux、開機完之後直接提供一個 shell 給使用者用,其完整開機方式如下:

BIOS --> Syslinux --> Kernel --> init --> shell

首先,必須針對我們的 Floppy Linux 來量身製作它所屬 Kernel,最好也把所需的
driver make 進核心中,如下:

# make xconfig
# make dep
# make bzImage

Kernel 建構完畢後,我們還需要一些常用的工具程式,兼具便利與低容量使用,
Busybox 是個不錯的選擇。

在此對這個大名鼎鼎 BusyBox 簡單的介紹一下:BusyBox 它包含了七十多種 Linux 上
標準的工具程式,只需要的磁碟空間僅僅幾百 k (視所選擇工具程式的數目來決定大小
),在嵌入式 系統上常用到它 (例如 Linux Router Project 和 Debian boot floppy
就使用到它),可在

http://busybox.lineo.com

找到參考資料及下載,同時,Busybox 是由 Lineo, Inc 這家嵌入式系統廠商所贊助的
Open Source tool。

而,Busybox 為何能夠佔有如此小的容量,卻能提供為數不少的常用工具呢?這訣竅在
於 busybox 在編譯後,雖然整體只是一個執行檔,卻可以透過 symbolic link 的方式
,將常用指令 (Busybox 的術語是 "applet") 「連」到 busybox 這個執行檔上,如:

# tree bin/
.
-- ash
-- busybox <===========================\ -- cat -> busybox ----------------
-- chgrp -> busybox ----------------
-- chmod -> busybox ----------------
-- chown -> busybox ----------------
-- cp -> busybox ----------------
-- date -> busybox
-- df -> busybox
-- dmesg -> busybox
-- echo -> busybox
-- false -> busybox
-- fdflush -> busybox
-- grep -> busybox

[後略]

如我們所見,cat、chgrp、chown、... 都被 symbolic link 到 busybox,那,要如何
運作呢?

咱們翻開 busybox 的 source 片段來看:(busybox.c)

int main(int argc, char **argv)
{
const char *s;
for ( s = applet_name = argv[0]; *s != '\0'; ) {
if ( *s++ == '/' )
applet_name = s; /* 取得 applet 名稱 */
}

run_applet_by_name( applet_name, argc, argv ); /* applet 進入點 */
error_msg_and_die( "applet not found" );
}

想必您一定會說:「啊哈,原來就是這麼一回事」,複習一下 C 語言,main(int argc,
char **argv) 的引數中,argv[0] 是什麼呢?就是「執行時期的名稱」,換言之,就是
cp、cat、chown 等等,確定使用者所下的指令後,就丟給 run_applet_by_name() 這個
執行引擎去跑,示意圖如下:

something(我們的指令) ---------\
cp
buzybox <----------------------/ / /---mv 1. 辨識 applet 名稱 2. 丟進 run_applet_by_name() 去跑 ===> +-mkdir


透過功能共用的方式,不僅可以共享相似的副程式 (function),更減少重複 link 的負
擔,於是乎,不需要替每個指令都製造新的執行檔,只需要在 busybox 中撰寫 applet
的功能即可,大幅縮小空間佔有量。

嗯,接著趕緊建立出一個靜態連結的 BusyBox (Floppy Linux 這個例子用到了 init、
ls、cp、cat、mount、umount、more、ps、sh),之所以要編譯成靜態連結的原因,就是
不希望 Floppy Linux 使用到 glibc 而增加磁碟的使用空間。

以下為建立 BusyBox 的步驟:

# tar zxvf busybox-0.51.tar.gz
修改 Makefile 中的 DOSTATIC 參數,從 false 改為 true
# make

到現在為止,我們已經有了 Liunx kernel 及一些常用的工具程式,似乎還少了 root
filesysem,所以進入打造迷你的 root fs 的步驟。在開始建造 root fs 之前,必須
成為 super uesr 也就是 root,因為接下來必須要用到 mknod 不得不為 root。

首先,為 root fs 建一個目錄叫做 floppy-linux,然後進入 floppy-linux 目錄內:

# mkdir floppy-linux
# cd floppy-linux

再來為 root filesystem 建立一些標準的目錄:

# mkdir dev etc etc/rc.d bin proc mnt tmp var
# chmod 755 dev etc etc/rc.d bin mnt tmp var
# chmod 555 proc
# ln -s sbin bin

進入 /dev 目錄下建立一般終端機設備:

# cd dev
# mknod tty c 5 0
# mkdir console c 5 1
# chmod 666 tty console

接著建立 VGA Display 虛擬終端機設備:

# mknod tty0 c 4 0
# chmod 666 tty0

再建立 RAM disk 設備:

# mknod ram0 b 1 0
# chmod 600 ram

建立 floppy 設備:

# mknod fd0 b 2 0
# chmod 600 fd0

最後在建立 null 設備:

# mknod null c 1 3
# chmod 666 null

擁有 fs 的框架後,現在開始編輯有關的 shell srcipt。我們先從 /etc/inittab 這
一支 script 下手,因為我們用的是 BusyBox 上的 init,與一般所使用的 init 不太
一樣,會先執行 /etc/init.d/rcS 而非 /etc/rc.d/rc.sysinit,為了做出來的 Floppy
Linux 架構與 Redhat 的架構一樣,所以修改了 BusyBox 中的 init.c 。底下是修到的
部分內容:

#ifndef INIT_SCRIPT
#define INIT_SRCIPT "/etc/rc.d/rc.sysinit"
#endif

請進入到 /floppy-linux/etc/rc.d 這個目錄下編輯 inittab ,內容如下:

::sysinit:/etc/rc.d/rc.sysinit
::askfirst:/bin/sh

修改 inittab 的權限:

# chmod 644 inittab

編輯好 rc.sysinit 之後,緊接著就是編輯 /floppy-linux/etc/rc.d 底下的
rc.sysinit,其內容如下:

#!/bin/sh
mount -a

變更其權限:

# chmod 755 rc.sysinit

再來在編輯 /floppy-linux/etc/ 底下的 fstab , fstab 內容如下:

proc /proc proc defaults 0 0

修改 fstab 權限:

# chmod 644 fstab

完成上述編輯之後,就要把靜態連結版的 BusyBox 搬到 /floppy-linux/bin 下,並做出
所需要的工具程式的連結符號,其步驟為下:

# cd /floppy-linux/bin
# cp /busybox-0.51/busybox ./init
# ln -s init ls
# ln -s init cp
# ln -s init mount
# ln -s init umount
# ln -s init more
# ln -s init ps
# ln -s init sh

到這裡為止,可以說 floppy-linux 的 root fs 已經製作完畢,不過,一般來說,我們
會採取 RAM Disk 的方式實現。為什麼呢?從前面 [起點:開機片] 的經驗可以得知,
如果我們採取 ramdisk 的方式,而非實體 (physical) 存在於儲存媒體 (當然這裡是指
floppy) 中,那麼,我們甚至可以進一步壓縮這個 ramdisk image,以便放置更多附加
功能。如此一來,整個系統的規劃就變成:

------------------ \
Kernel Loader
------------------
Linux Kernel +-- floppy image
------------------
透過 Kernel Loader 的協助,傳遞給 Kernel
RAM Disk Image ===================================\ 變成

------------------ /


/ \
-- bin
-- boot
-- dev
-- etc
-- home
-- lib +=================================
-- mnt
-- proc Kernel mount 上 ramdisk,成為 root fs
-- root
-- sbin
-- tmp
-- usr
`-- var /

不過呢,這樣的設計雖然多了不少彈性,卻也付出些代價:必須騰出兩倍於 floppy 容
量的記憶體,用來置放解壓縮後的 RAM Disk。當然,就 PC 來說,動輒幾百麥加 (喔,
是 "Mega" 的意思) 的記憶體容量早已司空見慣,但是考量到日後的平台如 PDA、手機
,甚至更小的嵌入式裝置,不得不僅慎行之。

現在,我們就來製作 ram disk ,其方法如下:

# dd if=/dev/zero of=/tmp/tmp_loop bs=1k count=2048
# losetup /dev/loop0 /tmp/tmp_loop
# mke2fs -m 0 /dev/loop0
# mount -t ext2 /dev/loop0 /mnt
# cp -a /floppy-linux /mnt
# umount /mnt
# losetup -d /dev/loop0
# dd if=/tmp/tmp_loop gzip -9 > /tmp/Image.gz
# rm -f /tmp/tmp_loop
# sync

此時,可以在 /tmp 底下發現 Image.gz 這個檔案,這就是 ram disk 影像檔,當然啦
,假如經常修改 root fs 的內容,大可將上述步驟寫成一支 shell srcipt,省下無謂
的時間浪費。

到目前為止,我們已經完成製作 floppy-linux 的準備,接下來,就將我們努力的成果
擺入 floppy。我們的 floppy-linux 的 loader 為 syslinux ,所以先要準備好
syslinux。首先,將空白的磁片格式化,然後載入 sysliunux,步驟如下:

# mkdosfs /dev/fd0
# syslinux /dev/fd0

完成上述步驟之後,請編輯 syslinux 的組態檔 syslinux.cfg,其 syslinux.cfg 內容
如下:

TIMEOUT 20
DEFAULT linux
LABEL linux
KERNEL linux
APPEND root=/dev/ram0 initrd=Image.gz

然後將 syslinux.cfg、kernel、Image.gz 拷貝到磁片中:

# mount -t msdos /dev/fd0 /mnt
# cp /usr/src/linux/arch/i386/boot/zImage /mnt/linux
# cp /tmp/Image.gz /mnt
# cp syslinux.cfg /mnt

嗯,大功告成!此時你會發現到我們的 Floppy Linux 的大小竟然只有幾百 k 呢,還不
賴,初試啼聲就有此表現 (據卡內基訓練的說法,適時給自己一點鼓勵,可以激發更多
的成就),現在可以測試親手打造的 Floopy Linux 唷。

■ 反省:進步的機會

剛剛提到 BusyBox 這個相當著名的解決方案,以及其背後的贊助公司:Lineo,事實上
,Lineo 在 Embedded Linux 界提供不少 Open Source 的高品質產品,可以參考網頁:

http://opensource.lineo.com/

我們將會注意到 [Project List] 下方的幾個項目:

. BusyBox
. TinyLogin
. uClibc
. udhcp

除了第一項 BusyBox 已經提過外,其他軟體專案計畫,我們會將逐一探討,並且試圖整
合進入咱們的 Floppy Linux 中。

首先看到 uClibc (發音:micro-controller lib-c) 這個專案:

* uClibc is a C library for embedded systems. You can actually
statically link a "Hello World" application under x86 that only
takes 4k (as opposed to 200k under GNU libc). Some features are
still a work in progress, but for many things, this is a very
sensible choice.

URL> http://uclibc.org/uClibc.html

簡單來說,在 UNIX 環境,C 語言幾乎成為母語,而一般的 Linux Distribution 中,C
Library 大多是 GNU 的實作 -- glibc,但是隨著 i18n 的廣泛支援、security 的加強
、對 POSIX 的相容性、... 等等歷史因素,glibc 已經龐大以麥加 (mega) 來計算了。
相對來說,embedded system 幾乎都是「量身打造」、「具體而微」,上述 glibc 相當
不適合,尤其版本越新,包袱越重

■ 疑難排除

Q: 您所提及的工具程式,在我的 Linux 上沒找到,怎麼辦?
A:
Linux 最棒的特色就是從 Kernel、Library,到上面的 Application 都可以修改,
您大可自己抓 source 慢慢編,或是到 http://rpmfind.net/ 找所需要的 RPM 來
裝。例如,前面提及的 genromfs 這個工具,如果恰好並未預先安置於 Linux 系統
中,利用 Rpmfind 的 search 功能,就可以找到為數不少的套件,選擇適合者安裝
即可

-----------------------
補充﹕

對了,我忘記說明,文章第一段提到這個 Floppy-Linux 發展到 0.2 pre,卻
沒有附上該 image,實在是我的過錯,所以我補上該 link:


http://ccns.ncku.edu.tw/~jimchyun/floppy/flinux.tgz

之前還加入些 fancy 的效果,快照如以下:


http://ccns.ncku.edu.tw/~jimchyun/floppy/screenshot/floppy1.jpg


http://ccns.ncku.edu.tw/~jimchyun/floppy/screenshot/floppy2.jpg

事實上,我在我的 O.S. 專題還有花上若干篇幅介紹 udhcp、uClibc、uCLinux、
syslinux、busybox、TinyLogin、... 等,不過因為那部份是我 partner 寫的,
剛剛找了一段時間,發現我們的報告已經不見,否則應該可以連貫起來的。

星期三, 6月 06, 2007

嵌入式設備上的 Linux 系統開發

http://www-900.ibm.com/developerWorks/cn/linux/embed/embdev/index.shtml

嵌入式設備上的 Linux 系統開發
出於好玩和獲利目的修補 PDA
Anand K Santhanam(asanthan@in.ibm.com),軟體工程師,IBM Global Services
Vishal Kulkarni(kvishal@in.ibm.com),軟體工程師,IBM Global Services

2002 年 3 月
如果您剛接觸嵌入式開發,那�l大量可用的引導裝載程式(bootloader)、規模縮小的分發版(distribution)、文件系統和 GUI 看起來可能太多了。但是這些豐富的選項實際上是一種恩賜,允許您調整開發或用戶環境以完全符合您的需要。對 Linux 嵌入式開發的概述將幫助您理解所有這些選項。
Linux 正在嵌入式開發領域穩步發展。因�d Linux 使用 GPL(請參閱本文後面的參考資料),所以任何對將 Linux 定制於 PDA、掌上機或者可佩帶設備感興趣的人都可以從因特網免費下載其內核和應用程式,並開始移植或開發。許多 Linux 改良品種迎合了嵌入式/即時市場。它們包括 RTLinux(即時 Linux)、uclinux(用於非 MMU 設備的 Linux)、Montavista Linux(用於 ARM、MIPS、PPC 的 Linux 分發版)、ARM-Linux(ARM 上的 Linux)和其他 Linux 系統(請參閱參考資料以鏈結到本文中提到的這些和其他術語及�{品。)
嵌入式 Linux 開發大致涉及三個層次:引導裝載程式、Linux 內核和圖形用戶介面(或稱 GUI)。在本文中,我們將集中討論涉及這三層的一些基本概念;深入瞭解引導裝載程式、內核和文件系統是如何交互的;並將研究可用於文件系統、GUI 和引導裝載程式的�萓h選項中的一部分。
引導裝載程式
引導裝載程式通常是在任何硬體上執行的第一段代碼。在象臺式機這樣的常規系統中,通常將引導裝載程式裝入主引導記錄(Master Boot Record,(MBR))中,或者裝入 Linux 駐留的磁片的第一個磁區中。通常,在臺式機或其他系統上,BIOS 將控制移交給引導裝載程式。這就提出了一個有趣的問題:誰將引導裝載程式裝入(在大多數情況中)沒有 BIOS 的嵌入式設備上呢?
解決這個問題有兩種常規技術:專用軟體和微小的引導代碼(tiny bootcode)。
專用軟體可以直接與遠端系統上的快閃記憶體設備進行交互並將引導裝載程式安裝在快閃記憶體的給定位置中。快閃記憶體設備是與存儲設備功能類似的特殊晶片,而且它們能持久存儲資訊 — 即,在重新引導時不會擦除其內容。
這個軟體使用目標(在嵌入式開發中,嵌入式設備通常被稱�d目標)上的 JTAG 埠,它是用於執行外部輸入(通常來自主機機器)的指令的介面。JFlash-linux 是一種用於直接寫快閃記憶體的流行工具。它支援�d數�萓h的快閃記憶體晶片;它在主機機器(通常是 i386 機器 — 本文中我們把一台 i386 機器稱�d主機)上執行並通過 JTAG 介面使用平行埠訪問目標的快閃記憶體晶片。當然,這意味著目標需要有一個平行介面使它能與主機通信。Jflash-linux 在 Linux 和 Windows 版本中都可使用,可以在命令行中用以下命令�妍吤式G

Jflash-linux
某些種類的嵌入式設備具有微小的引導代碼 — 根據幾個位元組的指令 — 它將初始化一些 DRAM 設置並�坏峊媦苳W的一個串列(或者 USB,或者乙太網)埠與主機程式通信。然後,主機程式或裝入程式可以使用這個連接將引導裝載程式傳送到目標上,並將它寫入快閃記憶體。
在安裝它並給予其控制後,這個引導裝載程式執行下列各類功能:
· 初始化 CPU 速度
· 初始化記憶體,包括�坏帡O憶體庫、初始化記憶體配置寄存器等
· 初始化序列埠(如果在目標上有的話)
· �坏峆�令/資料快取記憶體
· 設置堆疊指標
· 設置參數區域並構造參數結構和標記(這是重要的一步,因�d內核在標識根設備、頁面大小、記憶體大小以及更多內容時要使用引導參數)
· 執行 POST(加電自檢)來標識存在的設備並報告任何問題
· �d電源管理提供挂起/恢復支援
· 跳轉到內核的開始

帶有引導裝載程式、參數結構、內核和文件系統的系統典型記憶體佈局可能如下所示:
清單 1. 典型記憶體佈局
/* Top Of Memory */ Bootloader Parameter Area Kernel Filesystem /* End Of Memory */
嵌入式設備上一些流行的並可免費使用的 Linux 引導裝載程式有 Blob、Redboot 和 Bootldr(請參閱參考資料獲得鏈結)。所有這些引導裝載程式都用於基於 ARM 設備上的 Linux,並需要 Jflash-linux 工具用於安裝。
一旦將引導裝載程式安裝到目標的快閃記憶體中,它就會執行我們上面提到的所有初始化工作。然後,它準備接收來自主機的內核和文件系統。一旦裝入了內核,引導裝載程式就將控制轉給內核。
設置工具鏈
設置工具鏈在主機機器上創建一個用於編譯將在目標上運行的內核和應用程式的構建環境 — 這是因�d目標硬體可能沒有與主機相容的二進位執行級別。
工具鏈由一套用於編譯、彙編和鏈結內核及應用程式的元件組成。 這些元件包括:
· Binutils — 用於操作二進位文件的實用程式集合。它們包括諸如 ar、as、objdump、objcopy 這樣的實用程式。
· Gcc — GNU C 編譯器。
· Glibc — 所有用戶應用程式都將鏈結到的 C 庫。避免使用任何 C 庫函數的內核和其他應用程式可以在沒有該庫的情況下進行編譯。

構建工具鏈建立了一個交叉編譯器環境。本地編譯器編譯與本機同類的處理器的指令。交叉編譯器運行在某一種處理器上,卻可以編譯另一種處理器的指令。重頭設置交叉編譯器工具鏈可不是一項簡單的任務:它包括下載源代碼、修補補丁、配置、編譯、設置頭文件、安裝以及很多很多的操作。另外,這樣一個徹底的構建過程對記憶體和硬碟的需求是巨大的。如果沒有足夠的記憶體和硬碟空間,那�l在構建階段由於相關性、配置或頭文件設置等問題會突然冒出許多問題。
因此能夠從因特網上獲得已預編譯的二進位文件是一件好事(但不太好的一點是,目前它們大多數只限於基於 ARM 的系統,但遲早會改變的)。一些比較流行的已預編譯的工具鏈包括那些來自 Compaq(Familiar Linux )、LART(LART Linux)和 Embedian(基於 Debian 但與它無關)的工具鏈 — 所有這些工具鏈都用於基於 ARM 的平臺。
內核設置
Linux 社區正積極地�d新硬體添加功能部件和支援、在內核中修正錯誤並且及時地進行常規改進。這導致大約每 6 個月(或 6 個月不到)就有一個穩定的 Linux 樹的新發行版。不同的維護者維護針對特定體系結構的不同內核樹和補丁。當�d一個專案選擇了一個內核時,您需要評估最新發行版的穩定性如何、它是否符合專案要求和硬體平臺、從編程角度來看它的舒適程度以及其他難以確定的方面。還有一點也非常重要:找到需要應用於基本內核的所有補丁,以便�d特定的體系結構調整內核。
內核佈局
內核佈局分�d特定於體系結構的部分和與體系結構無關的部分。內核中特定於體系結構的部分首先執行,設置硬體寄存器、配置記憶體映射、執行特定於體系結構的初始化,然後將控制轉給內核中與體系結構無關的部分。系統的其餘部分在這第二個階段期間進行初始化。內核樹下的目錄 arch/ 由不同的子目錄組成,每個子目錄用於一個不同的體系結構(MIPS、ARM、i386、SPARC、PPC 等)。每一個這樣的子目錄都包含 kernel/ 和 mm/ 子目錄,它們包含特定於體系結構的代碼來完成象初始化記憶體、設置 IRQ、�坏峓眹�記憶體、設置內核頁面表等操作。一旦裝入內核並給予其控制,就首先調用這些函數,然後初始化系統的其餘部分。
根據可用的系統資源和引導裝載程式的功能,內核可以編譯成 vmlinux、Image 或 zImage。vmlinux 和 zImage 之間的主要區別在於 vmlinux 是實際的(未壓縮的)可執行文件,而 zImage 是或多或少包含相同資訊的自解壓壓縮文件 — 只是壓縮它以處理(通常是 Intel 強制的)640 KB 引導時間的限制。有關所有這些的權威性解釋,請參閱 Linux Magazine 的文章“Kernel Configuration: dealing with the unexpected”(請參閱參考資料)。
內核鏈結和裝入
一旦�d目標系統編譯了內核後,通過使用引導裝載程式(它已經被裝入到目標的快閃記憶體中),內核就被裝入到目標系統的記憶體(在 DRAM 中或者在快閃記憶體中)。通過使用串列、USB 或乙太網埠,引導裝載程式與主機通信以將內核傳送到目標的快閃記憶體或 DRAM 中。在將內核完全裝入目標後,引導裝載程式將控制傳遞給裝入內核的位址。
內核可執行文件由許多鏈結在一起的物件文件組成。物件文件有許多節,如文本、資料、init 資料、bass 等等。這些物件文件都是由一個稱�d鏈結器腳本的文件鏈結並裝入的。這個鏈結器腳本的功能是將輸入物件文件的各節映射到輸出文件中;換句話說,它將所有輸入物件文件都鏈結到單一的可執行文件中,將該可執行文件的各節裝入到指定地址處。vmlinux.lds 是存在於 arch// 目錄中的內核鏈結器腳本,它負責鏈結內核的各個節並將它們裝入記憶體中特定偏移量處。典型的 vmlinux.lds 看起來象這樣:
清單 2. 典型的 vmlinux.lds 文件
OUTPUT_ARCH() /* includes architecture type */ ENTRY(stext) /* stext is the kernel entry point */ SECTIONS /* SECTIONS command describes the layout of the output file */ { . = TEXTADDR; /* TEXTADDR is LMA for the kernel */ .init : { /* Init code and data*/ _stext = .; /* First section is stext followed by __init data section */ __init_begin = .; *(.text.init) __init_end = .; } .text : { /* Real text segment follows __init_data section */ _text = .; *(.text) _etext = .; /* End of text section*/ } .data :{ _data=.; /* Data section comes after text section */ *(.data) _edata=.; } /* Data section ends here */ .bss : { /* BSS section follows symbol table section */ __bss_start = .; *(.bss) _end = . ; /* BSS section ends here */ } }
LMA 是裝入模組位址;它表示將要裝入內核的目標虛擬記憶體中的位址。TEXTADDR 是內核的虛擬起始位址,並且在 arch// 下的 Makefile 中指定它的值。這個地址必須與引導裝載程式使用的地址相匹配。
一旦引導裝載程式將內核複製到快閃記憶體或 DRAM 中,內核就被重新定位到 TEXTADDR — 它通常在 DRAM 中。然後,引導裝載程式將控制轉給這個位址,以便內核能開始執行。
參數傳遞和內核引導
stext 是內核入口點,這意味著在內核引導時將首先執行這一節下的代碼。它通常用組合語言編寫,並且通常它在 arch// 內核目錄下。這個代碼設置內核頁面目錄、創建身份內核映射、標識體系結構和處理器以及執行分支 start_kernel(初始化系統的主常式)。
start_kernel 調用 setup_arch 作�d執行的第一步,在其中完成特定於體系結構的設置。這包括初始化硬體寄存器、標識根設備和系統中可用的 DRAM 和快閃記憶體的數量、指定系統中可用頁面的數目、文件系統大小等等。所有這些資訊都以參數形式從引導裝載程式傳遞到內核。
將參數從引導裝載程式傳遞到內核有兩種方法:parameter_structure 和標記列表。在這兩種方法中,不贊成使用參數結構,因�d它強加了限制:指定在記憶體中,每個參數必須位於 param_struct 中的特定偏移量處。最新的內核期望參數作�d標記列表的格式來傳遞,並將參數轉化�d已標記格式。param_struct 定義在 include/asm/setup.h 中。它的一些重要欄位是:
清單 3. 樣本參數結構
struct param_struct { unsigned long page_size; /* 0: Size of the page */ unsigned long nr_pages; /* 4: Number of pages in the system */ unsigned long ramdisk /* 8: ramdisk size */ unsigned long rootdev; /* 16: Number representing the root device */ unsigned long initrd_start; /* 64: starting address of initial ramdisk */ /* This can be either in flash/dram */ unsigned long initrd_size; /* 68: size of initial ramdisk */ }
請注意:這些數表示定義欄位的參數結構中的偏移量。這意味著如果引導裝載程式將參數結構放置在位址 0xc0000100,那�l rootdev 參數將放置在 0xc0000100 + 16,initrd_start 將放置在 0xc0000100 + 64 等等 — 否則,內核將在解釋正確的參數時遇到困難。
正如上面提到的,因�d從引導裝載程式到內核的參數傳遞會有一些約束條件,所以大多數 2.4.x 系列內核期望參數以已標記的列表格式傳遞。在已標記的列表中,每個標記由標識被傳遞參數的 tag_header 以及其後的參數值組成。標記列表中標記的常規格式可以如下所示:
清單 4. 樣本標記格式。內核通過 頭來標識每個標記。
#define struct { u32 ; u32 ; }; /* Example tag for passing memory information */ #define ATAG_MEM 0x54410002 /* Magic number */ struct tag_mem32 { u32 size; /* size of memory */ u32 start; /* physical start address of memory*/ };
setup_arch 還需要對快閃記憶體存儲庫、系統寄存器和其他特定設備執行記憶體映射。一旦完成了特定於體系結構的設置,控制就返回到初始化系統其餘部分的 start_kernel 函數。這些附加的初始化任務包含:
· 設置陷阱
· 初始化中斷
· 初始化計時器
· 初始化控制臺
· 調用 mem_init,它計算各種區域、高記憶體區等內的頁面數量
· 初始化 slab 分配器並�d VFS、緩衝區快取記憶體等創建 slab 快取記憶體
· 建立各種文件系統,如 proc、ext2 和 JFFS2
· 創建 kernel_thread,它執行文件系統中的 init 命令並顯示 lign 提示符。 如果在 /bin、/sbin 或 /etc 中沒有 init 程式,那�l內核將執行文件系統的 /bin 中的 shell。

設備驅動程式
嵌入式系統通常有許多設備用於與用戶交互,象觸摸屏、小鍵盤、滾動輪、感測器、RA232 介面、LCD 等等。除了這些設備外,還有許多其他專用設備,包括快閃記憶體、USB、GSM 等。內核通過所有這些設備各自的設備驅動程式來控制它們,包括 GUI 用戶應用程式也通過訪問這些驅動程式來訪問設備。本節著重討論通常幾乎在每個嵌入式環境中都會使用的一些重要設備的設備驅動程式。
幀緩衝區驅動程式
這是最重要的驅動程式之一,因�d通過這個驅動程式才能使系統螢幕顯示內容。幀緩衝區驅動程式通常有三層。最底層是基本控制臺驅動程式 drivers/char/console.c,它提供了文本控制臺常規介面的一部分。通過使用控制臺驅動程式函數,我們能將文本列印到螢幕上 — 但圖形或動畫還不能(這樣做需要使用視頻模式功能,通常出現在中間層,也就是 drivers/video/fbcon.c 中)。這個第二層驅動程式提供了視頻模式中繪圖的常規介面。
幀緩衝區是顯卡上的記憶體,需要將它記憶體映射到用戶空間以便可以將圖形和文本能寫到這個記憶體段上:然後這個資訊將反映到螢幕上。幀緩衝區支援提高了繪圖的速度和整體性能。這也是頂層驅動程式引人注意之處:頂層是非常特定於硬體的驅動程式,它需要支援顯卡不同的硬體方面 — 象�坏峞�禁用顯卡控制器、深度和模式的支援以及調色板等。所有這三層都相互依賴以實現正確的視頻功能。與幀緩衝區有關的設備是 /dev/fb0(主設備號 29,次設備號 0)。
輸入設備驅動程式
可觸摸板是用於嵌入式設備的最基本的用戶交互設備之一 — 小鍵盤、感測器和滾動輪也包含在許多不同設備中以用於不同的用途。
觸摸板設備的主要功能是隨時報告用戶的觸摸,並標識觸摸的座標。這通常在每次發生觸摸時,通過生成一個中斷來實現。
然後,這個設備驅動程式的角色是每當出現中斷時就查詢觸摸屏控制器,並請求控制器發送觸摸的座標。一旦驅動程式接收到座標,它就將有關觸摸和任何可用資料的信號發送給用戶應用程式,並將資料發送給應用程式(如果可能的話)。然後用戶應用程式根據它的需要處理資料。
幾乎所有輸入設備 — 包括小鍵盤 — 都以類似原理工作。
快閃記憶體 MTD 驅動程式
MTD 設備是象快閃記憶體晶片、小型快閃記憶體卡、記憶棒等之類的設備,它們在嵌入式設備中的使用正在不斷增長。
MTD 驅動程式是在 Linux 下專門�d嵌入式環境開發的新的一類驅動程式。相對於常規塊設備驅動程式,使用 MTD 驅動程式的主要優點在於 MTD 驅動程式是專門�d基於快閃記憶體的設備所設計的,所以它們通常有更好的支援、更好的管理和基於磁區的擦除和讀寫操作的更好的介面。Linux 下的 MTD 驅動程式介面被劃分�d兩類模組:用戶模組和硬體模組。
用戶模組
這些模組提供從用戶空間直接使用的介面:原始字元訪問、原始塊訪問、FTL(快閃記憶體轉換層,Flash Transition Layer — 用在快閃記憶體上的一種文件系統)和 JFS(即日誌文件系統,Journaled File System — 在快閃記憶體上直接提供文件系統而不是類比塊設備)。用於快閃記憶體的 JFS 的當前版本是 JFFS2(稍後將在本文中描述)。
硬體模組
這些模組提供對記憶體設備的物理訪問,但並不直接使用它們。通過上述的用戶模組來訪問它們。這些模組提供了在快閃記憶體上讀、擦除和寫操作的實際常式。
MTD 驅動程式設置
�d了訪問特定的快閃記憶體設備並將文件系統置於其上,需要將 MTD 子系統編譯到內核中。這包括選擇適當的 MTD 硬體和用戶模組。當前,MTD 子系統支援�d數�萓h的快閃記憶體設備 — 並且有越來越多的驅動程式正被添加進來以用於不同的快閃記憶體晶片。
有兩個流行的用戶模組可�坏庣鴽眥{記憶體的訪問:MTD_CHAR 和 MTD_BLOCK。
MTD_CHAR 提供對快閃記憶體的原始字元訪問,而 MTD_BLOCK 將快閃記憶體設計�d可以在上面創建文件系統的常規塊設備(象 IDE 磁片)。與 MTD_CHAR 關聯的設備是 /dev/mtd0、mtd1、mtd2(等等),而與 MTD_BLOCK 關聯的設備是 /dev/mtdblock0、mtdblock1(等等)。由於 MTD_BLOCK 設備提供象塊設備那樣的類比,通常更可取的是在這個類比基礎上創建象 FTL 和 JFFS2 那樣的文件系統。
�d了進行這個操作,可能需要創建分區表將快閃記憶體設備分拆到引導裝載程式節、內核節和文件系統節中。樣本分區表可能包含以下資訊:
清單 5. MTD 的簡單快閃記憶體設備分區
struct mtd_partition sample_partition = { { /* First partition */ name : bootloader, /* Bootloader section */ size : 0x00010000, /* Size */ offset : 0, /* Offset from start of flash- location 0x0*/ mask_flags : MTD_WRITEABLE /* This partition is not writable */ }, { /* Second partition */ name : Kernel, /* Kernel section */ size : 0x00100000, /* Size */ offset : MTDPART_OFS_APPEND, /* Append after bootloader section */ mask_flags : MTD_WRITEABLE /* This partition is not writable */ }, { /* Third partition */ name : JFFS2, /* JFFS2 filesystem */ size : MTDPART_SIZ_FULL, /* Occupy rest of flash */ offset : MTDPART_OFS_APPEND /* Append after kernel section */ } }
上面的分區表使用了 MTD_BLOCK 介面對快閃記憶體設備進行分區。這些分區的設備節點是:
簡單快閃記憶體分區的設備節點
User device node Major number Minor number Bootloader /dev/mtdblock0 31 0 Kernel /dev/mtdblock1 31 1 Filesystem /dev/mtdblock2 31 2
在本例中,引導裝載程式必須將有關 root 設備節點(/dev/mtdblock2)和可以在快閃記憶體中找到文件系統的地址(本例中是 FLASH_BASE_ADDRESS + 0x04000000)的正確參數傳遞到內核。一旦完成分區,快閃記憶體設備就準備裝入或挂裝文件系統。
Linux 中 MTD 子系統的主要目標是在系統的硬體驅動程式和上層,或用戶模組之間提供通用介面。硬體驅動程式不需要知道象 JFFS2 和 FTL 那樣的用戶模組使用的方法。所有它們真正需要提供的就是一組對底層快閃記憶體系統進行 read、 write 和 erase 操作的簡單常式。
嵌入式設備的文件系統
系統需要一種以結構化格式存儲和檢索資訊的方法;這就需要文件系統的參與。Ramdisk(請參閱參考資料)是通過將電腦的 RAM 用作設備來創建和挂裝文件系統的一種機制,它通常用於無盤系統(當然包括微型嵌入式設備,它只包含作�d永久存儲媒質的快閃記憶體晶片)。
用戶可以根據可靠性、健壯性和/或增強的功能的需求來選擇文件系統的類型。下一節將討論幾個可用選項及其優缺點。
第二版擴展文件系統(Ext2fs)
Ext2fs 是 Linux 事實上的標準文件系統,它已經取代了它的前任 — 擴展文件系統(或 Extfs)。Extfs 支援的文件大小最大�d 2 GB,支援的最大檔案名稱大小�d 255 個字元 — 而且它不支援索引節點(包括資料修改時間標記)。Ext2fs 做得更好;它的優點是:
· Ext2fs 支援達 4 TB 的記憶體。
· Ext2fs 檔案名稱最長可以到 1012 個字元。
· 當創建文件系統時,管理員可以選擇邏輯塊的大小(通常大小可選擇 1024、2048 和 4096 位元組)。
· Ext2fs 了實現快速符號鏈結:不需要�d此目的而分配資料塊,並且將目標名稱直接存儲在索引節點(inode)表中。這使性能有所提高,特別是在速度上。

因�d Ext2 文件系統的穩定性、可靠性和健壯性,所以幾乎在所有基於 Linux 的系統(包括臺式機、伺服器和工作站 — 並且甚至一些嵌入式設備)上都使用 Ext2 文件系統。然而,當在嵌入式設備中使用 Ext2fs 時,它有一些缺點:
· Ext2fs 是�d象 IDE 設備那樣的塊設備設計的,這些設備的邏輯塊大小是 512 位元組,1 K 位元組等這樣的倍數。這不太適合於磁區大小因設備不同而不同的快閃記憶體設備。
· Ext2 文件系統沒有提供對基於磁區的擦除/寫操作的良好管理。在 Ext2fs 中,�d了在一個磁區中擦除單個位元組,必須將整個磁區複製到 RAM,然後擦除,然後重寫入。考慮到快閃記憶體設備具有有限的擦除壽命(大約能進行 100,000 次擦除),在此之後就不能使用它們,所以這不是一個特別好的方法。
· 在出現電源故障時,Ext2fs 不是防崩潰的。
· Ext2 文件系統不支援損耗平衡,因此縮短了磁區/快閃記憶體的壽命。(損耗平衡確保將地址範圍的不同區域輪流用於寫和/或擦除操作以延長快閃記憶體設備的壽命。)
· Ext2fs 沒有特別完美的磁區管理,這使設計塊驅動程式十分困難。

由於這些原因,通常相對於 Ext2fs,在嵌入式環境中使用 MTD/JFFS2 組合是更好的選擇。
用 Ramdisk 挂裝 Ext2fs
通過使用 Ramdisk 的概念,可以在嵌入式設備中創建並挂裝 Ext2 文件系統(以及用於這一目的的任何文件系統)。
清單 6. 創建一個簡單的基於 Ext2fs 的 Ramdisk
mke2fs -vm0 /dev/ram 4096 mount -t ext2 /dev/ram /mnt cd /mnt cp /bin, /sbin, /etc, /dev ... files in mnt cd ../ umount /mnt dd if=/dev/ram bs=1k count=4096 of=ext2ramdisk
mke2fs 是用於在任何設備上創建 ext2 文件系統的實用程式 — 它創建超級塊、索引節點以及索引節點表等等。
在上面的用法中,/dev/ram 是上面構建有 4096 個塊的 ext2 文件系統的設備。然後,將這個設備(/dev/ram)挂裝在名�d /mnt 的臨時目錄上並且複製所有必需的文件。一旦複製完這些文件,就卸裝這個文件系統並且設備(/dev/ram)的內容被轉儲到一個文件(ext2ramdisk)中,它就是所需的 Ramdisk(Ext2 文件系統)。
上面的順序創建了一個 4 MB 的 Ramdisk,並用必需的文件實用程式來填充它。
一些要包含在 Ramdisk 中的重要目錄是:
· /bin — 保存大多數象 init、busybox、shell、文件管理實用程式等二進位文件。
· /dev — 包含用在設備中的所有設備節點
· /etc — 包含系統的所有配置文件
· /lib — 包含所有必需的庫,如 libc、libdl 等

日誌快閃記憶體文件系統,版本 2(JFFS2)
瑞典的 Axis Communications 開發了最初的 JFFS,Red Hat 的 David Woodhouse 對它進行了改進。 第二個版本,JFFS2,作�d用於微型嵌入式設備的原始快閃記憶體晶片的實際文件系統而出現。JFFS2 文件系統是日誌結構化的,這意味著它基本上是一長列節點。每個節點包含有關文件的部分資訊 — 可能是文件的名稱、也許是一些資料。相對於 Ext2fs,JFFS2 因�d有以下這些優點而在無盤嵌入式設備中越來越受歡迎:
· JFFS2 在磁區級別上執行快閃記憶體擦除/寫/讀操作要比 Ext2 文件系統好。
· JFFS2 提供了比 Ext2fs 更好的崩潰/掉電安全保護。當需要更改少量資料時,Ext2 文件系統將整個磁區複製到記憶體(DRAM)中,在記憶體中合併新資料,並寫回整個磁區。這意味著�d了更改單個字,必須對整個磁區(64 KB)執行讀/擦除/寫常式 — 這樣做的效率非常低。要是運氣差,當正在 DRAM 中合併資料時,發生了電源故障或其他事故,那�l將丟失整個資料集合,因�d在將資料讀入 DRAM 後就擦除了快閃記憶體磁區。JFFS2 附加文件而不是重寫整個磁區,並且具有崩潰/掉電安全保護這一功能。
· 這可能是最重要的一點:JFFS2 是專門�d象快閃記憶體晶片那樣的嵌入式設備創建的,所以它的整個設計提供了更好的快閃記憶體管理。

因�d本文主要是寫關於快閃記憶體設備的使用,所以在嵌入式環境中使用 JFFS2 的缺點很少:
· 當文件系統已滿或接近滿時,JFFS2 會大大放慢運行速度。這是因�d垃圾收集的問題(更多資訊,請參閱參考資料)。

創建 JFFS2 文件系統
在 Linux 下,用 mkfs.jffs2 命令創建 JFFS2 文件系統(基本上是使用 JFFS2 的 Ramdisk)。
清單 7. 創建 JFFS2 文件系統
mkdir jffsfile cd jffsfile /* copy all the /bin, /etc, /usr/bin, /sbin/ binaries and /dev entries that are needed for the filesystem here */ /* Type the following command under jffsfile directory to create the JFFS2 Image */ ./mkfs.jffs2 -e 0x40000 -p -o ../jffs.image
上面顯示了 mkfs.jffs2 的典型用法。-e 選項確定快閃記憶體的擦除磁區大小(通常是 64 千位元組)。-p 選項用來在映射的剩餘空間用零填充。-o 選項用於輸出文件,通常是 JFFS2 文件系統映射 — 在本例中是 jffs.image。一旦創建了 JFFS2 文件系統,它就被裝入快閃記憶體中適當的位置(引導裝載程式告知內核查找文件系統的地址)以便內核能挂裝它。
tmpfs
當 Linux 運行於嵌入式設備上時,該設備就成�d功能齊全的單元,許多守護進程會在後臺運行並生成許多日誌消息。另外,所有內核日誌記錄機制,象 syslogd、dmesg 和 klogd,會在 /var 和 /tmp 目錄下生成許多消息。由於這些進程�{生了大量資料,所以允許將所有這些寫操作都發生在快閃記憶體是不可取的。由於在重新引導時這些消息不需要持久存儲,所以這個問題的解決方案是使用 tmpfs。
tmpfs 是基於記憶體的文件系統,它主要用於減少對系統的不必要的快閃記憶體寫操作這一唯一目的。因�d tmpfs 駐留在 RAM 中,所以寫/讀/擦除的操作發生在 RAM 中而不是在快閃記憶體中。因此,日誌消息寫入 RAM 而不是快閃記憶體中,在重新引導時不會保留它們。tmpfs 還使用磁片交換空間來存儲,並且當�d存儲文件而請求頁面時,使用虛擬記憶體(VM)子系統。
tmpfs 的優點包括:
· 動態文件系統大小 — 文件系統大小可以根據被複製、創建或刪除的文件或目錄的數量來縮放。使得能夠最理想地使用記憶體。
· 速度 — 因�d tmpfs 駐留在 RAM,所以讀和寫幾乎都是暫態的。即使以交換的形式存儲文件,I/O 操作的速度仍非常快。

tmpfs 的一個缺點是當系統重新引導時會丟失所有資料。因此,重要的資料不能存儲在 tmpfs 上。
挂裝 tmpfs
諸如 Ext2fs 和 JFFS2 等大多數其他文件系統都駐留在底層塊設備之上,而 tmpfs 與它們不同,它直接位於 VM 上。因而,挂裝 tmpfs 文件系統是很簡單的事:
清單 8. 挂裝 tmpfs
/* Entries in /etc/rc.d/rc.sysinit for creating/using tmpfs */ # mount -t tmpfs tmpfs /var -o size=512k # mkdir -p /var/tmp # mkdir -p /var/log # ln -s /var/tmp /tmp
上面的命令將在 /var 上創建 tmpfs 並將 tmpfs 的最大大小限制�d 512 K。同時,tmp/ 和 log/ 目錄成�d tmpfs 的一部分以便在 RAM 中存儲日誌消息。
如果您想將 tmpfs 的一個項添加到 /etc/fstab,那�l它可能看起來象這樣:

tmpfs /var tmpfs size=32m 0 0
這將在 /var 上挂裝一個新的 tmpfs 文件系統。
圖形用戶介面(GUI)選項
從用戶的觀點來看,圖形用戶介面(GUI)是系統的一個最至關重要的方面:用戶通過 GUI 與系統進行交互。所以 GUI 應該易於使用並且非常可靠。但它還需要是有記憶體意識的,以便在記憶體受限的、微型嵌入式設備上可以無縫執行。所以,它應該是羽量級的,並且能夠快速裝入。
另一個要考慮的重要方面涉及許可證問題。一些 GUI 分發版具有允許免費使用的許可證,甚至在一些商業�{品中也是如此。另一些許可證要求如果想將 GUI 合併入專案中則要支付版稅。
最後,大多數開發人員可能會選擇 XFree86,因�d XFree86 �d他們提供了一個能使用他們喜歡的工具的熟悉環境。但是市場上較新的 GUI,象 Century Software 的 Microwindows(Nano-X)和 Trolltech 的 QT/Embedded,與 X 在嵌入式 Linux 的競技舞臺中展開了激烈競爭,這主要是因�d它們佔用很少的資源、執行的速度很快並且具有定制視窗構件的支援。
讓我們看一看這些選項中的每一個。
Xfree86 4.X(帶幀緩衝區支援的 X11R6.4)
XFree86 Project, Inc. 是一家生�{ XFree86 的公司,該�{品是一個可以免費重復分發、開放源碼的 X Window 系統。X Window 系統(X11)�d應用程式以圖形方式進行顯示提供了資源,並且它是 UNIX 和類 UNIX 的機器上最常用的視窗系統。它很小但很有效,它運行在�d數�萓h的硬體上,它對網路透明並且有良好的文檔說明。X11 �d窗口管理、事件處理、同步和客戶機間通信提供強大的功能 — 並且大多數開發人員已經熟悉了它的 API。它具有對內核幀緩衝區的內置支援,並佔用非常少的資源 — 這非常有助於記憶體相對較少的設備。X 伺服器支援 VGA 和非 VGA 圖形卡,它對�岫漜`度 1、2、4、8、16 和 32 提供支援,並對渲染提供內置支援。最新的發行版是 XFree86 4.1.0。
它的優點包括:
· 幀緩衝區體系結構的使用提高了性能。
· 佔用的資源相對很小 — 大小在 600 K 到 700 K 位元組的範圍內,這使它很容易在小型設備上運行。
· 非常好的支援:在線有許多文檔可用,還有許多專用於 XFree86 開發的郵遞列表。
· X API 非常適合擴展。

它的缺點包括:
· 比最近出現的嵌入式 GUI 工具性能差。
· 此外,當與 GUI 中最新的開發 — 象專門�d嵌入式環境設計的 Nano-X 或 QT/Embedded — 相比時,XFree86 似乎需要更多的記憶體。

Microwindows
Microwindows 是 Century Software 的開放源代碼專案,設計用於帶小型顯示單元的微型設備。它有許多針對現代圖形視窗環境的功能部件。象 X 一樣,有多種平臺支援 Microwindows。
Microwindows 體系結構是基於客戶機/伺服器的並且具有分層設計。最底層是螢幕和輸入設備驅動程式(關於鍵盤或滑鼠)來與實際硬體交互。在中間層,可移植的圖形引擎提供對線的繪製、區域的填充、多邊形、裁剪以及�岫熉珓洩漱銧屆C
在最上層,Microwindows 支援兩種 API:Win32/WinCE API 實現,稱�d Microwindows;另一種 API 與 GDK 非常相似,它稱�d Nano-X。Nano-X 用在 Linux 上。它是象 X 的 API,用於佔用資源少的應用程式。
Microwindows 支援 1、2、4 和 8 bpp(每圖元的位元數)的 palletized 顯示,以及 8、16、24 和 32 bpp 的真彩色顯示。Microwindows 還支援使它速度更快的幀緩衝區。Nano-X 伺服器佔用的資源大約在 100 K 到 150 K 位元組。
原始 Nano-X 應用程式的平均大小在 30 K 到 60 K。由於 Nano-X 是�d有記憶體限制的低端設備設計的,所以它不象 X 那樣支援很多函數,因此它實際上不能作�d微型 X(Xfree86 4.1)的替代品。
可以在 Microwindows 上運行 FLNX,它是針對 Nano-X 而不是 X 進行修改的 FLTK(快速輕巧工具箱(Fast Light Toolkit))應用程式開發環境的一個版本。本文中描述 FLTK。
Nano-X 的優點包括:
· 與 Xlib 實現不同,Nano-X 仍在每個客戶機上同步運行,這意味著一旦發送了客戶機請求包,伺服器在�d另一個客戶機提供服務之前一直等待,直到整個包都到達�d止。這使伺服器代碼非常簡單,而運行的速度仍非常快。
· 佔用很小的資源

Nano-X 的缺點包括:
· 聯網功能部件至今沒有經過適當地調整(特別是網路透明性)。
· 還沒有太多現成的應用程式可用。
· 與 X 相比,Nano-X 雖然近來正在加速開發,但仍沒有那�l多文檔說明而且沒有很好的支援,但這種情形會有所改變。

Microwindows 上的 FLTK API
FLTK 是一個簡單但靈活的 GUI 工具箱,它在 Linux 世界中贏得越來越多的關注,它特別適用于佔用資源很少的環境。它提供了您期望從 GUI 工具箱中獲得的大多數視窗構件,如按鈕、對話方塊、文本框以及出色的“賦值器”選擇(用於輸入數值的窗口構件)。還包括滑動器、捲軸、刻度盤和其他一些構件。
針對 Microwindows GUI 引擎的 FLTK 的 Linux 版本被稱�d FLNX。FLNX 由兩個元件構成:Fl_Widget 和 FLUID。Fl_Widget 由所有基本窗口構件 API 組成。FLUID(快速輕巧的用戶介面設計器(Fast Light User Interface Designer, FLUID))是用來�{生 FLTK 源代碼的圖形編輯器。總的來說,FLNX 是能用來�d嵌入式環境創建應用程式的一個出色的 UI 構建器。
Fl_Widget 佔用的資源大約是 40 K 到 48 K,而 FLUID(包括了每個窗口構件)大約佔用 380 K。這些非常小的資源佔用率使 Fl_Widget 和 FLUID 在嵌入式開發世界中非常受歡迎。
優點包括:
· 習慣於在象 Windows 這樣已建立得較好的環境中開發基於 GUI 的應用程式的任何人都會非常容易地適應 FLTK 環境。
· 它的文檔包括一本十分完整且編寫良好的手冊。
· 它使用 LGPL 進行分發,所以開發人員可以靈活地發放他們應用程式的許可證。
· FLTK 是一個 C++ 庫(Perl 和 Python 綁定也可用)。面向物件模型的選擇是一個好的選擇,因�d大多數現代 GUI 環境都是面向物件的;這也使將編寫的應用程式移植到類似的 API 中變得更容易。
· Century Software 的環境提供了幾個有用的工具,諸如 ScreenToP 和 ViewML 瀏覽器。

它的缺點是:
· 普通的 FLTK 可以與 X 和 Windows API 一同工作,而 FLNX 不能。它與 X 的不相容性阻礙了它在許多專案中的使用。

Qt/Embedded
Qt/Embedded 是 Trolltech 新開發的用於嵌入式 Linux 的圖形用戶介面系統。Trolltech 最初創建 Qt 作�d跨平臺的開發工具用於 Linux 臺式機。它支援各種有 UNIX 特點的系統以及 Microsoft Windows。KDE — 最流行的 Linux 桌面環境之一,就是用 Qt 編寫的。
Qt/Embedded 以原始 Qt �d基礎,並做了許多出色的調整以適用於嵌入式環境。Qt Embedded 通過 Qt API 與 Linux I/O 設施直接交互。那些熟悉並已適應了面向物件編程的人員將發現它是一個理想環境。而且,面向物件的體系結構使代碼結構化、可重用並且運行快速。與其他 GUI 相比,Qt GUI 非常快,並且它沒有分層,這使得 Qt/Embedded 成�d用於運行基於 Qt 的程式的最緊湊環境。
Trolltech 還推出了 Qt 掌上機環境(Qt Palmtop Environment,俗稱 Qpe)。Qpe 提供了一個基本桌面視窗,並且該環境�d開發提供了一個易於使用的介面。Qpe 包含全套的個人資訊管理(Personal Information Management (PIM))應用程式、因特網客戶機、實用程式等等。然而,�d了將 Qt/Embedded 或 Qpe 集成到一個�{品中,需要從 Trolltech 獲得商業許可證。(原始 Qt 自版本 2.2 以後就可以根據 GPL 獲得 。)
它的優點包括:
· 面向物件的體系結構有助於更快地執行
· 佔用很少的資源,大約 800 K
· 抗鋸齒文本和混合視頻的象素映射

它的缺點是:
· Qt/Embedded 和 Qpe 只能在獲得商業許可證的情況下才能使用。

結束語
嵌入式 Linux 開發正迅速地發展著。您必須學習並從引導裝載程式和分發版到文件系統和 GUI 中的每一個事物的各種選項中作出選擇。但是要感謝有這種選擇自由度以及非常活躍的 Linux 社區,Linux 上的嵌入式開發已經達到了新的境界,並且調整模組以適合您的規範從未比現在更簡單。這已經導致出現了許多時新的手持和微型設備作�d開放盒,這是件好事 — 因�d事實是您不必成�d一個專家從這些模組中進行選擇來調整您的設備以滿足您自己的要求和需要。
我們希望這篇對嵌入式 Linux 領域的介紹性概述能激起您進行試驗的欲望,並且希望您將體會擺弄微型設備的樂趣以滿足您的愛好。�d進一步有助於您的專案,請參閱下面的“參考資料”,鏈結到有關我們這裏已經概述的技術的更深入的資訊。
參考資料
· 引導:
· 如需獲得對 vmlinux 和 zimage 之間區別的極好解釋,請在 Alessandro Rubini 編寫的“Kernel Configuration: dealing with the unexpected(Linux Magazine)的一文中找到“Booting your kernel”一節。
· 有關內核、映射和引導過程的更多資訊,請閱讀中央昆士蘭大學(Central Queensland University)的系統管理文本的第 13 章。
· 要進一步瞭解引導過程的工作原理,請參閱 ROLO: A Developer's Guide,它討論了在不利用 BIOS 的情況下引導 Linux(Embedded Linux Works,2001 年 6 月)。
· 小型分發版:
· The Embedded Linux Distributions Quick Reference Guide 涵蓋了許多商業的和開放源碼的分發版(Linux Devices,2001 年 8 月)。
· 請查看另一個詳盡的分發版和有用的工具的清單(Linux-embedded.com)。
· 工具鏈:
· Wiki 工具鏈頁面包含到本文提到的所有三個工具鏈的鏈結,還有對它們的評論。
· 設備驅動程式:
· Memory Technology Device (MTD) Subsystem for Linux 的目的是簡化記憶體設備(特別是快閃記憶體設備)的驅動程式的創建。
· Vipin Malik 編寫的 The Linux MTD, JFFS HOWTO 將幫助您使 MTD 和 JFFS2 一起工作。
· Linux for PowerPC Embedded Systems HOWTO 有一個很好的設備驅動程式清單。
· 理解 Linux device drivers 有助於理解本篇介紹性文章(Penguin Magazine)。
· 要精通 Linux 設備驅動程式,請閱讀 O'Reilly 的 Linux Device Drivers,第 2 版一書。
· 有用的工具:
· 請查看 LART 上的 Jflash-linux。
· Binutils、GCC 和 Glibc 都可從 Free Software Foundation 下載獲得。
· 許多有用的下載都可從 Netwinder.org 獲得,這是一個致力於 NetWinder 平臺上開發工作的志願者站點。
· 請在 Mark Nielsen 寫得非常棒的 How to use a Ramdisk for Linux 一文中閱讀有關 Ramdisk 的所有資訊。
· FLNX 是以 FLTK(快速輕巧的工具箱)�d基礎的。
· 文件系統:
· 第二版擴展文件系統 Ext2fs 的主頁在 SourceForge。
· Red Hat 英國公司的 David Woodhouse 概述了大量有關 JFFS2:日誌快閃記憶體文件系統,第 2 版的背景知識。
· Vipin Malik 的 JFFS - A practical guide 一文也詳細討論了 JFFS,包括垃圾收集的問題(Embedded Linux Works,2001 年 5 月)。
· 您可以在 Linux HeadQuarters 閱讀更多有關 tmpfs 的資訊。
· Cliff Brake 和 Jeff Sutherland 編寫的 Flash Filesystems for Embedded Linux Systems 一文論述了用於快閃記憶體設備的更多文件系統(Embedded Linux Journal)。
· GUI:
· Xfree86 是 X 開發的主頁。
· 在 Microwindows 站點上可以找到有關 Microwindows 和 Nano-X 的資訊。
· 請查看一篇對 Microwindows 的一些�

學習嵌入式Linux的筆記和體會

[轉自Experiences of Code (http://blog.csdn.net/zblue78)]


以下是我初次學習嵌入式linux的筆記和體會,製作了軟盤minicom應用,在此基礎上也清楚了軟盤Linux的實現,並利用busybox實際製作了軟盤上的Linux系統。希望能給新手一些幫助和啟發,同時也請高手批評指正。
yihui, eazi@163.com 2004年2月12日

一個典型的桌面Linux系統包括3個主要的軟件層---linux內核、C庫和應用程序代碼。
內核是唯一可以完全控制硬件的層,內核驅動程序代表應用程序與硬件之間進行會話。內核之上是C庫,負責把POSIX API轉換為內核可以識別的形式,然後調用內核,從應用程序向內核傳遞參數。應用程序依靠驅動內核來完成特定的任務。
在設計嵌入式應用的時候,可以不按照這種層次,應用程序越過C庫直接和內核會話,或者把應用和內核捆綁在一起,甚至可以把應用寫為內核的一個線程,在內核中運行,雖然這樣在移植上帶來了困難,但考慮嵌入式系統對尺寸要求小的特點,是完全可行的。不過我們使用三層軟件結構的模式來學習嵌入式linux將會是我們認識更清晰,簡單可行並使應用具有彈性。

快速入門,最簡單的建立嵌入式Linux應用的方法就是從我們使用的桌面Linux入手,安裝一個喜愛的版本,把我們的某個應用作為初始化的一部分,框架就算完成了。當然,嵌入式linux應用遠比我們的桌面版本功能簡單專一,它也許就是一個用於足彩的終端機,或是一個數碼音頻播放器,這些系統除了使用嵌入式CPU外,僅僅再需要一個串口,網口等少量的輸入輸出接口就可以完成它們特定的應用了。在軟件上,它可以按照三層的概念由內核裝載器,定制的內核和較少的為特定任務設計的靜態連接的應用程序組成。之所以使用靜態連接的應用程序,是因為少量的靜態連接程序所要的存儲空間,比同樣數量的動態連接的程序所佔的空間小,這個平衡點需要我們在實際開發中去獲取。也許你正在設計的是個PDA,它的應用程序較多,那麼你很可能就要使用動態連接程序來減少存儲空間。在你的/bin或者/sbin目錄下,用廠列表看看bash,ifconfig,vi...,也許只用幾十K,當你運行 ldd /bin/bash 時,你會看到它們都和好幾個庫文件相連。好了,這樣看來,我們得把PC想像成一個嵌入式硬件平台,再重新製作一個特定功能的嵌入式linux。

再進行實際操作之前,先來搞清楚幾個基礎知識。
內核裝載器Loader,它的作用是把內核從外部存儲器,移動到內存中。它只作這個事情,一旦完成了調入內核的工作,Loader就跳轉到內核位置開始執行。不同架構有不同的Loader,在x86結構的PC上,通常使用的loader有LILO,GRUB,syslinux,syslinux在嵌入式linux中也同樣工作。其他非x86架構的應用中,你必須使用專門的loader,或者自己編寫loader來裝入內核。也有不使用loader的情況,系統加電以後,內核直接從燒錄有映像的Flash上開始執行。
內核,一旦內核開始執行,它將通過驅動程序初始化所有硬件,這可以從我們的pc機監視器的輸出看出來,每個驅動程序都打印一些有關它的信息。初始化完成後,計算機就準備運行嵌入式應用。也許一個,也許是多個應用程序組成了嵌入式應用,但通常首先調用的是init(通過loader 向核心傳入init=/program 可以定制首先運行的程序)。桌面linux中,init會讀取/etc/inittab文件,來決定執行級別和哪些腳本和命令。嵌入式應用中,可以根據實際的情況決定是否使用標準的init執行方式,也許這個init是個靜態程序,它能夠完成我們的嵌入應用的特定任務,那完全不用考慮inittab了。
initrd文件系統,initrd以一種把內核從存儲介質裝入到內存的相同的機制來裝入一個小型文件系統。這個文件系統最好是以壓縮的方式存儲在介質上的,解壓縮到RAM盤上。通過使用initrd,包含有核心驅動和啟動腳本的小文件系統,就可以直接從介質上和內核一起啟動起來,內核屆壓縮這個文件系統,並執行這個文件系統上叫做/linuxrc的腳本文件,這個腳本通常會把啟動過程中所需要的驅動程序裝入。腳本退出以後,initrd文件系統也卸下了,啟動過程進入真正初始化過程。對於嵌入式來講,可以將需要的應用軟件都運行在這個initrd文件系統上,只要/linxrc文件不結束,內核啟動過程的其他部分就不會繼續。
做個試驗:

cp /boot/initrd-2.4.20.img /tmpcd /tmpmv initrd-2.4.2-.img initrd.img.gzgunzip initrd.img.gzmount -o loop initrd.img /mntcd /mntlscat linuxrc可以看到裡面執行了加載了兩個模塊的操作,你在啟動linxu的時候會看見屏幕打印信息。

入門試驗,製作一個簡單的應用:我們使用一張軟盤啟動一台假象的只有一個串口,鍵盤輸入,顯示輸出的x86架構的linux系統,執行的特定應用就是運行minicom,通過串口撥號。需要軟件: minicom-xx.src.tar.gz 和 syslinux-xx.tar.gz,xx代表版本號 ,開始之前,在主目錄建立一個目錄,來釋放這兩個軟件包:
cdmkdir -p project/minilinuxcd project/minilinuxtar zxvf minicom-xx.src.tar.gztar zxvf syslinux-xx.tar.gz

1、裁減linux內核(需要系統安裝內核文件包)

配置內核的時候,我們需要選擇這些:摸塊編入內核,386處理器、物理內存off、支持ELF、標準PC軟盤、支持RAM盤(4096)、支持initial RAM disk (initrd)、虛你終端、虛擬終端控制台、標準串口、ext2文件系統、控制台驅動,VGA text console、DOS FAT、MSDOS文件系統,其他的都可以不要,這樣內核編出來較小。
步驟:
cd /usr/src/linuxmake mrpropermake xconfigmake dep && make bzImage
得到 /usr/src/linux/arch/i386/boot/目錄的內核文件bzIamge。

2、編譯一個靜態的minicom ,把它作為將來的linuxrc
cd minicom-xx/srcvi Makefile修改下面這行minicom: $(minicom_OBJECTS) $(minicom_DEPENDENCIES)rm -f minicom 下面的行加上 -static,連接為靜態程序(LINK) -static $(minicom_LDFLAGS) $(minicom_OBJECTS) $(minicom_LDADD) $(LIBS)vi minicom.c找到 if (real_uid==0 && dosetup==0 ) 刪除這個判斷條件語句,主要是用於權限判斷的,因為這個嵌入應用不關注權限問題,否則會出錯。make得到可執行程序,用ldd 檢查一下是不是靜態程序。

3、準備initrd壓縮文件image.gz
dd if=/dev/zero of=image bs=1k count=4096losetup /dev/loop0 imagemke2fs -m 0 /dev/loop0mounmt -t ext2 /dev/loop0 /mnt/mkdir -p /mnt/devmkdir -p /mnt/usr/share/terminfo/l/cd /devcp -a consle null tty tty0 zero mem /mnt/devcp -P /usr/share/terminfo/l/linux /mnt/usr/share/terminfo/l/linuxcp ~/project/minilinux/mincom/src/minicom /mnt/linuxrcumount /mntlosetup -d /dev/loop0syncgzip -9 image

4、製作軟盤引導,並拷貝文件 bzimage image.gz 到軟盤

A.使用grub
fdformat /dev/fd0mke2fs /dev/fd0mount /mnt/fd0 /mnt/floppymkdir -p /mnt/floppy/boot/grubcp /boot/grub/stage1 /boot/grub/stage2 /mnt/floppy/boot/grub執行 grub,在軟盤上創建引導grub > root (fd0)grub > setup (fd0)grub > quitcp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppycp ~/porject/minilinux/image.gz /mnt/floppy編輯 /mnt/floppy/boot/grub/grub.confdefault =0timeout-=10title minilinuxroot (fd0)kernel /bzImageinitrd /image.gz卸下軟盤umount /mnt/floppy


B. 使用syslinux
fdformat /dev/fd0mkfs.msdos /dev/fd0mount -t msdos /dev/fd0 /mnt/floppycp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppycp ~/porject/minilinux/image.gz /mnt/floppycp syslinux-xx/ldlinxu.sys /mnt/floppycat > /mnt/floppy/syslinux.cfgLABEL linuxKERNEL bzimageAPPEND initrd=image.gzumont /mnt/floppysyslinux-xx/syslinux /dev/fd0sync

5、用軟盤啟動計算機,如果幸運,minicom的運行畫面出現在屏幕上。

到此,我們的單應用嵌入式linux做好了,但它還很簡陋,沒有什麼實際用途,但通過這個實驗,可以瞭解嵌入式系統的大致結構和開發過程。在進行實際的嵌入式開發時,通常要在PC機上借助嵌入式linux開發工具包,如:uclinux,bluecat等,對相應的硬件平台(目標機)進行軟件編寫編譯,調試成功後,將內核及應用程序寫入到目標機的存儲器中,從而完成整個應用。

一步一步教你在skyeye上運行uboot

[轉自Experiences of Code (http://blog.csdn.net/zblue78)]

一步一步教你在skyeye上運行uboot
by faif

1. 簡介

skyeye是一個很好的,基於各種ARM系列CPU的,SOC和主板級的模擬器。uboot是一個可以在各種cpu(arm,mips,powerpc)的主板上運行的引導程序,相當於PC機的BIOS但是又遠遠的強於普通的BIOS,比如支持網絡引導,引導各種內核,甚至一個簡單的shell,等等。他們兩個都是基於GPL的開源自由軟件。

這篇文章教你怎樣在最少量的修改代碼的情況下,用skyeye模擬EP7312並在上面運行uboot,給接觸嵌入系統的新手一個感性的認識。

2. 建立開發環境

2.1 skyeye模擬器的安裝

開發環境是建立在Linux上的。首先下載安裝skyeyes-0.8.5.1的源代碼包,解壓,按照裡面的readme安裝,注意你的linux要有gtk的支持。安裝的時候要以root的身份。在各種linux發行版下的安裝注意事項參照論壇的相關帖子。安裝成功以後,把skyeye的目標目錄加入你的路徑,這樣你就可以在任何目錄下執行skyeye模擬器了。

2.2 交叉編譯器的安裝

交叉編譯器是運行在主機上編譯另外一種體系結構的編譯器。比如,我的主機是linux在x86上,我現在要編譯基於ARM的代碼,所以我就不能用普通的編譯器而需要交叉編譯器。我曾經試過自己從gcc的源代碼構建交叉編譯器,很麻煩和耗時。uboot的作者同樣也開發了一個很好的交叉編譯器叫ELDK(Embedded linux development kit)。我就使用這個,當然你也可以使用其他嵌入式公司提供的。你可以從以下的網址查看提供ELDK的鏡像:

ELDK Availability: http://www.denx.de/twiki/bin/view/DULG/ELDKAvailability

ELDK有三個版本分別編譯MIPS,PPC和ARM。我從下面的鏡像下載了基於ARM的交叉編譯器:

http://sunsite.utk.edu/ftp/pub/linux/eldk/3.1/arm-linux-x86/iso/

文件為"arm-2004-11-09.iso",它支持ARM7, ARM9, XScale, AT91RM9200 and other ARM based systems。

安裝交叉編譯器,我將交叉編譯器安裝到自己的目錄「/opt/x86-arm/」裡面:
/mnt/cdrom/install -d /opt/x86-arm/

等待安裝結束以後設置好用戶環境:

export PATH="${PATH}:/opt/x86-arm/usr/bin:/opt/x86-arm/bin"export CROSS_COMPILE=arm-linux-
這樣你在任何目錄也可以訪問交叉編譯器了。

測試:
arm-linux-gcc -o testarm test.cfile testarmtestarm: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped.
說明你編譯好的文件是ARM上的代碼了。你可以用arm-linux-gcc來編譯你的文件了。

3. 修改Uboot

從uboot的網站上可以下載到最新的uboot源代碼,你可以從以下的網址下載

http://u-boot.sourceforge.net/
ftp://ftp.denx.de/pub/u-boot/

uboot的源碼結構清晰,註釋詳細,是學習嵌入系統的很好的例子。我下載的是最新的U-Boot-1.1.2。因為我們要模擬EP7312的芯片,而uboot已經支持一個基於EP7312的板子了,所以我們只要對uboot裡面有關EP7312的板子的配置略作修改就可以了。uboot裡面有關主板的配置文件都在"include/configs/.h"下,所以我們找到include/configs/ep7312.h,對它進行修改。
找到#define CONFIG_DRIVER_CS8900 1改為#define CONFIG_DRIVER_CS8900 0 找到#define CONFIG_COMMANDS (CONFIG_CMD_DFL | CFG_CMD_JFFS2)改為#define CONFIG_COMMANDS (CONFIG_CMD_DFL) /*Skyeye doesn't have jffs2*/

然後回到uboot的根目錄下,配置,編譯:
make ep7312_configmake all
等待結束以後我們會發現u-boot.bin和u-boot兩個文件,其中u-boot.bin是raw的二進制文件。u-boot是ELF格式的。

4. 配置skyeye,並運行uboot

首先,新建一個目錄代表你的EP7312的主板。這樣也可以保持文件的清潔和有序。
mkdir board01
將你剛才編譯成功的u-boot.bin拷貝到這個目錄下來。skyeye支持raw binary和ELF的格式,這裡我們用raw binary的格式。

編輯skyeye.conf,這個文件是用來配置主板的,詳細說明見skyeye的相關文檔。我的skyeye.conf如下:
#skyeye config file for ubootcpu: arm720tmach: ep7312 mem_bank: map=I, type=RW, addr=0x80000000, size=0x00010000#skyeye for uboot flash 16M bank 1mem_bank: map=M, type=RW, addr=0x00000000, size=0x01000000, file=./u-boot.bin,boot=yes#skyeye for uboot sdram 16m bank 1mem_bank: map=M, type=RW, addr=0xc0000000, size=0x01000000
注意這裡的內存的地址和容量的分配都是根據uboot裡面的ep7312的配置文件調整的。這樣也可以是我們對uboot的代碼修改做到最小。

這時候你的skyeye-ep7312主板就配置好了。你可以試著運行了。在你現在的目錄下打入:
skyeye

然後在skyeye的界面下打入:
target simrun
這時候你可以看到uboot的啟動界面,和提示符,如果你鍵入「hlep」,可以查看所有uboot支持的命令,鍵入「version」可以查看當前uboot的版本,等等。

5. 進一步的工作

由於本文是最基本的介紹性的一步一步的指導。有很多工作還要去嘗試。比如:

× 由於現在skyeye還不支持flash內存,所以我們是不是可以修改uboot上ep7312相對flash的代碼來臨時滿足我們的需要,不然的話,對於在uboot上面的環境參數的設置,我們只能去修改源碼裡面的缺省參數。

× Uboot支持8019AS的以太網控制器,skyeye也支持了這個硬件的模擬,我們要進一步的使uboot的網絡也在skyeye上用起來?

× 對於模擬flash的開發也可以用uboot來測試。uboot裡面各種板子有大量的flash驅動程序。

還望各位高手指教,我進一步修改和提高。

學習使用SkyEye模擬器

[轉自Experiences of Code (http://blog.csdn.net/zblue78)]

學習使用SkyEye模擬器

--------------------------------------------------------------------------------


SkyEye是一個可以運行嵌入式操作系統的硬件仿真工具,這樣就可以在沒有硬件條件下來進行嵌入式系統的開發。

以下操作均在Fedora Core 1.0里通過。

Skyeye項目資源列表
http://gro.clinux.org/projects/skyeye/


文檔摘要:
1、什麼是SkyEye?
2、SkyEye可以做什麼事情?
3、安裝SkyEye
4、安裝arm-elf交叉編譯器
5、測試你的arm-elf-gcc編譯器
6、執行你的hello程序
7、編譯並運行uClinux-dist-20030909.tar.gz
8、加入網絡功能
9、安裝完成SkyEye後,下一步將做什麼?


1、什麼是SkyEye?

SkyEye是開源軟件的一個項目,SkyEye的目標是在Linux和Windows操作系統裡提供一個完全的仿真環境。SkyEye仿真環境相當於一個嵌入式計算機系統,你可以在SkyEye裡運行一些嵌入式Linux操作系統,如ARMLinux,uClinux,uc/OS-II(ucos-ii)等,並能分析和調試它們的源代碼。

如果你想知道關於SkyEye和嵌入式系統更詳細的信息,請訪問下面的站點:
www.SkyEye.org
http://www.skyeye.org/index_cn.html

通過SkyEye能仿真下面的硬件:
CPU核心:ARM7TDMI, ARM720T, ARM9, StrongARM, XScale
CPU: Atmel AT91/X40, Cirrus CIRRUS LOGIC EP7312, Intel SA1100/SA1110, Intel XScale PXA 250/255, CS89712, samsung 4510B, samsung 44B0(還不全)
內存: RAM, ROM, Flash
周邊設備: Timer, UART, ne2k網絡芯片, LCD, 觸摸屏等

目前能在SkyEye上運行下面的操作系統和系統軟件:
uC/OSII-2.5.x(支持網絡)
uClinux(基於Linux2.4.x內核, 支持網絡)
ARM Linux 2.4.x/2.6.x
lwIP on uC/OSII
基於uC/OSII, uClinux, ARM Linux的應用程序


2.SkyEye可以做什麼事情?
1. 通過SkyEye可以幫助促進嵌入式系統的學習,在不需要額外硬件的情況下學習和分析uclinux操作系統和其它嵌入式操作系統,如ucosII等。
2. SkyEye可用於嵌入式系統的教學。
3. 希望通過skyeye促進操作系統的研究,如ucosII,uclinux+RTAI,uclinux2.5.x等。
4. 可以基於SkyEye進行仿真特定硬件模塊的研究。
5. SkyEye可以作為嵌入式集成開發環境開發嵌入式系統(當然需要對SkyEye做大量的工作)。
註:引自陳渝《SkyEye Project FAQ》


3、安裝SkyEye

到http://gro.clinux.org/projects/skyeye/下載skyeye-0.7.0.tar.bz2包:

tar jxvf skyeye-v0.7.0.tar.bz2

進入解壓後的skyeye目錄,如果SkyEye的版本低於0.6.0,則運行下面的命令:

./configure --target=arm-elf --prefix=/usr/local --without-gtk-prefix --without-gtk-exec-prefix --disable-gtktest

如果SkyEye的版本高於0.6.0,則運行下面的命令:

./configure --target=arm-elf --prefix=/usr/local

接下來執行:

make
make install

安裝完成後執行skyeye

注意:
a.如果你使用的是Mandrake Linux發行版,那麼你在編譯SkyEye時遇到錯誤,並且錯誤與readline, ncurse, termcap等有關,你可以試試下面的方法:

ln -s /usr/include/ncurses/termcap.h /usr/local/include/termcap.h

接著再make和make install看能否成功!
b.如果你的Linux發行版是Debian Linux,那麼不要使用gcc 2.95或是gcc 3.0,請使用gcc 3.2+
c.gcc的版本要在2.96或以上
d.如果SkyEye的版本大於0.6.0,那麼使用LCD仿真需要在Linux系統裡安裝GTK軟件。


4、安裝arm-elf交叉編譯器

下載arm-elf-tools-20030314.sh
ftp://166.111.68.183/pub/embed/uclinux/soft/tools/arm
或到
ftp://166.111.8.229/OS/Embeded

執行:
chmod a+x arm-elf-tools-20030314.sh
然後:
./arm-elf-tools-20030314.sh

ls /usr/local/bin/

你應能看到以arm-elf開頭的可執行文件,其中arm-elf-gcc就是用來編譯你目標平台的編譯器的,當然還有一些小工具,後面將一一講來。


5、測試你的arm-elf-gcc編譯器

先寫一個小程序hello.c

#include int main(void) { int i; for(i = 0; i < 6; i++){ printf("i = %d ",i); printf("Hello, embedded linux!\n"); } return 0; }

然後執行:

arm-elf-gcc -Wl,-elf2flt -o hello hello.c

-elf2flt參數是將elf文件格式轉為flat文件格式,這個工具是在你安裝交叉編譯器產生的。

或者你可以寫個Makefile文件,執行:

make

這裡是我的Makefile文件,僅供參考:
# begin CC = arm-elf-gcc CFLAGS = -D__PIC__ -fpic -msingle-pic-base -O2 -pipe -Wall -g LDFLAGS = -Wl,-elf2flt LIBS = OBJS = hello.o all: hello hello: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o hello $(OBJS) $(LIBS) clean: rm -rf *.o *.elf *.gdb hello # end

如果編譯通過,就會產生hello可執行文件。用下面的命令:

file hello

你會發現,它是BFLT(binary FLAT),你目標平台所支持的文件格式。


6、執行你的hello程序

這裡,我們將借助genromfs這個小工具來完成測試,這個工具就是你在安裝交叉編譯器時產生的,你可以直接使用它。

到http://gro.clinux.org/projects/skye...0.4.tar.bz2包:

tar jxvf skyeye-binary-testutils-1.0.4.tar.bz2

cd testsuits/at91/uclinux2(當然你還可以用別的)

mkdir romfs(建一個目錄,後面用)

mount -o loop boot.rom /mnt/xxx

cp -r /mnt/xxx/* romfs

另外,把你編譯好的可執行程序拷貝到/romfs/bin目錄裡,這裡就是hello了!

genromfs -f boot.rom -d romfs/

註:可以用genromf -h來獲得幫助!

OK!執行下面的命令:

skyeye linux

(skyeye)target sim

(skyeye)load

(skyeye)run

kernel start.....

很熟悉了吧。。。

cd /bin

hello

可以看到結果了嗎?

其實到了這一步,你就可以開發自己的程序了!


7、編譯並運行uClinux-dist-20030909.tar.gz

到ftp://166.111.68.183/pub/embed/uclinux/soft/
或到ftp://166.111.8.229/OS/Embeded/uclinux/pub/uClinux/dist下載
uClinux-dist-20030909.tar.gz

假設把它下載到/usr/src/目錄下,然後依次執行下面的命令:

tar zxvf uClinux-dist-20030909.tar.gz
cd uClinux-dist/

在圖形方式下可用命令make xconfig

在命令行方式下用命令make menuconfig

vendor/product中選擇GDB/ARMulator
kernel版本選擇2.4
然後save and exit

運行下面這兩條命:
make dep
make

此時在/usr/src/uClinux-dist/linux-2.4.x目錄下會生成可執行文件linux
在/usr/src/uClinux-dist/images/會生成romfs.img等文件

在uClinux-dist目錄下建立仿真AT91的skyeye配置文件skyeye.conf,內容如下:
cpu: arm7tdmi
mach: at91
mem_bank: map=M, type=RW, addr=0x00000000, size=0x00004000
mem_bank: map=M, type=RW, addr=0x01000000, size=0x00400000
mem_bank: map=M, type=R, addr=0x01400000, size=0x00400000, file=images/romfs.img
mem_bank: map=M, type=RW, addr=0x02000000, size=0x00400000
mem_bank: map=M, type=RW, addr=0x02400000, size=0x00008000
mem_bank: map=M, type=RW, addr=0x04000000, size=0x00400000
mem_bank: map=I, type=RW, addr=0xf0000000, size=0x10000000


這個時候就可以用skyeye來調試運行kernel了,在/usr/src/uClinux-dist執行如下命令:

skyeye linux-2.4.x/linux

(skyeye)target sim

(skyeye)load

(skyeye)run

kernel start.....

注意:
要在skyeye.conf所在目錄下執行skyeye linux-2.4.x/linux


8、加入網絡功能
a.用root用戶進行操作。
b.你要看你的/lib/modules/'uname -r'/kernel/drivers/net/目錄裡有沒有tun.o
如果沒有的話你就需要編譯你的linux內核來獲得tun.o了。
c.(1)運行tun設備模塊:

#insmod /lib/modules/'uname -r'/kernel/drivers/net/tun.o

如果你沒有該設備,那你就要用下面的命令來創建它:

#mkdir /dev/net
#mknod /dev/net/tun c 10 200

(2)運行vnet(虛擬集線器)設備模塊(這一步不是必需的):
獲取vnet的源碼,然後創建設備:

#mknod /dev/net/vnet c 10 201
#chmod 666 /dev/net/vnet

創建vnet.o
#make vnet.o

插入模塊vnet.o
#insmod vnet.o

進入test目錄,用test來測度vnet.o
#cd test
#make
#./testvnet1

d.配置skyeye.conf文件

cpu: arm7tdmi
mach: at91
mem_bank: map=M, type=RW, addr=0x00000000, size=0x00004000
mem_bank: map=M, type=RW, addr=0x01000000, size=0x00400000
mem_bank: map=M, type=R, addr=0x01400000, size=0x00400000, file=images/romfs.img
mem_bank: map=M, type=RW, addr=0x02000000, size=0x00400000
mem_bank: map=M, type=RW, addr=0x02400000, size=0x00008000
mem_bank: map=M, type=RW, addr=0x04000000, size=0x00400000
mem_bank: map=I, type=RW, addr=0xf0000000, size=0x10000000
# format: state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd
net: state=on, mac=0:4:3:2:1:f, ethmod=tun, hostip=10.0.0.1


下面將對上面的一些參數作下說明:
state=on/off意思是仿真的NIC(網絡接口板)是有線的還是無線的;
mac=仿真適配器的MAC地址;
ethmod=tuntap/vnet在主機環境裡使用的虛擬設備;
hostip=意思是主機環境與keyeye交互用的IP
格式: state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd

For example:
#set nic info state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd
net: state=on, mac=0:4:3:2:1:f, ethmod=tun, hostip=10.0.0.1

net: state=on, mac=0:4:3:2:1:f, ethmod=vnet, hostip=10.0.0.1

注意:
如果你想在同一時刻運行兩個或更多的skyeye,那麼請為每一個skyeye使用不同的skyeye.conf

e.運行skyeye linux-2.4.x/linux

9、安裝完成SkyEye後,下一步將做什麼?

1、對於嵌入式操作系統的初學者和入門者和入門的學生而言,他們可以先看一些有關操作系統和嵌入式操作系統方面的教材和書籍,如與uC/OS、Minix、uClinux、Linux相關的書籍等。然後可以在Skyeye上開發一些簡單的應用程序例子(如進程間通信、進程優先級、死鎖情況、網絡應用等),對某些操作系統功能(如進程調度、內存管理、網絡子系統、文件子系統等)進行簡單的修改和擴展,並通過Skyeye進行運行和調試,看看會發生什麼情況。

2、對於有一定經驗的軟件工程師而言,在SkyEye上完成一定的應用系統原型開發是值得一做的事情。比如移植或開發一個文件子系統或網絡子系統到一個特定的操作系統中,相信比在一個真實的開發板上開發要容易一些。在Skyeye上進行一些操作系統的移植和開發(如移植RTLinux、RTAI等其它操作系統到Skyeye上)也是很有挑戰性的工作。

3、對於硬件工程師而言,對Skyeye進行擴充,設計新的硬件仿真(如USB、IDE硬盤等)使得Skyeye的硬件仿真功能更加強大,支持更多功能的軟件,是很有意義的事情。

參考:
SkyEye項目站點裡的一篇中文文檔;
陳渝《SkyEye Project FAQ》;
skyeye-0.7.0中的README文檔。

後記:
為了讓大家能快速上手,進行實際的開發工作,我趕湊了一篇文檔,很粗糙。但我堅信隨著更多的有經驗的人的加入;隨著我們自己水平的提高,一定會出現更多、更好的文章來。就讓我們快點行動起來吧!

最後,我再次建議大家看一下《嵌入式Linux技術與應用》這本書。

可以到http://www.skyeye.org/document.htm或是
ftp://166.111.68.183/pub/embed/skyeye/document/或是
http://www.huihoo.org/mirrors/skyeye/
下載文檔,可以獲得更多有關skyeye和嵌入式Linux開發的知識和經驗。

[技術] 製作 ARM9 的 Bootstrap Root Filesystem

本文出處: www.jollen.org
已取得原作者授權使用

《Jollen的Root Filesystem建置技術系列》

製作ARM9的Bootstrap Root Filesystem

作者/陳俊宏

http://www.jollen.org

更新日期:2007/1/23

在「完整註明出處」的前提下(註明方式說明),您能立即擁有轉貼與引用的授權,且毋需知會作者。

目的

製作 bootstrap root filesystem(base root filesystem)以提供一個最簡單、陽春且可開機的環境;製作完成的系統可開機到shell模式,並可使用 busybox 提供的指令。

準備工作

首先,您必須準備一台 host 開發環境,並安裝好 cross toolchain;接著,由於本文是做實機測試,因此,如果您沒有 ARM9 開發板,可以考慮使用 Qemu 來做模擬測試。

以下的操作示範,只節錄重點指令片段,您可能必須根據自己的整體實作流程,來微調指令的順序,或是參數等。

Step 1:建立工作目錄

建立一個專用的工作目錄,命名為 arm9.so-busybox/:

# mkdir arm9.so-busybox/

# cd arm9.so-busybox/

接著在 arm9.so-busybox/ 目錄下建立 4 個子目錄:

# mkdir src/ install/ mnt/ pub/ build/

實際進行 root filesystem 實作時,我們應該養成將檔案分類擺放的好習慣。以本專案為例,build/ 目錄用來編譯程式,src/ 目錄用來存放原始程式碼,install/ 目錄則用來擺放我們最後的 root filesystem。

Step 2:建立目錄架構

根據 FHS 的目錄架構標準,在 root filesystem 目錄下(install/)建立目錄階層架構:

# cd install/

# mkdir bin/ dev/ etc/ mnt/ proc/ sbin/ usr/

另外還有二個必要的目錄:/var 與 /tmp,由於這二個目錄都需要具備寫入權限,所以在這裡我們是以 ramdisk 的做法來 mount 這二個目錄。

Step 3:建立裝置檔

在 root filesystem 的 dev/ 目錄下建立必要的裝置檔:

crw------- 1 root root 5, 1 1月 1 1970 console

crw------- 1 root root 29, 0 1月 1 1970 fb0

crw------- 1 root root 1, 3 1月 1 1970 null

brw------- 1 root root 1, 0 1月 1 1970 ram0

crw------- 1 root root 5, 0 1月 1 1970 tty

crw------- 1 root root 4, 0 1月 1 1970 tty0

此階段使用 mknod 指令來完成。請先切換到 root filesystem 的 dev/ 目錄下,接著執行以下指令:

# mknod console c 5 1

# mknod fb0 c 29 0

# mknod null c 1 3

# mknod ram0 1 0

# mknod tty c 5 0

# mknod tty0 c 4 0

對於需要產生大量 device file 的場合來說,可以改用 genext2fs 的 ‘-D’ 參數來製作。詳見 Jollen’s Blog:[使用 genext2fs 的 '-D'(device file table)來建立 root filesystem]。

Step 4:加入Busybox

編譯並安裝 Busybox(動態程式庫方式)。將取得的Busybox原始碼解壓縮至 project 目錄裡的 src/ 子目錄下,以下是幾個注意事項:

本教學文件使用 Busybox 1.3.1

Busybox 1.3.0 開始,使用 Linux Kernel 的 Makefile(因為開始支援 CONFIG_DESKTOP)。Cross compile 時,需要修改 Makefile 如下:

ARCH ?= arm

CROSS_COMPILE ?= /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-

CROSS_COMPILE 的設定是 cross toolchain 的「PREFIX」,視您的 toolchain 而定。您可由 http://www.jollen.org/kit/ 下載本文所使用的 GCC 3.4.1 ARM9 toolchain,以使用與本文完全相同的修改。

Busybox 整合了常用的指令與工具,我們可以設定 Busybox,以勾選我們需要的功能選項。進入 Busybox 的設定選單:

# make menuconfig

請注意,init 與 shell 是必選的項目,請檢查是否有勾選這二個功能。同時,也別忘了設定 Busybox 的安裝路徑,將安裝路徑指到我們 root filesystem 目錄下。

接著直接進行編譯(cross compile):

# make

編譯完成後,將 Busybox 安裝至我們的 root filesystem 目錄(即 Step 2 的 install/ 目錄):

# make install

此時,您應該可以在 root filesystem 目錄下看到 Busybox 所安裝的檔案。

Step 5:加入動態程式庫

編譯完成的 Busybox 已經是給 ARM9 執行的格式了,但我們的編譯設定是將Busybox 編譯成 shared library 架構,因此 Busybox 執行時需要以下的檔案:

˙ libc.so.6:C library標準程式庫。

˙ ld-linux.so.2:Native dynamic loader。

請由 toolchain 將以上二個檔案複製至 root filesystem 的 lib/ 目錄下:

# cd ../../install (切換至root filesystem根目錄)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/ld-linux.so.2 lib/ (複制native dynamic loader。以上命令請勿斷行)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/libc.so.6 lib/ (複製C library。以上命令請勿斷行)

Busybox 會因版本與功能選項設定的差異,而需要更多的程式庫。請使用 cross toolchain 的 objdump 指令來檢查 Busybox 的程式庫相依問題(無法使用 ldd 指令),並將所需的程式庫由 toolchain 複製到 root filesystem 的 lib/ 目錄下。

Step 6:加入系統檔案

加入2個重要的系統檔案於 etc/ 目錄下:

˙ fstab:mount table。

˙ inittab:系統初始表(init table)。

etc/fstab內容如下:

/dev/ram0 / ext2 defaults 1 1

none /proc proc defaults 0 0

/dev/ram1 /tmp ramfs defaults 0 0

/dev/ram2 /var ramfs defaults 0 0

fstab 第一行設定,目的在將 /dev/ram0 重新附掛成 ‘/’(root),此動作用意在於重新指定 ‘/’ 的檔案系統為 ext2。最後二行的目的是為了以 ramfs 來 mount 重要的二個目錄:/var 與 /tmp;如此一來,就算開機沒做 remount root(詳見後文說明),也能對 /tmp 與 /var 目錄做寫入的動作

etc/inittab內容如下:

:0:sysinit:/etc/rc.d/rc.init

:0:respawn:/bin/sh

根據這個 inittab 設定,當系統開機後便會進入 run level 0,在 run level 0 模式下,init process會執行2個動作:(1) 執行 /etc/rc.d/rc.init,此即「init script」;(2) 執行 /bin/sh,即進入 shell 模式。

在此我們並沒有參照 LSB 的標準來設定 run level,而且也沒有使用 getty 來讓使用者登入(多使用者模式)。

Step 7:編寫 Initial Script

根據 inittab 的設定,我們 root filesystem 的 init script 位於 /etc/rc.d/rc.init。以下提供一個供 Embedded Linux 使用的 init script 範本:

#!/bin/sh

# automount (/etc/fstab)

mount -a

# remount root

mount -o remount rw /

#

mkdir /var/lock

mkdir /var/lock/subsys

mkdir /var/run



# start other applications (Running application automatically during

# booting up.

# eg. /bin/thttpd –p 80 –d /var/www

當我們執行「mount –a」後,mount 便會去讀取前一步驟所設定的 fstab,並根據此表格的內容來做 mount 的動作。另外,這裡有一個 remount 的動作:

# mount -o remount rw /

此動作的目的是將 root(’/’)重新 mount 成可讀寫,此動作是選擇性的,若省略不做,請務必保持 /var 與 /tmp 目錄是能寫入的(建議以 ramdisk 方式實作為佳)。

若 root filesystem 未包含 inittab 設定檔,則 Busybox 會使用以下的內建設定:

::sysinit:/etc/init.d/rcS

::askfirst:/bin/sh

::ctrlaltdel:/sbin/reboot

::shutdown:/sbin/swapoff -a

::shutdown:/bin/umount -a -r

::restart:/sbin/init

不過,還是建議編寫自己的 inittab 設定檔。

Step 8:製作 Root Filesystem 映像檔(Image File)

截至目前為止,我們的檔案系統已經擁有基本的系統指令與工具。接下來,我們即可將建置完成的 root filesystem 製作成 ext2 格式的映像檔。

以下提供二種 ext2fs image file 的製作方式:(1) 土方法;(2) 使用 genext2fs 工具。

先說明傳統的土方法。首先,先利用dd指令做出一個空白的檔案,大小為 4M(bytes):

# dd if=/dev/zero of=ext2fs bs=1k count=4096

我們將檔案命名為 ext2fs,接著再將 ext2fs 製作成 ext2 格式的檔案系統:

# mkfs.ext2 ext2fs

mke2fs 1.32(09-Nov-2002)

ext2fs is not a block special device.

Proceed anyway?(y,n)y

選擇y後出現以下畫面:

Filesystem label=

OS type: Linux

Block size=1024(log=0)

Fragment size=1024(log=0)

128 inodes, 1024 blocks

51 blocks(4.98%)reserved for the super user

First data block=1

1 block group

8192 blocks per group, 8192 fragments per group

128 inodes per group



Writing inode tables: done

Writing superblocks and filesystem accounting information: done



This filesystem will be automatically checked every 26 mounts or

180 days, whichever comes first. Use tune2fs -c or -i to override.

到這裡我們已經做好一個檔案格式為 ext2 的空白映像檔,再來只要將先前做好的 root filesystem 全部複製到 ext2fs 映像檔「裡面」即可。

先將 ext2fs 附掛至任一空目錄,例如 mnt/:

# mkdir mnt/

# mount -t ext2 -o loop ext2fs mnt/ (指定檔案系統為 ext2)

複製檔案系統時,我們不使用 cp 指令,而是利用 tar 來完成:

# cd install/

# tar cz * > ../install.tar.gz (將檔案系統做成tarball,同時也備份 root filesystem。)

# cd ..

# cd mnt/

# tar zxvf ../install.tar.gz (再將tarball解至映像檔)

接著將映像檔 umount 並壓縮即可:

# cd ..

# umount mnt/

# gzip -9c ext2fs > pub/ext2fs.gz

最後得到的 ext2fs.gz 即是完成品。請注意,若不使用 tar 來說,也應該使用 cpio 來複製檔案,避免使用 cp 指令。

使用 genext2fs

genext2fs 是一個 ext2 filesystem image file 的製作工具,可以讓我們很方便地將 root filesystem 製作成 image 檔。請由 genext2fs 的官方網站下載原始碼套件:

http://genext2fs.sourceforge.net/

編譯後可以取得 genext2fs 檔案,以下是將 install/ 目錄製作成 ext2fs image 檔的指令:

# genext2fs -b 8192 -i 1024 -d install/ ext2fs

執行後,會得到檔名為 ext2fs 的 image 檔,大小為 8 MB(透過 ‘-b’ 參數指定 image file 大小);接著同樣再用 gzip 將 ext2fs 檔壓縮即可。

Step 9:在 Target 端做測試

本步驟以 Jollen-Kit! 為例,Jollen-Kit! 是由 www.jollen.org 所推出的 ARM9 training board,詳細介紹請參考 [http://www.jollen.org/kit/]。請注意,本階段的操作,視 target device 的不同而不同,因此以下示範只適用於 Jollen-Kit! 或是其他的 SMDK2410 平臺。

步驟 8 所得到的 ext2.gz 必須再包裝成 U-Boot 的格式,才能透過 U-Boot 載入到 RAM,以成為 kernel 的 initial ramdisk(initrd):

# mkimage -A arm -O linux -T ramdisk -C none -a 0x30800000 -e 0x30800000 -n ramdisk -d ext2fs.gz urootfs.img

執行後可得到 urootfs.img 檔案,在測試階段為了方便起見,我們可以直接將 urootfs.img 載到 RAM 做測試;U-Boot 指令如下:

jollen.org # tftpboot 32000000 urootfs.img; tftpboot 30F00000 uimage.img; bootm 30F00000 32000000

urootfs.img 是我們製作的 root filesystem,uimage.img 則是給 Jollen-Kit! 使用的 Linux kernel(pre-built)。

嵌入式Linux文件系统的构建

http://www.sinklow.com/blog/article.asp?id=159
busybox的配置及编译
#make menuconfig
配置参数
#make

文件结构
/dev -- 里面存放设备档 (Devices) ,为达成 I/O 工作所需
/proc -- Directory stub required by the proc filesystem
/etc -- 里面存放系统组态设定档
/sbin -- 重要的 (critical) 系统二进位执行档 (binaries)
/bin -- 被认为是系统一部分的基本二进位执行档
/lib -- 提供 run-time 支持的共享函式库
/mnt -- 维护其它磁盘所用的磁盘挂入点 (mount point)
/usr -- 额外的工具程序与应用程序

文件说明
/bin /sbin 已经由busybox构建
/proc /mnt /usr 三个目录为空
/lib 将所需要的库都复制进来,可用ldd命令进行查看
/etc /dev 比较复杂,可参考《Building a root filesystem》

最后用mkfs.jffs2做成JFFS2文件系统映像
#mkdir jffsfile
#cd jffsfile

/* copy all the /bin, /etc, /usr/bin, /sbin/ binaries and /dev entries that are needed for the filesystem here */

/* Type the following command under jffsfile directory to create the JFFS2 Image */

#./mkfs.jffs2 -e 0x40000 -p -o ../jffs.image
Quote
以下内容转载自《Building a root filesystem》

/dev
/dev 目錄包含一群特別的檔案,這些檔案是給系統上所有設備使用的,這樣的 /dev 目錄每個 Linux 系統都一定會有。這個目錄本身是一個普通目錄,可以以一般的方法用 mkdir 造出來。然而,這些特別的檔案必須以特別的方法用 mknod 指令造出來。

但還是有一條捷徑 -- 直接 copy 你現有 /dev 目錄的內容,然後再清除你不想要的設備檔。唯一的要求是 copy 這些特別的設備檔時,要用 -R 選項。這個選項會 copy 整個目錄中的檔案,但是不會 copy 這些檔案的內容。請確定使用 大寫字母 R 。這個指令是:

cp -dpR /dev /mnt

在此我們假設磁片是被掛在 /mnt 底下。 dp 選項 (switches) 確保 symbolic links 是以 links 的方式來 copy ,而不是 copy 鏈結檔所指向的 target file ,同時原本的檔案屬性也被保留,因此保留了檔案的所有權資訊。

如果你想要用高難度技巧完成這個任務,就利用 ls -l 列出你想要的設備檔之 major 與 minor device numbers ,然後再用 mknod 在磁片上造出它們。

無論如何 copy 這些設備檔,還是要檢查任何你所需之設備檔 (device special file) 是否已放入這張救援磁片中。舉例來說, ftape 使用磁帶設備,如果你想要從 bootdisk 存取軟式磁帶機,你就需要 copy 所有有關的設備檔。

請注意,每一個設備檔需要一個 inode ,但 inodes 一直都是稀少的資源,特別是在磁片 filesystems 上。因此,從磁片上的 /dev 目錄移除任何你所不需要的設備檔是有意義的。舉例來說,如果你沒有 SCSI 磁碟,你可以放心地移除所有以 sd 開頭的設備檔。同樣地,如果你並不想使用你的序列埠 (serial port) ,那麼你也可以移除所有以 cua 開頭的設備檔。

請確定從這個目錄放入了以下檔案的: console, kmem, mem, null, ram0 and tty1.

/etc
這個目錄包含了重要的組態設定檔。在大部分的系統上,這些檔案被分為三個群組:

一直都是必備的, e.g. rc, fstab, passwd 。
可能是必備的,但是沒有人能十分確定。
偷跑進來的垃報。
通常可以用以下指令識出哪些是非基本的檔案:

ls -ltru

這個指令將檔案依據上次被存取的日期,以先早後晚 (reverse) 的順序列出,所以如果有任何檔案不會被存取,那麼它們就可以從 root 磁片中刪去。

在我的 root 磁片上,我的組態檔數目已減至 15 個。這可減少我處理以下三種檔案的工作:

我必須為 boot/root 系統進行組態設定的檔案:
rc.d/* -- 系統啟動與改變 run level 的 scripts
fstab -- 要被掛上的 file systems 清單
inittab -- 給 init process 的參數,於開機時啟動的第一個 process 。
我們應該為 boot/root 系統整理的檔案:
passwd -- 重要的使用者、 home 目錄等其它項目的清單。
group -- 使用者群組。
shadow -- 使用者的密碼。你可能沒有這個檔。
termcap -- the terminal capability database.
如果系統安全 (security) 對你很重要,那麼 passwd 與 shadow 應該被刪減,以避免將使用者密碼 copy 出系統,這樣當你從磁片開機時,不想要的 logins 會被拒絕。 請確定 passwd 至少包含了 root 。如果你要讓其他的使用者 login ,請確定他們的 home 目錄與 shells 都存在。 termcap ,終端機資料庫,一般而言有幾百個 kilobytes 。你 boot/root 磁片的版本應該被刪減到只包含你所使用的終端機,這通常就是 linux 或 linux-console 項目 (entry) 。
The rest. They work at the moment, so I leave them alone.
Out of this, 我實際上只必須設定兩個檔,而它們所應包含的項目驚人地少。

rc 應該包含:
#!/bin/sh
/bin/mount -av
/bin/hostname Kangaroo

請確定上述的目錄都是正確的。你並不需要真地去執行 hostname -- 如果你執行只是讓系統比較好看 (譯註:如此系統會有個名字) 。
fstab 應該至少要包含:
/dev/ram0 / ext2 defaults
/dev/fd0 / ext2 defaults
/proc /proc proc defaults

你可以從你現存的 fstab copy 你想要的項目,但是你並不應該自動地掛上你硬碟任何的 partitions ;請對這些項目使用 noauto 關鍵字 (譯註:用 noauto 代替 default ) 。當使用 bootdisk 時,你的硬碟可能是早已損壞或掛了。
你的 inittab 應該被改變,以使其中 sysinit 這行能執行 rc 或無論什麼將被執行的基本開機 script 。同時,如果你想要確保不可從序列埠 login ,請在所有行尾包括 ttys 或 ttyS 的 getty 項目前加上「#」符號 (comment out) 。請保留 tty 埠以讓你可以在 console 前 login 。

一個最起碼的 inittab 檔看起來樣這樣:

id:2:initdefault:
si::sysinit:/etc/rc
1:2345:respawn:/sbin/getty 9600 tty1
2:23:respawn:/sbin/getty 9600 tty2

inittab 檔定義了系統在各種不同的情況中將執行什麼項目,包括 startup 、切換至多使用者模式等等。請仔細地檢查在 inittab 中被提及的檔案名稱 (filenames) ;如果 init 不能找到所提及的程式,那麼 bootdisk 將會停止運作,而你甚至不會得到錯誤訊息。

請注意,某些程式不能被移到其它地方,因為其它程式已在撰寫時,就把它們的檔案位置寫死了 (hardcode) 。舉例來說在我的系統上, /etc/shutdown 已把 /etc/reboot 的位置寫死在其中。如果我移動了 reboot 到 /bin/reboot,然後下達一個 shutdown 指令,將會因為找不到 reboot 檔而發生錯誤。

剩下來的,就是 copy 在你 /etc 目錄中的所有文字檔 (text files) ,再加上在你 /etc 目錄中,你無法確定你需不需要的所有可執行檔。需要指示 (guide) 者,請參考在 Sample roodisk directory listings 的樣本清單。也許只要 copy 這些檔案就足夠了,但是系統差異會有很大的影響,所以你無法確定你系統上相同的檔案組合,就一定等於清單中的檔案。唯一確定的方法就是從 inittab 著手,並找出需要什麼。

現在大部分的系統使用 /etc/rc.d/ 目錄,其中包含給不同 run levels 的shell scripts 。最起碼會有一個單一的 rc script,但是僅從你現存的系統 copy inittab 與 /etc/rc.d 這兩個目錄,然後刪減 rc.d 目錄中的 shell scripts 以移除和磁片系統環境無關的 processing ,會是較為簡單的做法。

打造自己的 Floppy Linux

http://cha.homeip.net/blog/archives/2006/07/_floppy_linux.html

目的: 把 Linux 塞進 1.44MB 磁片中, 以該磁片開機後能透過網卡連上網路


環境:





建構 Linux kernel



cd /usr/src/linux


make mrproper


make menuconfig



下列項目以 kernel 2.4.32 為例, 不同版本的 kernel menu 排列順序不盡相同


取消以下項目 (按 'n')



Loadable module support:



Enable loadable module support



(以及任何你不想編入 kernel 的項目, 如: Sound Card,
PCMCIA...)



選擇以下項目 (按 'y')



Block devices:



Normal floppy disk support
RAM disk support
Initial RAM disk (initrd) support



File systems:



DOS FAT fs support

/proc file system support
Second extended fs support



Console drivers:



VGA text console



General setup:



Power Management support

ACPI Support



Networking options:



Packet socket

TCP/IP networking



Network device support →



Ethernet (10 or 100Mbit) →



EISA, VLB, PCI and on board controllers →



RealTek RTL-8139 PCI Fast Ethernet Adapter support



(以及其他任何你想編入 kernel 的項目)




make dep


make bzImage



建立 Busybox 工具指令集



cd /usr/local/src


wget
http://www.busybox.net/downloads/busybox-snapshot.tar.bz2


tar jxf busybox-snapshot.tar.bz2


cd busybox


vi ./init/init.c



#define INIT_SCRIPT "/etc/init.d/rcS" → "/etc/rc.d/rc.sysinit"



make menuconfig



選擇以下項目 (按 'y')



Busybox Settings → Build Options → Build BusyBox as a static binary


Init Utilities → init


Shells → Choose your default shell → ash


(以及任何想使用的指令, 如: ls, clear, telnet...)




make


make install



建立目錄環境



mkdir /floppy-linux


cd /floppy-linux


mkdir -p dev etc etc/rc.d proc mnt tmp var


chmod 755 dev etc etc/rc.d mnt tmp var


chmod 555 proc


vi ./etc/inittab



::sysinit:/etc/rc.d/rc.sysinit

::askfirst:/bin/sh



vi ./etc/fstab



proc /proc proc
defaults 0 0



vi ./etc/rc.d/rc.sysinit



#!/bin/sh


mount -a



chmod 644 ./etc/inittab ./etc/fstab


chmod 755 ./etc/rc.d/rc.sysinit


cd dev


mknod null c 1 3


mknod urandom c 1 9


mknod tty1 c 4 1


mknod tty5 c 4 5


mknod console c 5 1


mknod ram0 b 1 0


mknod fd0 b 2 0


chmod 666 *


chmod 600 fd0 ram0


cp -Rf /usr/local/src/busybox/_install/* /floppy-linux
把剛剛做好的 Busybox 工具程式環境複製進來


建立 udhcpc 環境 (讓 udhcpc
指令取得動態分配 IP)



wget

http://udhcp.busybox.net/downloads/snapshots/udhcp-snapshot.tar.bz2


tar jxf udhcp-snapshot.tar.bz2


mkdir -p /floppy-linux/usr/share/udhcpc


cp ./udhcp/samples/simple.script /floppy-linux/usr/share/udhcpc/default.script


chmod +x /floppy-linux/usr/share/udhcpc/default.script



如欲使用網路功能, 須在 kernel 加入網路設定與網卡 driver
(Networking options & Network device support), 並在 Busybox 選用網路指令



製作 ram disk image



mkdir /tmp/floppy-linux


vi /flimg_maker.sh



#!/bin/sh


dd if=/dev/zero of=/tmp/tmp_loop bs=1k count=2048

losetup /dev/loop0 /tmp/tmp_loop

mke2fs -m 0 /dev/loop0

mount -t ext2 /dev/loop0 /mnt

cp -a /floppy-linux/* /mnt

umount /mnt

losetup -d /dev/loop0

dd if=/tmp/tmp_loop gzip -9 > /tmp/floppy-linux/initrd.gz

rm -f /tmp/tmp_loop

sync



chmod +x /flimg_maker.sh


/flimg_maker.sh


日後若 /floppy-linux 目錄內容有所變更時, 只要執行 /flimg_maker.sh
即可重新製作 initrd.gz



整合所需檔案, 並檢查容量是否小於 1414 Blocks



cp /usr/src/linux/arch/i386/boot/bzImage /tmp/floppy-linux/linux


cp /tmp/initrd.gz /tmp/floppy-linux


du /tmp/floppy-linux


如果數字大於 1414 就要重新 make kernel, 儘量減少編入 kernel 的項目


可在 syslinux /dev/fd0 後, mount /dev/fd0 /mnt && df 即可查出磁片可用空間 (Available)
有多少 block


至少應預留 1 block 的空間給待會要建立的 syslinux.cfg



編輯 syslinux.cfg



vi /tmp/floppy-linux/syslinux.cfg



DEFAULT Linux

LABEL Linux

KERNEL linux

APPEND root=/dev/ram0 rw initrd=initrd.gz




將 syslinux boot loader 與剛剛完成的檔案載入磁片中



mkfs -t msdos /dev/fd0


syslinux /dev/fd0


mount /dev/fd0 /mnt


cp /tmp/floppy-linux/* /mnt


sync


完成後, 以該磁片開機即可





參考資料:



其他備忘:



Common block device





















































DeviceMajorMinor
hda30
hdb364
hdc220
hdd2264
sda80
sdb816
sdc832
sdd848
scd0110

ex. mknod /dev/hda1 b 3 1

製作ARM9的Bootstrap Root Filesystem

作者/陳俊宏
http://www.jollen.org
更新日期:2007/1/23
在「完整註明出處」的前提下(註明方式說明),您能立即擁有轉貼與引用的授權,且毋需知會作者。
目的
製作 bootstrap root filesystem(base root filesystem)以提供一個最簡單、陽春且可開機的環境;製作完成的系統可開機到shell模式,並可使用 busybox 提供的指令。
準備工作
首先,您必須準備一台 host 開發環境,並安裝好 cross toolchain;接著,由於本文是做實機測試,因此,如果您沒有 ARM9 開發板,可以考慮使用 Qemu 來做模擬測試。
以下的操作示範,只節錄重點指令片段,您可能必須根據自己的整體實作流程,來微調指令的順序,或是參數等。
Step 1:建立工作目錄
建立一個專用的工作目錄,命名為 arm9.so-busybox/:
# mkdir arm9.so-busybox/
# cd arm9.so-busybox/
接著在 arm9.so-busybox/ 目錄下建立 4 個子目錄:
# mkdir src/ install/ mnt/ pub/ build/
實際進行 root filesystem 實作時,我們應該養成將檔案分類擺放的好習慣。以本專案為例,build/ 目錄用來編譯程式,src/ 目錄用來存放原始程式碼,install/ 目錄則用來擺放我們最後的 root filesystem。
Step 2:建立目錄架構
根據 FHS 的目錄架構標準,在 root filesystem 目錄下(install/)建立目錄階層架構:
# cd install/
# mkdir bin/ dev/ etc/ mnt/ proc/ sbin/ usr/
另外還有二個必要的目錄:/var 與 /tmp,由於這二個目錄都需要具備寫入權限,所以在這裡我們是以 ramdisk 的做法來 mount 這二個目錄。
Step 3:建立裝置檔
在 root filesystem 的 dev/ 目錄下建立必要的裝置檔:
crw------- 1 root root 5, 1 1月 1 1970 console
crw------- 1 root root 29, 0 1月 1 1970 fb0
crw------- 1 root root 1, 3 1月 1 1970 null
brw------- 1 root root 1, 0 1月 1 1970 ram0
crw------- 1 root root 5, 0 1月 1 1970 tty
crw------- 1 root root 4, 0 1月 1 1970 tty0
此階段使用 mknod 指令來完成。請先切換到 root filesystem 的 dev/ 目錄下,接著執行以下指令:
# mknod console c 5 1
# mknod fb0 c 29 0
# mknod null c 1 3
# mknod ram0 1 0
# mknod tty c 5 0
# mknod tty0 c 4 0
對於需要產生大量 device file 的場合來說,可以改用 genext2fs 的 ‘-D’ 參數來製作。詳見 Jollen’s Blog:[使用 genext2fs 的 '-D'(device file table)來建立 root filesystem]。
Step 4:加入Busybox
編譯並安裝 Busybox(動態程式庫方式)。將取得的Busybox原始碼解壓縮至 project 目錄裡的 src/ 子目錄下,以下是幾個注意事項:
本教學文件使用 Busybox 1.3.1
Busybox 1.3.0 開始,使用 Linux Kernel 的 Makefile(因為開始支援 CONFIG_DESKTOP)。Cross compile 時,需要修改 Makefile 如下:
ARCH ?= arm
CROSS_COMPILE ?= /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-
CROSS_COMPILE 的設定是 cross toolchain 的「PREFIX」,視您的 toolchain 而定。您可由 http://www.jollen.org/kit/ 下載本文所使用的 GCC 3.4.1 ARM9 toolchain,以使用與本文完全相同的修改。
Busybox 整合了常用的指令與工具,我們可以設定 Busybox,以勾選我們需要的功能選項。進入 Busybox 的設定選單:
# make menuconfig
請注意,init 與 shell 是必選的項目,請檢查是否有勾選這二個功能。同時,也別忘了設定 Busybox 的安裝路徑,將安裝路徑指到我們 root filesystem 目錄下。
接著直接進行編譯(cross compile):
# make
編譯完成後,將 Busybox 安裝至我們的 root filesystem 目錄(即 Step 2 的 install/ 目錄):
# make install
此時,您應該可以在 root filesystem 目錄下看到 Busybox 所安裝的檔案。
Step 5:加入動態程式庫
編譯完成的 Busybox 已經是給 ARM9 執行的格式了,但我們的編譯設定是將Busybox 編譯成 shared library 架構,因此 Busybox 執行時需要以下的檔案:
˙ libc.so.6:C library標準程式庫。
˙ ld-linux.so.2:Native dynamic loader。
請由 toolchain 將以上二個檔案複製至 root filesystem 的 lib/ 目錄下:
# cd ../../install (切換至root filesystem根目錄)
# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/ld-linux.so.2 lib/ (複制native dynamic loader。以上命令請勿斷行)
# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/libc.so.6 lib/ (複製C library。以上命令請勿斷行)
Busybox 會因版本與功能選項設定的差異,而需要更多的程式庫。請使用 cross toolchain 的 objdump 指令來檢查 Busybox 的程式庫相依問題(無法使用 ldd 指令),並將所需的程式庫由 toolchain 複製到 root filesystem 的 lib/ 目錄下。
Step 6:加入系統檔案
加入2個重要的系統檔案於 etc/ 目錄下:
˙ fstab:mount table。
˙ inittab:系統初始表(init table)。
etc/fstab內容如下:
/dev/ram0 / ext2 defaults 1 1
none /proc proc defaults 0 0
/dev/ram1 /tmp ramfs defaults 0 0
/dev/ram2 /var ramfs defaults 0 0
fstab 第一行設定,目的在將 /dev/ram0 重新附掛成 ‘/’(root),此動作用意在於重新指定 ‘/’ 的檔案系統為 ext2。最後二行的目的是為了以 ramfs 來 mount 重要的二個目錄:/var 與 /tmp;如此一來,就算開機沒做 remount root(詳見後文說明),也能對 /tmp 與 /var 目錄做寫入的動作
etc/inittab內容如下:
:0:sysinit:/etc/rc.d/rc.init
:0:respawn:/bin/sh
根據這個 inittab 設定,當系統開機後便會進入 run level 0,在 run level 0 模式下,init process會執行2個動作:(1) 執行 /etc/rc.d/rc.init,此即「init script」;(2) 執行 /bin/sh,即進入 shell 模式。
在此我們並沒有參照 LSB 的標準來設定 run level,而且也沒有使用 getty 來讓使用者登入(多使用者模式)。
Step 7:編寫 Initial Script
根據 inittab 的設定,我們 root filesystem 的 init script 位於 /etc/rc.d/rc.init。以下提供一個供 Embedded Linux 使用的 init script 範本:
#!/bin/sh
# automount (/etc/fstab)
mount -a
# remount root
mount -o remount rw /
#
mkdir /var/lock
mkdir /var/lock/subsys
mkdir /var/run

# start other applications (Running application automatically during
# booting up.
# eg. /bin/thttpd –p 80 –d /var/www
當我們執行「mount –a」後,mount 便會去讀取前一步驟所設定的 fstab,並根據此表格的內容來做 mount 的動作。另外,這裡有一個 remount 的動作:
# mount -o remount rw /
此動作的目的是將 root(’/’)重新 mount 成可讀寫,此動作是選擇性的,若省略不做,請務必保持 /var 與 /tmp 目錄是能寫入的(建議以 ramdisk 方式實作為佳)。
若 root filesystem 未包含 inittab 設定檔,則 Busybox 會使用以下的內建設定:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
不過,還是建議編寫自己的 inittab 設定檔。
Step 8:製作 Root Filesystem 映像檔(Image File)
截至目前為止,我們的檔案系統已經擁有基本的系統指令與工具。接下來,我們即可將建置完成的 root filesystem 製作成 ext2 格式的映像檔。
以下提供二種 ext2fs image file 的製作方式:(1) 土方法;(2) 使用 genext2fs 工具。
先說明傳統的土方法。首先,先利用dd指令做出一個空白的檔案,大小為 4M(bytes):
# dd if=/dev/zero of=ext2fs bs=1k count=4096
我們將檔案命名為 ext2fs,接著再將 ext2fs 製作成 ext2 格式的檔案系統:
# mkfs.ext2 ext2fs
mke2fs 1.32(09-Nov-2002)
ext2fs is not a block special device.
Proceed anyway?(y,n)y
選擇y後出現以下畫面:
Filesystem label=
OS type: Linux
Block size=1024(log=0)
Fragment size=1024(log=0)
128 inodes, 1024 blocks
51 blocks(4.98%)reserved for the super user
First data block=1
1 block group
8192 blocks per group, 8192 fragments per group
128 inodes per group

Writing inode tables: done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 26 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
到這裡我們已經做好一個檔案格式為 ext2 的空白映像檔,再來只要將先前做好的 root filesystem 全部複製到 ext2fs 映像檔「裡面」即可。
先將 ext2fs 附掛至任一空目錄,例如 mnt/:
# mkdir mnt/
# mount -t ext2 -o loop ext2fs mnt/ (指定檔案系統為 ext2)
複製檔案系統時,我們不使用 cp 指令,而是利用 tar 來完成:
# cd install/
# tar cz * > ../install.tar.gz (將檔案系統做成tarball,同時也備份 root filesystem。)
# cd ..
# cd mnt/
# tar zxvf ../install.tar.gz (再將tarball解至映像檔)
接著將映像檔 umount 並壓縮即可:
# cd ..
# umount mnt/
# gzip -9c ext2fs > pub/ext2fs.gz
最後得到的 ext2fs.gz 即是完成品。請注意,若不使用 tar 來說,也應該使用 cpio 來複製檔案,避免使用 cp 指令。
使用 genext2fs
genext2fs 是一個 ext2 filesystem image file 的製作工具,可以讓我們很方便地將 root filesystem 製作成 image 檔。請由 genext2fs 的官方網站下載原始碼套件:
http://genext2fs.sourceforge.net/
編譯後可以取得 genext2fs 檔案,以下是將 install/ 目錄製作成 ext2fs image 檔的指令:
# genext2fs -b 8192 -i 1024 -d install/ ext2fs
執行後,會得到檔名為 ext2fs 的 image 檔,大小為 8 MB(透過 ‘-b’ 參數指定 image file 大小);接著同樣再用 gzip 將 ext2fs 檔壓縮即可。
Step 9:在 Target 端做測試
本步驟以 Jollen-Kit! 為例,Jollen-Kit! 是由 www.jollen.org 所推出的 ARM9 training board,詳細介紹請參考 [http://www.jollen.org/kit/]。請注意,本階段的操作,視 target device 的不同而不同,因此以下示範只適用於 Jollen-Kit! 或是其他的 SMDK2410 平臺。
步驟 8 所得到的 ext2.gz 必須再包裝成 U-Boot 的格式,才能透過 U-Boot 載入到 RAM,以成為 kernel 的 initial ramdisk(initrd):
# mkimage -A arm -O linux -T ramdisk -C none -a 0x30800000 -e 0x30800000 -n ramdisk -d ext2fs.gz urootfs.img
執行後可得到 urootfs.img 檔案,在測試階段為了方便起見,我們可以直接將 urootfs.img 載到 RAM 做測試;U-Boot 指令如下:
jollen.org # tftpboot 32000000 urootfs.img; tftpboot 30F00000 uimage.img; bootm 30F00000 32000000
urootfs.img 是我們製作的 root filesystem,uimage.img 則是給 Jollen-Kit! 使用的 Linux kernel(pre-built)。