(一)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是功能強大、配置靈活的自動化構建工具,它的詳細使用和配置方法超出了本文的範圍,後續可能會專文描述,此處就從略了。