initrd | initramfs | |
---|---|---|
Image | 壓縮過的檔案系統 (如 ext2 + gzip) | 封裝過的檔案 (cpio + gzip) |
實做途徑 | block device (RAM disk) | tmpfs |
首先執行的程式 | /linuxrc | /init |
掛載 rootfs 方式 |
將欲載入的 rootfs 掛載於某個目錄,再 pivot_root 切換 rootfs | 使用 switch_root |
$ cd /home/jserv/initramfs-workspace $ tar jxvf $HOME/sources/linux-2.6.22.5.tar.bz2 $ mkdir -p hello-initramfs首先設立的目標是,可印出 "Hello World" 的 kernel + initramfs,並透過 qemu 進行模擬驗證。首先,建立一個 init.c,具備簡單的實做:
$ cd hello-initramfs $ cat init.c #include <stdio.h> int main() { printf("Hello World!\n"); sleep(99999); return 0; } $ gcc -static -o init init.c $ mkdir -p dev $ sudo mknod dev/console c 5 1建議先試著執行 "./init" 看看是否正確運作,程式碼中的 "sleep(99999)" 只是讓觀察更容易,避免畫面一閃而逝。剛剛的 "Hello World" 程式就是我們預期的 Early userspace,因為執行時期需要 tty (terminal),所以剛剛也一併建立 /dev/console 的 character device。現在我們可以來準備建構 kernel 了:
$ cd /home/jserv/initramfs-workspace/linux-2.6.22.5 $ make menuconfig要注意的是,需將 "General setup" 的子項目 "Initial RAM filesystem and RAM disk (initramfs/initrd) suppot" 打開,並在下方提示 "INITRAMFS_SOURCE" 的畫面輸入我們期望的 initramfs 的來源目錄,也就是 "/home/jserv/initramfs-workspace/hello-initramfs",參考的配置畫面如下:
$ make bzImage ... LD arch/i386/boot/compressed/vmlinux OBJCOPY arch/i386/boot/vmlinux.bin HOSTCC arch/i386/boot/tools/build BUILD arch/i386/boot/bzImage Kernel: arch/i386/boot/bzImage is ready建構成功,透過 qemu 來模擬測試:
$ qemu -kernel arch/i386/boot/bzImage -hda /dev/zero參考的執行畫面如下:
scripts/kconfig/conf -s arch/i386/Kconfig CHK include/linux/version.h CHK include/linux/utsrelease.h CC arch/i386/kernel/asm-offsets.s GEN include/asm-i386/asm-offsets.h ... CC init/initramfs.o CC init/calibrate.o LD init/built-in.o HOSTCC usr/gen_init_cpio GEN usr/initramfs_data.cpio.gz AS usr/initramfs_data.o ...我們可注意到 "GEN usr/initramfs_data.cpio.gz" 這行,勢必 kernel 2.6 中隱含了某種機制,執行看看之前產生的工具程式:
$ usr/gen_init_cpio Usage: usr/gen_init_cpio <cpio_list> <cpio_list> is a file containing newline separated entries that describe the files to be included in the initramfs archive: ...這裡提到的 "archive" 就是透過 [cpio] 工具產生的封裝檔案,在 RedHat .rpm 或 Debian .deb 均有採用此工具。不過 Linux kernel 則提供一個整合性的工具,可一次處理目錄與檔案的封裝,依據之前的流程試試看手動建立 cpio + gzip:
$ cd /home/jserv/initramfs-workspace $ sudo cp -af hello-initramfs hello2-initramfs $ cd hello2-initramfs $ cat init.c #include <stdio.h> int main() { printf("Yat Another Hello World!\n"); sleep(999999); return 0; } $ gcc -static -o init init.c $ cat desc_initramfs dir /dev 0755 0 0 nod /dev/console 0600 0 0 c 5 1 file /init /home/jserv/initramfs-workspace/hello2-initramfs/init 0755 0 0 $ ../linux-2.6.22.5/usr/gen_init_cpio desc_initramfs > my_initramfs.cpio $ gzip my_initramfs.cpio"desc_initramfs" 是我們自己寫的描述檔,格式大抵就如上面展示,usr/gen_init_cpio 這個工具則會建構對應的 dir + device node + file 的封裝,最後我們以 gzip 壓縮起來,於是可得到 "my_initramfs.cpio.gz" 這個新的 initramfs image。同樣的,我們可用 qemu 測試驗證,這次改由 qemu 模擬 boot loader 指定 initramfs image 的模式,操作如下:
$ cd /home/jserv/initramfs-workspace/linux-2.6.22.5 qemu -kernel arch/i386/boot/bzImage -initrd ../hello2-initramfs/my_initramfs.cpio.gz -hda /dev/zero這次應該就會在 qemu 模擬的輸出畫面最下方看到 "Yat Another Hello World!" 的字樣。
$ sudo apt-get install busybox-static隨後,系統會安裝 /bin/busybox 的執行檔,觀察一下:
$ file /bin/busybox /bin/busybox: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.8, statically linked, stripped咱們就以此為基礎,建立一個小而美的 initramfs + kernel image:
$ cd /home/jserv/initramfs-workspace $ mkdir -p busybox-initramfs/bin $ mkdir -p busybox-initramfs/proc $ cd busybox-initramfs/bin $ cp /bin/busybox . $ ./busybox --help | ruby -e 'STDIN.read.split(/functions:$/m)[1].split(/,/).each{|i|`ln -s busybox #{i.strip}` unless i=~/busybox/}' $ cd .. $ echo -e '#!/bin/busybox sh\nmount -t proc proc /proc\nexec busybox sh\n' > init ; chmod +x init $ find . | cpio -o -H newc | gzip > ../busybox.initramfs.cpio.gz可看到 $HOME/initramfs-workspace 就輸出了名為 busybox.initramfs.cpio.gz 的 initramfs image,可仿造上一個範例,透過 qemu 模擬:
$ cd /home/jserv/initramfs-workspace/linux-2.6.22.5 qemu -kernel arch/i386/boot/bzImage -initrd ../busybox.initramfs.cpio.gz -hda /dev/zero參考的執行畫面如下:
哇,這一篇對小型Linux系統的開發者大有幫助。
由 checko 發表於 January 21, 2008 12:16 AM$ cp /bin/busybox .
$ ./busybox --help | ruby -e 'STDIN.read.split(/functions:$/m)[1].split(/,/).each{|i|`ln -s busybox #{i.strip}` unless i=~/busybox/}'
$ cd ..
$ echo -e '#!/bin/busybox sh\nmount -t proc proc /proc\nexec busybox sh\n' > init ; chmod +x init
也許可以改成
$ cp /bin/busybox .
$ cd ..
$ echo -e '#!/bin/busybox sh\n/bin/busybox --install\nmount -t proc proc /proc\nexec busybox sh\n' > init ; chmod +x init
就不用 ruby 了~ 對初學者可能會更簡單~ :)
當然 jserv 也許有效率上的考量
先建好就不用在 runtime 花時間建立
To FourDollars,
Thanks. 不過一般我們看到 shell,就會不自覺想敲入 "ls" 一類的指令,所以本文先建立 symbolic links。話說回來,好像沒透過文字敘述,感覺不太好。
由 jserv 發表於 January 21, 2008 02:36 PMjserv 好~
去年也做了一些只用 initramfs 的系统~
很受教益,轉栽了,謝謝。
由 why 發表於 January 21, 2008 08:59 PM前輩寫的好清楚喔,讓我這個沒概念的人大概了解了。
話說前幾天我的系統重開機後,在啟動的過程中出現「ERROR 15」,然後我進grub修改一些啟動參數發現在root=這邊是一串UUID識別碼,改完內核版本及root目錄啟動後就出現 busybox 的命令工具,它的提示符就是(initramfs)
為什麼root=後面會是UUID識別碼呢?
因為看的太仔細了XD,糾幾個錯字^^
:
>>而 /linuxrc 執行的 PID 「必」非為 1
>「跟」本
由 yugu 發表於 January 22, 2008 03:02 AM@yugu
你也可以 root=/dev/sda1 之類,
新的 linux 發行版默認用 uuid 是為了更通用,
譬如下邊兩個情況:
1. 不同的驅動下 /dev 里的設備名字會不一樣,而 uuid 是唯一的。
2. 更換硬碟/磁盤或使用移動存儲的時候, /dev 下的設備名字也會不一樣, 而 uuid 是唯一的
感謝jserv大!
我跟著您的步驟做了之後,發現在x86_64上會有些問題
在使用自己產生的initrd會有問題
qemu上面秀的訊息是停在
It looks like a initrd.
然後就死在那邊了@@
不知是cpio那邊出問題還是...?
由 cyberj 發表於 January 30, 2008 02:59 PMTo jserv,
我是用qemu-system-x86_64執行的
不過我似乎沒有注意到更關鍵的提示
checking if image is initramfs... it isn't (bad gzip magic numbers); looks like an initrd
至於為什麼gzip會有問題,我就不清楚了XD
btw, 只要用第一個方法 make menuconfig
還是可以輕鬆地將busybox做成ramdisk^^
ps:用錯版本qemu的話,開機時會有提示
(我不確定是bootloader提示的,還是??)
To cyberj,
真有意思,所以我們發現了在 x86_64 平台上,initramfs image 的生成可能有些許的差異。看來可能要追一下 kernel build 的細節了,感謝提醒!
由 jserv 發表於 January 31, 2008 03:51 PM請教一下.
我用 allnoconfig, 有加上 ram block device, 也有設 initramfs.
卻說找不到 /init.
why ?
自問自答.
因為沒有開 kernel support for ELF binaries.
ram block device 倒是可以不用開.
改成這樣的產生方式如何?
cd /home/jserv/initramfs-workspace
mkdir -p busybox-initramfs/{bin,proc,usr/{bin,sbin}}
cp /bin/busybox busybox-initramfs/bin
mount -t proc none busybox-initramfs/proc
chroot busybox-initramfs /bin/busybox sh
/bin/busybox --install -s
exit
umount busybox-initramfs proc
完全不使用 ruby, 直接產生 symbol links
由 dou0228 發表於 January 9, 2009 05:14 PM谢谢博主
由 two536 發表於 February 8, 2009 02:02 PMjserv 兄,这篇文章的(下)写好没有,很期待啊。
很期待你的“後續的篇幅”对“fast-booting”的探讨。
看到最後才發現是 jserv 寫的..難怪條理清晰觀念清楚...(絕對不是拍馬屁)
由 sfc 發表於 March 6, 2009 10:17 AMTo cyberj,jserv
基本上jserv的做法無誤
問題在於以第二種方法, 必須精確地把initramfs.cpio.gz的檔案長度透過boot option傳入, 否則gunzip 會嘗試繼續 decode檔案之後的空間, 造成gzip 錯誤
try trace unpack_to_rootfs() in init/initramfs.c
由 網路黑貓 發表於 March 24, 2009 11:55 AM