linux記憶體磁碟初始化技術

來源:互聯網
上載者:User

linux記憶體磁碟初始化技術

2010-09-25 23:13轉載自
分享最終編輯
lifelens

linux記憶體初始化技術(initrd)用於支援兩階段的系統引導過程,是在系統啟動過程中被掛載的臨時root檔案系統(譯者註:這裡的root檔案系統是指的根檔案系統)。initrd包含很多可執行程式和驅動,並允許在臨時的記憶體磁碟根檔案系統被卸載,記憶體被釋放後掛載真實的root檔案系統。在許多嵌入式linux檔案系統中,initrd是最終的根檔案系統。這篇文章主要講解了linux2.6核心的initrd技術,包括在核心中的建立及使用。

1 什麼是記憶體磁碟初始化?
initrd掛載優先順序高於真實根檔案系統,它被邦定在核心上,做為核心啟動過程的一部分被載入(load)。然後,做為兩階段引導過程的第一部分,核心掛載(mount)initrd,用於獲得並載入真實有效檔案系統。
為了達到這個目的,initrd包含有最起碼的目錄與程式,例如insmod,來安裝核心模組到核心中。
對於案頭或伺服器linux,initrd是臨時檔案系統,它的生存周期很短,僅僅是做為到達真實根檔案系統的橋樑。但對於沒有存放裝置的嵌入式系統來說,它才是永久性的根檔案系統。本篇文章對這兩方面均有涉及。

2 深入分析initrd
initrd包含有必須的程式和系統檔案,用於支援系統的啟動的第二階段過程。建立初始化記憶體的方法,是隨著你所使用的系統版本而改變的。從Fedora Core3以後,initrd就由回送裝置(loop device)建立。什麼是回送裝置?它是一個裝置驅動,允許你將一個檔案掛載為塊裝置,並對其檔案系統做出描述。也許loop device並不存在與你的核心中,但是你能夠通過核心的組態工具(make menuconfig)開啟它。路徑是:Device Drivers-》Block Devices-》LoopBack Device
support。下面為檢查命令:
# mkdir temp ; cd temp
# cp /boot/initrd.img.gz .
# gunzip initrd.img.gz
# mount -t ext -o loop initrd.img /mnt/initrd
# ls -la /mnt/initrd
#
現在,你可以通過查看/mnt/initrd的子目錄來查看initrd的內容。需要注意的是,即使你的initrd鏡像檔案並不是以.gz做為尾碼名,但是你同樣可以通過增加此尾碼名來讓gunzip開啟它。
從Fedora Core3開始,預設的initrd鏡像就是一個壓縮的gpio歸檔檔案。除了用掛載檔案的方式以外,你同樣可以通過cpio歸檔的方式來將其掛載成使用了回送裝置的壓縮鏡像。你可以通過以下的指令來檢查這個cpio歸檔檔案的內容:
# mkdir temp ; cd temp
# cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz
# gunzip initrd-2.6.14.2.img.gz
# cpio -i --make-directories < initrd-2.6.14.2.img
    你看到的結果將是一個小型根檔案系統,如下所示:

# ls -la
#
drwxr-xr-x 10 root root    4096 May 7 02:48 .
drwxr-x--- 15 root root    4096 May 7 00:54 ..
drwxr-xr-x 2 root root    4096 May 7 02:48 bin
drwxr-xr-x 2 root root    4096 May 7 02:48 dev
drwxr-xr-x 4 root root    4096 May 7 02:48 etc
-rwxr-xr-x 1 root root     812 May 7 02:48 init
-rw-r--r-- 1 root root 1723392 May 7 02:45 initrd-2.6.14.2.img
drwxr-xr-x 2 root root    4096 May 7 02:48 lib
drwxr-xr-x 2 root root    4096 May 7 02:48 loopfs
drwxr-xr-x 2 root root    4096 May 7 02:48 proc
lrwxrwxrwx 1 root root       3 May 7 02:48 sbin -> bin
drwxr-xr-x 2 root root    4096 May 7 02:48 sys
drwxr-xr-x 2 root root    4096 May 7 02:48 sysroot
#

一些小的,但是很有必要的程式組合能在./bin目錄下得到,包括nash(它不是一個shell,而是一個指令碼解釋工具),用於載入核心模組的insmod,以及lvm等。
上面所示目錄中,相對比較有趣的是root目錄下的初始設定檔案。這些檔案,和傳統的linux啟動過程中一樣,是在initrd鏡像被解壓縮到RAM中時產生的。待會我們將繼續探討這個問題。

3 建立initrd的工具。
現在,讓我們回到一開始的討論:initrd的鏡像是如何被建立的?在傳統的linux系統中,initrd是在linux build的時候被建立的。像mkinitrd這樣的許許多多的工具,都能夠用於通過必須的庫和模組來自動構建一個用於過渡到真實根檔案系統的initrd。事實上,mkinitrd工具是一個指令檔,因此,我們能夠很清楚得看到,這個過程是如何進行的。同樣的,YAIRD (Yet Another Mkinitrd)工具,也允許我們自定製每一個initrd被構建的階段。

4 自己動手,打造自訂的初始化記憶體盤
由於很多基於linux的嵌入式系統都沒有硬碟,initrd也可以做為永久性的根檔案系統。下面我就將告訴你們,如何建立一個initrd鏡像。我使用的是標準linux案頭系統,因此大家即使沒有嵌入式目標裝置也可以照著做。除了交叉編譯以外,嵌入式目標檔案的構建過程是相同的。

#!/bin/bash

# Housekeeping...
rm -f /tmp/ramdisk.img
rm -f /tmp/ramdisk.img.gz

# Ramdisk Constants
RDSIZE=4000
BLKSIZE=1024

# Create an empty ramdisk image
dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE

# Make it an ext2 mountable file system
/sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE

# Mount it so that we can populate
mount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0

# Populate the filesystem (subdirectories)
mkdir /mnt/initrd/bin
mkdir /mnt/initrd/sys
mkdir /mnt/initrd/dev
mkdir /mnt/initrd/proc

# Grab busybox and create the symbolic links
pushd /mnt/initrd/bin
cp /usr/local/src/busybox-1.1.1/busybox .
ln -s busybox ash
ln -s busybox mount
ln -s busybox echo
ln -s busybox ls
ln -s busybox cat
ln -s busybox ps
ln -s busybox dmesg
ln -s busybox sysctl
popd

# Grab the necessary dev files
cp -a /dev/console /mnt/initrd/dev
cp -a /dev/ramdisk /mnt/initrd/dev
cp -a /dev/ram0 /mnt/initrd/dev
cp -a /dev/null /mnt/initrd/dev
cp -a /dev/tty1 /mnt/initrd/dev
cp -a /dev/tty2 /mnt/initrd/dev

# Equate sbin with bin
pushd /mnt/initrd
ln -s bin sbin
popd

# Create the init file
cat >> /mnt/initrd/linuxrc << EOF
#!/bin/ash
echo
echo "Simple initrd is active"
echo
mount -t proc /proc /proc
mount -t sysfs none /sys
/bin/ash --login
EOF

chmod +x /mnt/initrd/linuxrc

# Finish up...
umount /mnt/initrd
gzip -9 /tmp/ramdisk.img
cp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz

想建立initrd的話,你需要首先建立一個空檔案,將/dev/zero(0字元流)做為ramdisk.img的輸入。得到的檔案大小大約是4MB(有4000個1K的塊組成)。接下來,用mke2fs命令來建立一個使用這個空檔案的ext2檔案系統。現在,這個檔案就是一個ext2檔案系統。ok,接下來,以迴路裝置的形式掛載這個檔案到/mnt/initrd,現在,你就在掛載點擁有一個代表著ext2檔案系統的目錄,並用與存放你的initrd。其他大多數的指令碼語句都是用於實現這個功能。
下一步,就是建立一些必須的子目錄,用於產生你的根檔案系統: /bin, /sys, /dev, 和 /pro。這裡只需要少數幾個目錄,例如,沒有/lib。但是它們已經包含了大部分功能。

如果想讓你的根檔案系統發揮更大的作用,請使用 BusyBox。這個工具是一個包含了許多獨立工具的鏡像,這些獨立的工具你都能在linux中找到( ash, a等等wk, sed, insmod)。BusyBox的優勢在於,它把它們集合在了一起,並分享了公用的部分,從而極大縮小了鏡像的體積。這對於嵌入式系統來講,是非常理想的。請將BustBox鏡像從它的來源目錄中複製出來,到你的/bin目錄下,這樣,很多指向BusyBox工具集的符號連結將被建立,BusyBox能確定哪一個工具將被使用,並自動引用它。這個/bin目錄下被建立的連結的小型集合將用於對啟動指令碼的支援。
再下一步,就是一小部分特殊裝置檔案的建立。我從我的/dev檔案夾中直接拷貝了出來,別忘了加上-a選項來保持它們原有的屬性。
倒數第二步,就是產生linuxrc檔案。在核心掛載了記憶體盤之後,它將搜尋並執行相關的開機檔案,如果沒有找到,核心就將linuxrc檔案做為其啟動指令碼。你最好在這個檔案中對環境變數做一些基本設定,例如掛載/proc檔案系統等。除了/proc外,我還掛載了/sys檔案系統,將訊息發送給終端。最後,我調用ash並通過它和根檔案系統互動。最後記住,用chmod把linuxrc檔案的屬性改為可執行。
最後,你的根檔案系統算是ok了。現在它並沒有被掛載,用gzip將它壓縮,並將壓縮後的檔案ramdisk.img.gz拷貝到/boot目錄下,這樣它就能被GRUB調用。
想要構建你的初始化ram盤的話,你只需要調用mkird,鏡像就將自動建立並拷貝到/boot目錄下。

5 測試自訂的初始化RAM盤。
   你擁有的新的initrd鏡像是在/boot目錄下,因此,下一步就是要用你預設的核心來測試它。ok,現在你可以先重新啟動你的linux系統,當GRUB引導畫面出現時,按下C鍵,開啟GRUB的命令列工具。現在,你就能通過GRUB確定啟動專門的核心和initrd鏡像。核心命令是允許你定製核心檔案的,而initrd命令則允許你指定專門的initrd鏡像檔案。當它們都被指定之後,通過啟動命令來啟動核心,如下所示:

    GNU GRUB version 0.95 (638K lower / 97216K upper memory)

[ Minimal BASH-like line editing is supported. For the first word, TAB
lists possible command completions. Anywhere else TAB lists the possible
completions of a device/filename. ESC at any time exits.]

grub> kernel /bzImage-2.6.1
   [Linux-bzImage, setup=0x1400, size=0x29672e]

grub> initrd /ramdisk.img.gz
   [Linux-initrd @ 0x5f2a000, 0xb5108 bytes]

grub> boot

Uncompressing Linux... OK, booting the kernel.

在核心啟動之後,它開始檢查initrd鏡像是否可用,如果答案是確定的,那麼就作為根檔案系統載入並掛載它。下面就是這個特殊啟動過程的結尾:

...
md: Autodetecting RAID arrays
md: autorun
md: ... autorun DONE.
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 file system).
Freeing unused kernel memory: 208k freed
/ $ ls
bin         etc       linuxrc       proc        sys
dev         lib       lost+found    sbin
/ $ cat /proc/1/cmdline
/bin/ash/linuxrc
/ $ cd bin
/bin $ ls
ash      cat      echo     mount    sysctl
busybox dmesg    ls       ps
/bin $ touch zfile
/bin $ ls
ash      cat      echo     mount    sysctl
busybox dmesg    ls       ps       zfile

當啟動之後,可以通過ash來進入命令模式。在本例中,我探究了根檔案系統並向你示範了,你能通過建立檔案來寫入這個檔案系統。只需要注意,第一步是要建立linuxrc。

6 通過初始化記憶體盤啟動
現在,大家已經看到了如何構建並使用一個自定製的初始化記憶體盤,這一節則用於介紹,核心是如何辨認initrd並將其作為它的根檔案系統掛載的。我將涉及一些boot chain中的主要的函數並對發生的事件做出解釋。
像GRUB這樣的boot loader,通常會確認即將載入的核心並複製該核心鏡像與任何相關聯的initrd到記憶體中,你可以在你linux核心來源程式目錄下的./init子目錄中找到這些功能實現。
在核心與initrd鏡像被解壓縮和複製到記憶體後,核心被調用。此時,開始各種各樣的初始化過程,最終,你會發現自己處於init/main.c:init() (subdir/file:function)。這個函數實現了很多的子系統初始化。在這裡,要調用init/do_mounts.c:prepare_namespace(),用來準備命名空間(掛載dev 檔案系統, RAID, 或者md, devices, 以及, 最後的initrd)。通過對 init/do_mounts_initrd.c:initrd_load()的調用,最終完成對initrd的載入。
initrd_load()調用init/do_mounts_rd.c:rd_load_image(),來決定是否通過調用init/do_mounts_rd.c:identify_ramdisk_image()來載入記憶體盤鏡像。後面這個函數通過檢查核心的編號來確定檔案究竟是是minux,etc2,romfs,cramfs,還是gzip格式,直到返回initrd_load_image後,init/do_mounts_rd:crd_load()又被調用。這個函數負責分配空間給記憶體盤,並進行校正計算,解壓縮,最後將記憶體盤鏡像載入到記憶體中。此時,你就已經擁有了一個適合於掛載的,在塊裝置中的initrd鏡像。
現在,通過調用init/do_mounts.c:mount_root()將這個塊裝置做為root掛載。ok,根裝置就被建立了,接下來調用的函數是init/do_mounts.c:mount_block_root(),此函數又調用fs/namespace.c:sys_mount()來掛載真實的根檔案系統並對其進行chdir操作。
最後,會返回到啟動函數中,並調用init/main.c:run_init_process。調用的結果是,初始化進程開始(在這裡是通過/linuxrc)。linuxrc可以是一個可執行程式,也可以是指令碼(只要指令碼解譯器能夠正常解釋它)。
函數調用的層次關係可以從下表中看出。並不是所有與複製、掛載初始化記憶體盤的函數都被列舉出來,這裡僅僅是大概的,對整體基本流程的回顧:
init/main.c:init
init/do_mounts.c:prepare_namespace
    init/do_mounts_initrd.c:initrd_load
      init/do_mounts_rd.c:rd_load_image
        init/do_mounts_rd.c:identify_ramdisk_image
        init/do_mounts_rd.c:crd_load
          lib/inflate.c:gunzip
    init/do_mounts.c:mount_root
      init/do_mounts.c:mount_block_root
         init/do_mounts.c:do_mount_root
           fs/namespace.c:sys_mount
init/main.c:run_init_process
    execve

7 無盤啟動的應用
同很多嵌入式系統的啟動一樣,本地磁碟(軟碟機或者光碟機)對於啟動核心和記憶體盤根檔案系統來說,並不是必須的。DHCP工具能被用於確認網路參數,例如大家熟悉的IP抵制和子網路遮罩等。此外,TFTP能被用於將核心鏡像以及初始化記憶體盤鏡像傳送到本地裝置。一旦傳輸完成,linux核心就能被啟動以及掛載initrd,和本地鏡像啟動的過程一樣。

8 讓你的initrd儘可能小
當你在構建嵌入式系統時,總是希望initrd的鏡像儘可能小,恩,這裡將提供一些小技巧。首先就是使用BusyBox。前面已經提到過,BusyBox包含了很多較大的工具,通常體積都以MB計算,但是它成功得將自己的體積控制在幾百KB的範圍內。
在本例中,BusyBox鏡像使用的是靜態連結,因此不需要提供任何庫檔案。但是,如果你需要得到標準的C庫檔案來滿足自己的二進位程式,除了大體積的glibc庫,你有其他更好的選擇。第一個,小體積的uClibc庫,是專門用於有空間限制的,標準C庫的縮水版本。另一個適用於有空間限制環境的庫是dietlib。記住,你需要在自己的嵌入式系統中,用這些庫重新編譯你的二進位程式。雖然使用它們會帶來一些附加的工作,但是,是值得的。

9 總結
初始化記憶體盤技術被建立的最初目的,是為了讓核心通過一個臨時的根檔案系統來過渡到最終的根檔案系統。initrd對於嵌入式linux系統同樣是很有用處的:它能做為一個非持久性的根檔案系統掛載到記憶體盤中。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.