精通initramfs構建step by step

(一)hello world 
1、initramfs是什麼 
在2.6版本的linux內核中,都包含一個壓縮過的cpio格式
的打包文件。當內核啓動時,會從這個打包文件中導出文件到內核的rootfs文件系統,而後內核檢查rootfs中是否包含有init文件,若是有則執行
它,做爲PID爲1的第一個進程。這個init進程負責啓動系統後續的工做,包括定位、掛載「真正的」根文件系統設備(若是有的話)。若是內核沒有在
rootfs中找到init文件,則內核會按之前版本的方式定位、掛載根分區,而後執行/sbin/init程序完成系統的後續初始化工做。 
個壓縮過的cpio格式的打包文件就是initramfs。編譯2.6版本的linux內核時,編譯系統總會建立initramfs,而後把它與編譯好的
內核鏈接在一塊兒。內核源代碼樹中的usr目錄就是專門用於構建內核中的initramfs的,其中的initramfs_data.cpio.gz文件就
是initramfs。缺省狀況下,initramfs是空的,X86架構下的文件大小是134個字節。 
2、構建第一個initramfs:hello world 
從C語言開始,學習計算機編程語言的第一個程序幾乎都是hello world,所以咱們也構建一個最簡單的hello world式的initramfs,以說明initramfs的基本構建方法。 
initramfs的靈魂是init文件(或者叫程序,由於它會被內核第一個執行),咱們先寫一個簡單的init程序,它會在內核的console中打印出經典的hello world信息。 
hello.c: 
#include  
#include  
int main(int argc,char argv[]) 
printf("hello world, from initramfs.\n"); 
sleep(9999999); 
return 0; 
其中的sleep()函數語句是爲了不執行時內核很快打出panic的信息,並不是功能上的須要。 
接着把hello.c編譯成靜態鏈接程序: 
gcc -o hello_static -static -s hello.c 
命令行中的-s參數表示編譯後的程序不包含調試定位信息,目的是減小編譯出來的程序文件的大小。 
再建立一個initramfs的構建源文件目錄image,把hello_static程序拷入這個目錄,並更名爲init。 
在image目錄下,建立一個dev/console的設備文件,否init程序沒法在內核console中輸出信息: 
mknod -m 600 dev/console c 5 1 
注意,執行這個命令須要有root權限。 
好了,如今能夠設置內核配置參數,進行initramfs的構建了: 
在general
setup配置目錄下的initramfs
sources配置項下輸入image的路徑名,好比個人路徑就是/home/wyk/initramfs-test/image。由於咱們的init程
序是ELF格式的,因此內核須要支持ELF的可執行文件,不然啓動這個init程序會失敗。在內核的 Executable file
formats配置目錄下,選擇 kernel support for ELF
binaries,則可以使內核支持ELF格式的可執行文件。其餘內核配置參數根據實際須要設置便可,不過,爲了減小內核編譯時間,可參考這篇文章
http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-293122.html
設置一個最簡單的內核配置。 
內核配置參數設置完成後,按常規的內核編譯方法進行編譯,initramfs就自動鏈接到編譯好的內核映像文件中了。 
3、試驗環境搭建 
試驗initramfs須要常常重啓系統,因此使用CPU模擬器是不錯的選擇。咱們能夠選用qemu,它支持直接啓動linux內核,無需在模擬器中安裝OS。從方便使用的角度考慮,咱們採用qemu launcher設置qemu的各項參數,它的安裝可參考
http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-612280.html
。 
在qemu launcher的linux配置標籤中,打勾直接啓動linux,而後在下面的文本框中填上剛纔編譯好的內核映像文件的路徑名。由於qemu的運行還須要設置硬盤映像文件,因此還須要在左邊的配置標籤中新建一個硬盤映像文件,但實際上咱們並不使用硬盤。 
配置好qemu的參數後,點擊launcher按鈕,內核就開始在qemu中運行了。內核輸出一堆內核運行信息後,最後打出了 
hello world, from initramfs. 
哈哈,咱們構建的initramfs已經可以正常工做了! 
(二)initramfs的前世此生 
4、什麼是rootfs和ramfs 
全部的2.6版本linux內核都有一個特殊的文件系統
rootfs,是內核啓動的初始始根文件系統,initramfs的文件會複製到rootfs。若是把initramfs比做種子,那麼rootfs就是
它生長的土壤。大部分linux系統正常運行後都會安裝另外的文件系統,而後忽略rootfs。 
rootfs是ramfs文件系統的一個特殊實例。ramfs是一種很是簡單的文件系統,是基於內存的文件系統。ramfs文件系統沒有容量大小的限制,它能夠根據須要動態增長容量。 
ramfs
直接利用了內核的磁盤高速緩存機制。全部的文件的讀寫數據都會在內存中作高速緩存(cache),當系統再次使用文件數據時,能夠直接從內存中讀寫,以提
供系統的I/O性能。高速緩存中的寫入數據會在適當的時候回寫到對應的文件系統設備(如磁盤等)中,這時它的狀態就標識爲clean,這樣系統在必要時可
以釋放掉這些內存。ramfs沒有對應文件系統設備,因此它的數據永遠都不會回寫回去,也就不會標識爲clean,所以系統也永遠不會釋放ramfs所佔
用的內存。 
由於ramfs直接使用了內核已有的磁盤高速緩存機制,因此它的實現代碼很是小。也因爲這個緣由,ramfs特性不能經過內核配置參數刪除,它是內核的自然特性。 
5、ramfs不是ramdisk 
ramdisk
是在一塊內存區域中建立的塊設備,用於存放文件系統。ramdisk的容量是固定的,不能象ramfs同樣動態增加。ramdisk須要內核的文件系統驅
動程序(如ext2)來操做其上的數據,而ramfs則是內核的自然特性,無需額外的驅動程序。ramdisk也象其餘文件系統設備同樣,須要在塊設備和
內存中的磁盤高速緩存之間複製數據,而這種數據複製實際沒必要要的。 
6、從ramfs派生的文件系統tmpfs 
ramfs
的一個缺點是它可能不停的動態增加直到耗盡系統的所有內存,因此只有root或受權用戶容許使用ramfs。爲了解決這個問題,從ramfs派生出了
tmpfs文件系統,增長了容量大小的限制,並且容許把數據寫入交換分區。因爲增長了這兩個特性,因此tmpfs容許普通用戶使用。 
關於tmpfs文件系統更多的信息,能夠看內核源碼中的 Documentation/filesystems/tmpfs.txt 文檔。 
綜上所述,initramfs是一種ramfs文件系統,在內核啓動完成後把它複製到rootfs中,做爲內核初始的根文件系統,它的任務是掛載系統真正的根文件系統。這就是initramfs的前世此生。 

(三):busybox 
7、什麼是busybox 
busybox號稱是嵌入式Linux中的瑞士軍刀——小巧、功能齊
全。它把許多經常使用的Linux命令都集成到一個單一的可執行程序中,只用這一個可執行程序(即busybox)加上Linux內核就能夠構建一個基本的
Linux系統。busybox程序很是小巧,包含所有命令可執行文件大小也只有750多K。busybox是徹底模塊化的,能夠很容易地在編譯時增長、
刪除其中包含的命令。 
因爲busybox的這些特色,它普遍應用於LiveCD、應急修復盤、安裝盤等系統中。咱們也是以它爲基礎,構建initramfs。 
8、busybox的配置、編譯和安裝 
(1)去
http://busybox.net
去下載最新的源碼,解壓展開。 
(2)用 
make menuconfig 
命令啓動配置界面配置,配置busybox的特性、選擇要包含在busybox的命令(busybox稱爲applet); 
也能夠用 
make defconfig 
命令作缺省配置,包含所有的applet。 
另外兩個配置命令是 
make allyesconfig——最大配置 
make allnoconfig——最小配置 
它們和make defconfig命令均可以用來做爲自定義配置的初始配置,而後再用make menuconfing命令作定製化配置。 
爲了簡單,咱們用make defconfig作缺省配置。 
(3)用 
make 
命令編譯busybox軟件。 
(4)用 
make CONFIG_PREFIX= install 
命令安裝。若是在命令行中省略CONFIG_PREFIX變量的賦值,則會安裝缺省值 ./_install 目錄下。CONFIG_PREFIX能夠在make menuconfig的配置界面中修改。 
咱們用make CONFIG_PREFIX=~/initramfs-test/image 命令把busybox安裝到initramfs的構建目錄中。 
(5)缺省配置下,busybox動態連接到glibc,因此要把它用到的動態庫複製到initramfs的構建目錄中。用ldd命令查看busybox用到了哪些動態庫文件及相應的文件路徑,而後把它們複製到相應的目錄下便可。 
咱們編譯的busybox須要向image/lib目錄下複製 
ld-linux.so.2 
libc.so.6 
libcrypt.so.1 
libm.so.6 
動態庫文件。 
9、在image下建立必要的目錄和設備文件 
(1)在imgae目錄下建立 
proc , sys , etc ,mnt 
四個目錄 
(2)hello world 已經建立了console 設備文件,咱們再用 
mknod -m 600 dev/null c 1 3 
命令建立另外一個基本的設備文件。 
10、試驗一下 
busybox的構建和準備工做作完了,咱們試驗一下吧: 
在image目錄下以root用戶權限—— 
(1)用 
mount -vt proc proc =proc 
mount -vt sysfs sysfs =sys 
命令安裝內核虛擬文件系統 
(2)用 
mount -v -o bind /dev dev 
命令綁定/dev的設備文件到image/dev 
(3)用 
chroot . /bin/sh 
命令進入busybox的環境。出現shell的命令提示符,能夠試着輸入幾個命令,看看執行結果。例如,輸入 fdisk -l 命令看看是否能顯示硬盤的分區。 

(四):mini linux 
11、自動生成/dev下的設備文件 
上節用chroot方法試驗busybox時,爲了簡單,是用「綁定」的方式把主機的/dev中的設備文件映射到image目錄下的dev目錄。在initramfs上,這種方法顯然不能使用。 
生成系統的設備文件,如今一般都是用udev動態生成,而initramfs爲了作到通用,動態生成的要求是必須的。在busybox中有一個mdev命令,就是用來動態生成設備文件,填充到/dev目錄的。 
在系統啓動時,用 
mdev -s 
命令能夠根據內核的sysfs文件系統在/dev目錄中自動生成相應的設備文件。命令執行前,須要先掛載內核的proc和sysfs虛擬文件系統。 
12、初始身手 
解決了自動生成設備文件的問題後,咱們能夠試着作一個最簡單的可運行的linux系統了: 
(1)在image目錄下寫一個最簡單的init腳本。 
#!/bin/sh 
mount -t proc proc /proc 
mount -t sysfs sysfs /sys 
mdev -s 
/bin/sh 
(2)爲init腳本設置可執行權限,不然內核不會去執行它。 
chmod +x init 
(3)有些busybox配置中,mdev命令須要讀取/etc/mdev.conf文件,爲了不出錯信息,咱們建立一個空文件。 
touch etc/mdev.conf 
(4)在內核源碼目錄下,執行 
make 
命令,從新編譯內核,生成新的initramfs。 
好了,在QEMU模擬環境下啓動這個新的內核,系統初始化後,會進入SHELL環境。在這個SHELL環境下,試驗一些經常使用命令,看看是否能夠正常運行。 
十3、can't access tty 
上一步建立的簡單linux系統在進入SHELL環境時,會打出下面這一句出錯信息: 
/bin/sh: can't access tty; job controll off 
雖然不影響使用,但終究不夠完美。 
生這個錯誤的緣由是咱們的SHELL是直接運行在內核的console上的,而console是不能提供控制終端(terminal)功能的,因此必須把
SHELL運行在tty設備上,才能消除這個錯誤。解決問題的辦法是使用正規init機制,在執行SHELL前打開tty設備。 
另外,這個簡單系統的reboot、halt等命令是不起做用的,也必須經過init方式解決。 
十4、busybox的缺省init模式 
busybox支持init功能,當系統沒有/etc/inittab文件時,它有一套缺省的模式,按下面配置執行: 
::sysinit:/etc/init.d/rcS 
::askfirst:/bin/sh 
::ctrlaltdel:/sbin/reboot 
::shutdown:/sbin/swapoff -a 
::shutdown:/bin/umount -a -r 
::restart:/sbin/init 
若是busybox檢測到/dev/console不是串口控制檯,init還要執行下面的動做: 
tty2::askfirst:/bin/sh 
tty3::askfirst:/bin/sh 
tty4::askfirst:/bin/sh 
咱們試試這種模式是否能夠解決咱們的問題。 
(1)寫/etc/init.d/rcS腳本 
這個腳本實際是要執行系統的初始化操做。咱們把前面的init腳本改造一下,將最後的/bin/sh命令刪除,而後移到 etc/init.d目錄下,更名爲rcS。 
(2)initramfs不須要linuxrc,並且若是沒有init文件,內核就不認爲它是一個有效的initramfs,於是不安裝它,致使內核panic。因而,咱們在image目錄下,把busybox安裝的linuxrc更名爲init 
mv linuxrc init 
(3)從新編譯內核,生成新的initramfs 
(4)用QEMU試驗一下新編譯的內核。系統啓動後,會打出一句話「please press Enter to active this console」——感受還不錯。可是按下回車鍵後,系統依然會打出錯誤信息「-/bin/sh: 
can't access tty; job controll off 」。用tty命令看看當前的終端設備文件名: 
# tty 
/dev/console 
它仍是console,不是tty設備,因此問題沒有解決。不過,reboot和halt命令卻是能夠正常工做了。 
通過驗證,busybox的缺省init模式沒法知足咱們的要求,咱們仍是要寫inittab,定製本身的init初始化流程。 
十5、busybox的inittab文件格式說明 
要寫本身的inittab,須要理解busybox的inittab文件格式。 
busybox的inittab文件與一般的inittab不一樣,它沒有runlevel的概念,語句功能上也有限制。inittab語句的標準格式是 
::: 
各字段的含義以下 
id字段與一般的inittab中的含義不一樣,它表明的是這個語句中process執行所在的tty設備,內容就是/dev目錄中tty設備的文件名。因爲是運行process的tty設備的文件名,因此也不能象一般的inittab那樣要求每條語句id的值惟一。 
busybox不支持runlevel,因此此字段徹底被忽略。 
爲下列這些值之一: 
sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown 
含義與一般的inittab的定義相同。特別提一下askfirst,它的含義與respawn相同,只是在運行process前,會打出一句話
「please press Enter to active this console」,而後等用戶在終端上敲入回車鍵後才運行process。 
: 
指定要運行的process的命令行。 
十6、寫mini linux的inittab 
理解了busybox的inittab格式,咱們就能夠寫mini linux的inittab: 
::sysinit:/etc/init.d/rcS 
tty1::askfirst:/bin/sh 
tty2::askfirst:/bin/sh 
tty3::askfirst:/bin/sh 
tty4::askfirst:/bin/sh 
tty5::askfirst:/bin/sh 
tty6::askfirst:/bin/sh 
::restart:/sbin/init 
::ctrlaltdel:/sbin/reboot 
::shutdown:/bin/umount -a -r 
把這個文件放到image的etc目錄下。爲了執行reboot命令時避免提示找不到/etc/fstab文件,咱們再在etc目錄下建立一個空文件 
touch fstab 
作好了這些,就能夠從新編譯內核,生成新的initramfs了。在QEMU試驗環境下驗證新生成的mini linux,系統運行正常,並且象一般的linux系統同樣,用ALT+F1~F6鍵能夠在6個終端間切換。 

(五)initrd 
十7、配置內核支持initrd 
到目前爲止,咱們的initramfs都由內核編譯系統生成的,並連接到內核中。其實咱們也能夠用cpio命令生成單獨的initramfs,與內核編譯脫鉤,在內核運行時以initrd的形式加載到內核,以增長靈活性。 
先配置內核使用單獨的initrd:在 Device Driver / Block device / 配置目錄下,選擇 RAM
filesystem and RAMdisk ( initramfs/initrd ) support 配置項;再到 General
Setup 配置目錄項下,將 initramfs source file(s)
配置項原有的內容清空。而後把內核源碼樹的usr目錄下已由內核編譯生成的initramfs文件initramfs_data.cpio.gz拷貝到
~/initramfs-test 目錄下,咱們先直接用這個文件試驗一下 initrd
方式的initramfs的效果。最後,執行make命令從新編譯內核後,在QEMU試驗環境中,把initrd配置框(linux配置框的下面)的內容
寫爲 ~/initramfs-test/initramfs_data.cpio.gz,指定initrd的文件路徑。 
好了,試驗一下新的initrd方式的initramfs吧,效果跟先前的徹底同樣。 
十8、用cpio命令生成initramfs 
cpio
命令有三種操做模式:copy-out、copy-in、copy-pass,生成initramfs用的是它的copy-out模式,即把文件打包的操
做模式。cpio的copy-out操做模式使用 -o 命令行選項指定。缺省狀況下,cpio從標準輸入讀取輸入數據,向標準輸出寫入輸出數據。使用
-I 選項能夠指定文件名代替標準輸入,使用 -O 選項能夠指定文件名代替標準輸出,而 -F
選項指定的文件名則根據cpio操做模式的不一樣可代替標準輸入或標準輸出。 
把~/initramfs-test/image目錄下的文件打包成initramfs,執行下面的命令: 
find . | cpio -o -H newc | gzip > ../image.cpio.gz 
命令執行完畢後,在~/initramfs-test目錄下就會生成文件名爲imgae.cpio.gz的initramfs。 
面cpio命令的 -H 選項指定打包文件的具體格式,要生成initramfs,只能用newc
格式,若是使用其餘格式,內核會打出這樣的出錯信息:Unpacking initramfs... kernel panic -
not syncing: no cpio magic 
在QEMU試驗環境下試驗一下新的initrd方式的initramfs,效果跟先前的徹底同樣。 
十9、cpio命令的其餘用法 
果咱們要解開一個cpio格式的打包文件,則要使用cpio命令的copy-in操做模式。cpio的copy-out操做模式使用 -i
命令行選項指定。例如,咱們想把前一步從內核源碼樹 usr目錄下拷貝的initramfs_data.cpio.gz
展開到~/initramfs-test/initramfs_data目錄下,則使用下列命令: 
mkdir ~/initramfs-test/initramfs_data 
cd ~/initramfs-test/initramfs_data 
cpio -i -F ../initramfs_data.cpio.gz --no-absolute-filename 
命令執行完畢後,initramfs_data目錄下出現多個目錄和文件,用diff命令比較initramfs_data與image目錄,二者的徹底同樣。 
上面cpio命令的
--no-absolute-filename
選項的做用是展開文件時,去掉文件路徑最前面的"/",把絕對路徑名變爲相對路徑名。內核編譯時生成的initramfs使用了絕對路徑名,因此這個選項
必須使用,不然initramfs內文件展開到"/"目錄去了,若是你是root用戶或有"/"目錄的寫權限,那麼展開的文件就有可能覆蓋同名的文件(在
文件修改時間新於原有文件),那就糟糕了! 
展開文件前,你可能會想先看看打包文件裏都有哪些文件,這時就要用 -t 選項了。例如,咱們想看看內核編譯時生成的initramfs_data.cpio.gz中都有哪些文件,咱們就能夠用下面的命令: 
zcat initramfs_data.cpio.gz | cpio -t 
在標準輸出中打出文件名列表。 
使用 -v 選項能夠在cpio命令執行時輸出詳細信息:在打包或展開文件時,輸出已處理的文件名;與 -t 選項連用時,則顯示文件的詳細信息,相似 ls -l 的輸出內容。-V 選項則用打點的方式,顯示cpio命令的執行進度信息,一個點表明處理一個文件。 
(六)switch_root 
二10、switch_root 命令 
除了基於initramfs的系統(如第四節的mini
linux),一般initramfs都是爲安裝最終的根文件系統作準備工做,它的最後一步須要安裝最終的根文件系統,而後切換到新根文件系統上去。以往
的基於ramdisk 的initrd
使用pivot_root命令切換到新的根文件系統,而後卸載ramdisk。可是initramfs是rootfs,而rootfs既不能
pivot_root,也不能umount。爲了從initramfs中切換到新根文件系統,須要做以下處理: 
(1)刪除rootfs的所有內容,釋放空間 
find -xdev / -exec rm '{}' ';' 
(2)安裝新的根文件系統,並切換 
cd /newmount; mount --move . /; chroot . 
(3)把stdin/stdout/stderr 附加到新的/dev/console,而後執行新文件系統的init程序 
上述步驟比較麻煩,並且要解決一個重要的問題:第一步刪除rootfs的全部內容也刪除了全部的命令,那麼後續如何再使用這些命令完成其餘步驟?busybox的解決方案是,提供了switch_root命令,完成所有的處理過程,使用起來很是方便。 
switch_root命令的格式是: 
switch_root [-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT] 
其中NEW_ROOT是實際的根文件系統的掛載目錄,執行switch_root命令前須要掛載到系統中;NEW_INIT是實際根文件系統的init程序的路徑,通常是/sbin/init;-c /dev/console是可選參數,用於重定向實際的根文件系統的設備文件,通常狀況咱們不會使用;而ARGUMENTS_TO_INIT則是傳遞給實際的根文件系統的init程序的參數,也是可選的。 

須要特別注意的是:switch_root命令必須由PID=1的進程調用,也就是必須由initramfs的init程序直接調用,不能由init派生的其餘進程調用,不然會出錯,提示: 
switch_root: not rootfs 
也是一樣的緣由,init腳本調用switch_root命令必須用exec命令調用,不然也會出錯,提示: 
switch_root: not rootfs 
二11、實踐:用initramfs安裝CLFS根文件系統 
如今實踐一下switch_root命令,用它切換一個CLFS的根文件系統硬盤分區。個人CLFS安裝在/dev/sda8硬盤分區,咱們就以此爲例說明。 
咱們仍是在之前的image目錄中構建 
(1)改寫init腳本 
#!/bin/sh 
mount -t proc proc /proc 
mount -t sysfs sysfs /sys 
mdev -s 
mount /dev/sda8 /mnt (注意:爲了簡單,咱們直接把CLFS分區寫死在init腳本中了) 
exec switch_root /mnt /sbin/init 
(2)生成新的initrd 
按上一節
「精通initramfs構建step by step (五):initrd」
描述的cpio命令生成新的initrd。 
(3)把新的initrd拷貝到CLFS分區的/boot目錄下,更名爲clfs-initrd 
(4)在GRUB的menu.lst配置文件中增長一個啓動項 
#test for initramfs of CLFS 
title test for initramfs of CLFS (on /dev/sda8) 
root (hd0,7) 
kernel /boot/clfskernel-2.6.17.13 (注意:並無向內核傳遞root參數信息) 
initrd /boot/clfs-initrd 
所有作完後,重啓機器,選擇 test for initramfs of CLFS 啓動項,機器順利進入了CLFS系統,咱們構建的initramfs用switch_root命令完成了CLFS實際根文件系統的安裝和切換。 

(七)modules
二12、內核模塊支持 
到目前爲止,咱們在構建initramfs時尚未涉及內核模塊的支持,所用到的硬件驅動程序都是直接編譯到內核中。如今咱們就看看如何使initramfs支持內核模塊。 
先,內核配置要支持模塊,並支持內核模塊的自動加載功能:在內核配置菜單中的激活下面的配置項,編譯進內核 Load module support
/ Enable loadable module support / Automatic kernel loading ; 
而後把須要的硬件驅動程序配置模塊形式,好比把個人機器上的硬盤控制器的驅動編譯成模塊,則選擇 
Device Driver 
|---->SCSI device support 
|---->SCSI disk support 
|----->verbose SCSI error reporting (不是必須的,但可方便問題定位) 
|----->SCSI low-level drivers 
|---->Serial ATA (SATA) support 
|---->intel PIIX/ICH SATA support 
把它們配置成模塊。 
最後,編譯內核,並把編譯好的內核模塊安裝到image的目錄下: 
make 
make INSTALL_MOD_PATH=~/initramfs-test/image modules_install 
令執行完畢後,在image/lib/modules/2.6.17.13/kernel/drivers/scsi目錄下安裝了4個內核模文
件:scsi_mod.ko、sd_mod.ko、ata_piix.ko、libata.ko,它們就是所需的硬盤控制器的驅動程序。 
好了,都準備好了,能夠用cpio命令生成inintramfs了。不過,爲了方便後面的試驗,咱們再把init腳本改爲 
#!/bin/sh 
mount -t proc proc /proc 
mount -t sysfs sysfs /sys 
mdev -s 
exec /bin/sh 
使系統啓動後進入shell環境,而且用exec調用的方式,使shell的pid爲1,可以執行switch_root命令。 
二十3、試驗:用initramfs中的內核模塊安裝硬盤文件系統 
新生成的initramfs啓動系統,內核並無自動加載硬盤控制器的驅動程序,因此
/dev目錄下也沒有sda等硬盤設備文件。好吧,咱們本身加載內核模塊文件。不幸的是,busybox的modprobe命令執行不正常,不能加載內核
模塊。懷疑是busybox的modprobe命令配置或編譯有問題,後續再花時間定位吧,先用insmod命令依次加載。查看/lib/modules
/2.6.17.13/modules.dep,弄清楚了4個模塊的依賴關係,執行下面的命令加載: 
insmod scsi_mod 
insmod libata 
insmod ata_piix 
insmod sd_mod 
而後再用 
mdev -s 
命令生成硬盤的設備文件。 
好了,能夠安裝CLFS的硬盤分區,並把根文件系統切換到CLFS的硬盤分區: 
mount /dev/sda8 /mnt 
exec switch_root /mnt /sbin/init 
系統正常啓動到了CLFS,咱們能夠作到用initramfs中的硬盤控制器的驅動模塊安裝硬盤分區了。 
二十4、mdev的hotplug模式 
上面的試驗中,咱們在加載完驅動模塊後調用了mdev -s 命令來生成硬盤的設備文件。其實,可使用mdev的hotplug模式在加載內核時自動生成對應的設備文件: 
在執行insmod命令前,用 
echo /sbin/mdev > /proc/sys/kernel/hotplug 
命令設置系統的hotplug程序爲mdev。 
後續使用insmod命令加載模塊時,系統自動調用mdev生成相應的設備文件。 
注意:內核必須配置支持hotplug功能,而前面提到的CLFS最簡內核配置方案是沒有配置hotplug支持的。 
(八)coldplug 
二十5、udev的coldplug模式 
內核在啓動時已經檢測到了系統的硬件設備,並把硬件設備
信息經過sysfs內核虛擬文件系統導出。udev掃描sysfs文件系統,根據硬件設備信息生成熱插拔(hotplug)事件,udev再讀取這些事
件,生成對應的硬件設備文件。因爲沒有實際的硬件插拔動做,因此這一過程被稱爲coldplug。咱們的initramfs就是利用這一機制,加載硬件設
備的驅動程序模塊。 
udev完成coldplug操做,須要下面三個程序: 
udevd——做爲deamon,記錄hotplug事件,而後排隊後再發送給udev,避免事件衝突(race conditions)。 
udevtrigger——掃描sysfs文件系統,生成相應的硬件設備hotplug事件。 
udevsettle——查看udev事件隊列,等隊列內事件所有處理完畢才退出。 
在initramfs的init腳本中能夠執行下面的語句實現coldplug功能: 
mkdir -p /dev/.udev/db 
udevd --daemon 
mkdir -p /dev/.udev/queue 
udevtrigger 
udevsettle 
許多文檔提到的在udevd --daemon 命令前要執行 
echo > /proc/sys/kernel/hotplug 
命令,經驗證,在咱們的initramfs環境下的coldplug功能中並不須要。 
二十6、試驗:用udev自動加載設備驅動模塊 
瞭解了udev的coldplug的機理,咱們就試驗一下用udev自動加載設備驅動模塊,並生成硬件設備文件。 
(1)從 /sbin 目錄下拷貝udevd、udevtrigger、udevsettle程序到image目錄下的sbin目錄下,並用ldd命令找到它們所須要的動態庫文件,拷貝到image目錄下的lib目錄下。 
(2)修改init腳本,增長coldplug功能: 
#!/bin/sh 
mount -t proc proc /proc 
mount -t sysfs sysfs /sys 
mdev -s 
#using udev autoload hard disk driver module 
mkdir -p /dev/.udev/db 
udevd --daemon 
mkdir -p /dev/.udev/queue 
udevtrigger 
udevsettle 
mount /dev/sda8 /mnt 
killall udevd 
exec switch_root /mnt /sbin/init 
注意:在切換到真正根文件系統前,要把udevd進程殺掉,不然會和真正根文件系統中的udev腳本的執行相沖突。這就是上面killall udevd 語句的做用。 
(3)編寫udev規則文件 
則文件是udev的靈魂,沒有規則文件,udev沒法自動加載硬件設備的驅動模塊。爲了簡單,咱們直接使用CLFS中的40-
modprobe.rules,把它拷貝到image目錄下的etc/udev/rules.d目錄。有關udev的規則文件編寫,已超出了本文的範圍,
後續我有可能專文描述。 
########################################################################
# Description : 40-modprobe.rules 
# Authors : Based on Open Suse Udev Rules 
kay.sievers@suse.de

# Adapted to : Jim Gifford 
# LFS : Alexander E. Patrakov 
# Version : 00.01 
# Notes : 
########################################################################
# hotplug 
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}" 
# scsi 
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="0|7|14", RUN+="/sbin/modprobe sd_mod" 
SUBSYSTEM=="scsi_device",
ACTION=="add", SYSFS{device/type}=="1",
SYSFS{device/vendor}=="On[sS]tream", RUN+="/sbin/modprobe osst" 
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="1", RUN+="/sbin/modprobe st" 
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="[45]", RUN+="/sbin/modprobe sr_mod"
SUBSYSTEM=="scsi_device", ACTION=="add", RUN+="/sbin/modprobe sg" 
# floppy 
KERNEL=="nvram", ACTION=="add", RUN+="load_floppy_module.sh" 
注意:上面的 
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}" 
句是實現自動加載硬件設備驅動模塊功能的關鍵,它根據sysfs文件系統中記錄的模塊aliases數據,用modprobe命令加載對應的內核模塊。有
關模塊aliases的進一步說明,可參考CLFS手冊(CLFS-1.0.0-x86)中的11.5.2.4. Module
Loading一節的描述。 
(4)拷貝modprobe命令 
前一節提到過,busybox的modprobe
命令不能正常使用,因此咱們須要拷貝 /sbin
目錄下的modprobe命令到image目錄下的sbin目錄,供udev加載內核模塊使用。再用ldd命令檢查一下 /sbin/modprobe
命令所需的動態庫文件,若是有則拷貝到image/lib目錄下。(個人檢查結果是,除了libc6外,不須要其餘動態庫,因此不須要拷貝) 
好了,從新生成initramfs,啓動CLFS系統,initramfs可以自動加載硬盤設備的驅動模塊,系統順利地從initramfs切換到了真正的CLFS的根文件系統。 

(九)內核編譯時構建initramfs補遺 
二十7、直接把cpio打包文件編譯進內核 
若是咱們有一個已經作好的cpio格式的initramfs,能夠在內核編譯時直接編譯進內核。回憶一下
第一節
的內容,咱們在內核配置參數中的initramfs sources配置項下輸入構建initramfs的目錄路徑。其實咱們也能夠直接輸出現成的initramfs的文件名,這樣在內核編譯時,就能夠把它編譯進內核了。 
使用這種方法,有兩點須要注意: 
(1)cpio文件不能壓縮。通常做爲initrd的cpio文件都通過了壓縮,因此編譯前須要先把壓縮過的文件解壓。 
(2)cpio文件的後綴名必須是 .cpio。內核編譯經過 .cpio的後綴名來識別此文件是cpio打包文件,而其餘文件後綴名則會被認爲是initramfs構建的描述文件(關於描述文件,下面後詳細說明)。 
二十8、用描述文件構建initramfs 
用內核編譯工具構建initramfs的第三種方法是使用描述文件。在內核配置參數中的initramfs sources配置項下能夠輸入initramfs構建描述文件的文件名,內核編譯工具根據描述文件完成initramfs的構建。 
描述文件的語法格式的說明以下: 
# a comment 
file      
dir     
nod        
slink      
pipe     
sock    
name of the file/dir/nod/etc in the archive 
location of the file in the current filesystem 
link target 
mode/permissions of the file 
user id (0=root) 
group id (0=root) 
device type (b=block, c=character) 
major number of nod 
minor number of nod 
例子: 
咱們用描述文件的方式,構建第一節中的hello world的initramfs。 
hello-init.desp: 
dir /dev 0755 0 0 
nod /dev/console 0600 0 0 c 5 1 
file /init /home/wyk/initramfs-test/hello_static 0755 0 0 
在內核配置項initramfs sources中指定描述文件hello-init.desp,編譯內核時就會生成hello world的initramfs,運行效果與第一節用指定構建目錄的方法構建的initramfs的徹底相同。 
注意:在
內核幫助文件中,提到initramfs
sources配置項能夠指定多個目錄或描述文件,內核會聚集這些目錄或文件生成一個initramfs。但從個人試驗來看,initramfs
sources只接受單一的目錄名或文件名,輸出多個目錄名或文件名(之間用空格分隔),內核編譯時就會出錯。也許是個人方法有誤,還望讀者指正。 

(十)uclibc 
二十9、toolchain 
在initramfs中使用uclibc庫,關鍵是構建uclibc的工具鏈toolchain。構建uclibc 的 toolchain 有兩種主要方式:(1)用buildroot工具( 
http://buildroot.uclibc.org/
)自動構建,這也是uclibc的官方標準作法。(2)用CLFS Embedded手冊的方法手工建立。目前CLFS Embedded還在開發中,可在
http://cross-lfs.org/view/clfs-embedded/x86/
中查閱。 
咱們簡單地說明用buildroot工具構建uclbic的toolchain的步驟: 
(1)獲取buildroot。 
推薦用svn命令從它的版本庫中下載: 
svn co svn://uclibc.org/trunk/buildroot 
要求使用svn命令,須要先安裝subversion軟件包。下載過程當中,可能會出現鏈接異常中斷的狀況,這時從新執行上述命令,繼續進行下載,有可能要重複屢次。 
(2)配置buildroot 
由於咱們只是建立toolchain,因此須要作相應的配置。在buildroot的頂層目錄下,執行 
make menuconfig 
命令,在缺省配置的基礎上作以下配置 
Target Architecture: i386 
Target Architecture Variant: i686 
Package Selection for the target: 取消BusyBox的選項(缺省是選中的) 
Target filesystem options: 取消 ext2 root filesystem(缺省是選中的) 
Toolchain --> Toolchain type: Buildroot toolchain 
(3)編譯 
執行 
make 
命令,buildroot工具會自動下載所須要的源文件並自動編譯,等一兩個小時後,toolchain就編譯好了。編譯好的toolchain位於
buildroot/build_i686/staging_dir/usr/bin/ 
目錄下。工具命令的前綴是 i686-linux- 。 
三10、編譯Busybox靜態鏈接uclibc庫 
通常而言,使用uclibc庫是爲了把它靜態鏈接到busybox中。具體步驟是: 
(1)把uclibc toolchain的目錄添加到PATH中。 
在~/.bash_profile文件中添加: 
#set PATH so it includes uclibc toolchain if it exist 
if [ -d ~/buildroot/build_i686/staging_dir/usr/bin ] ; then 
PATH="${PATH}":~/buildroot/build_i686/staging_dir/usr/bin 
fi 
(2)配置busybox靜態鏈接庫。 
在busybox的配置界面中,選擇: 
Build Options --> Build BusyBox as a static binary (no shared libs) 
(3)編譯 
執行 
make CROSS_COMPILE=i686-linux- 
命令「交叉編譯」busybox。 
最後編譯生成的是靜態鏈接的可執行文件,不須要在initramfs中拷貝庫文件。 
三11、用buildroot自動構建initramfs 
buildroot
工具實際是一個功能強大的根文件系統構建工具,它以uclibc和busybox做爲系統構建的基礎,toolchain只是它構建系統的中間產品。
initramfs是一種特殊的根文件系統,固然也能夠用buildroot工具自動構建,下面是構建方法的簡要描述: 
(1)配置 
在buildroot的配置界面下作以下的配置: 
Package Selection for the target: 選擇 
Busybox 
Run Busybox's own full installation 
Use minimal target skeleton 
Target filesystem options --> cpio the root filesystem --> comprassion method: gzip 
(2)編譯 
執行 
make 
命令,進行編譯。 
(3)輸出 
構建好的cpio文件是 
buildroot/binaries/rootfs.i686.cpio.gz 
同一目錄下還包含一個未壓縮的文件:rootfs.i686.cpio 
構建目錄則是 
buildroot/project_build_i686/uclibc/root 
能夠在這個目錄下對原始的initramfs進行修改調整,而後本身用cpio命令打包生成新的initramfs。 
(4)調整 
直接用buildroot生成的root.i686.cpio.gz做爲initramfs,運行時會出現 
can't open /dev/tty1: No such file or directory 
can't open /dev/tty2: No such file or directory 
can't open /dev/tty3: No such file or directory 
錯誤信息的循環輸出,系統不能正常運行。 
錯誤的緣由是沒有在initramfs的/dev目錄下生成相應的設備文件。須要作以下的調整: 
1)在構建目錄(buildroot/project_build_i686/uclibc/root)下的etc/init.d目錄中新增一個初始化腳本文件S10mountfs 
#!/bin/sh 
mount -t proc proc /proc 
mount -t sysfs sysfs /sys 
mdev -s 
2)更改busybox的setuid屬性,不然沒法執行mount命令。在構建目錄(buildroot/project_build_i686/uclibc/root)下執行 
chmod -s bin/busybox 
命令。 
這兩項調整工做作完後,在構建目錄(buildroot/project_build_i686/uclibc/root)下執行 
find . | cpio -o -H newc |gzip > ../initramfs.cpio.gz 
命令,從新生成initramfs的cpio打包文件。 
(5)運行效果 
運行新的initramfs,系統出現登陸提示。輸入用戶名 root,密碼爲空,便可進入一個mini的linux系統。 
buildroot是功能強大、配置靈活的自動化構建工具,它的詳細使用和配置方法超出了本文的範圍,後續可能會專文描述,此處就從略了。 



相關文章
相關標籤/搜索