Linux 初始 RAM 磁碟(initrd)概述

來源:互聯網
上載者:User

Linux 初始 RAM 磁碟(initrd)是在系統引導過程中掛載的一個臨時根檔案系統,用來支援兩階段的引導過程。initrd 檔案中包含了各種可執行程式和驅動程式,它們可以用來掛載實際的根檔案系統,然後再將這個 initrd RAM 磁碟卸載,並釋放記憶體。在很多嵌入式 Linux 系統中,initrd 就是最終的根檔案系統。本文將探索 Linux 2.6 的初始 RAM 磁碟,包括如何建立以及如何在 Linux 核心中使用。

 

什麼是初始 RAM 磁碟?

初始 RAM 磁碟(initrd)是在實際根檔案系統可用之前掛載到系統中的一個初始根檔案系統。initrd 與核心綁定在一起,並作為核心引導過程的一部分進行載入。核心然後會將這個 initrd 檔案作為其兩階段引導過程的一部分來載入模組,這樣才能稍後使用真正的檔案系統,並掛載實際的根檔案系統。

initrd 中包含了實現這個目標所需要的目錄和可執行程式的最小集合,例如將核心模組載入到核心中所使用的 insmod 工具。

在案頭或伺服器 Linux 系統中,initrd 是一個臨時的檔案系統。其生存周期很短,只會用作到真實檔案系統的一個橋樑。在沒有存放裝置的嵌入式系統中,initrd 是永久的根檔案系統。本文將對這兩種情況進行探索。



回頁首

initrd 剖析

initrd 映像中包含了支援 Linux 系統兩階段引導過程所需要的必要可執行程式和系統檔案。

根據我們啟動並執行 Linux 的版本不同,建立初始 RAM 磁碟的方法也可能會有所不同。在 Fedora Core 3 之前,initrd 是使用 loop 裝置 來構建的。loop 裝置 是一個裝置驅動程式,利用它可以將檔案作為一個塊裝置掛載到系統中,然後就可以查看這個檔案系統中的內容了。在您的核心中可能並沒有 loop 裝置,不過這可以通過核心組態工具(make menuconfig)選擇 Device Drivers > Block Devices > Loopback Device Support 來啟用。我們可以按照下面的方法來查看 loop 裝置的內容(initrd 檔案的名字可能會稍有不同):

清單 1. 查看 initrd 的內容(適用於 FC3 之前的版本)

# 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 結尾,它也可能是一個壓縮檔,您可以給這個檔案添加上 .gz 尾碼,然後再使用 gunzip 對其進行解壓。

從 Fedora Core 3 開始,預設的 initrd 映像變成了一個經過壓縮的 cpio 歸檔檔案。我們不用再使用 loop 裝置來將 initrd 作為壓縮映像進行掛載,而是可以將其作為 cpio 歸檔檔案來使用。要查看 cpio 歸檔檔案的內容,可以使用下面的命令:

清單 2. 查看 initrd 的內容(適用於 FC3 及其以後的版本)

# 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 

結果會產生一個很小的根檔案系統,如清單 3 所示。在 ./bin 目錄中有一組很少但卻非常必要的應用程式,包括 nash(即 not a shell,是一個指令碼解譯器)、insmod(用來載入核心模組)和 lvm(邏輯卷管理工具)。

清單 3. 預設的 Linux initrd 目錄結構

# 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 bindrwxr-xr-x  2  root root    4096 May 7 02:48 devdrwxr-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.imgdrwxr-xr-x  2  root root    4096 May 7 02:48 libdrwxr-xr-x  2  root root    4096 May 7 02:48 loopfsdrwxr-xr-x  2  root root    4096 May 7 02:48 proclrwxrwxrwx  1  root root       3 May 7 02:48 sbin -> bindrwxr-xr-x  2  root root    4096 May 7 02:48 sysdrwxr-xr-x  2  root root    4096 May 7 02:48 sysroot#

清單 3 中比較有趣的是 init 檔案就在根目錄中。與傳統的 Linux 引導過程類似,這個檔案也是在將 initrd 映像解壓到 RAM 磁碟中時被調用的。在本文稍後我們將來探索這個問題。



回頁首

建立 initrd 所使用的工具

cpio 命令

使用 cpio 命令,我們可以對 cpio 檔案進行操作。cpio 是一種檔案格式,它簡單地使用檔案頭將一組檔案串接在一起。cpio 檔案格式可以使用 ASCII 和二進位檔案。為了保證可移植性,我們可以使用 ASCII 格式。為了減小檔案大小,我們可以使用二進位的版本。

下面讓我們回到最開始,來看一下 initrd 映像最初是如何構建的。對於傳統的 Linux 系統來說,initrd 映像是在 Linux 構建過程中建立的。有很多工具,例如 mkinitrd,都可以用來使用必要的庫和模組自動構建 initrd,從而用作與真實的根檔案系統之間的橋樑。mkinitrd 工具實際上就是一個 shell 指令碼,因此我們可以看到它究竟是如何來實現這個結果的。還有一個 YAIRD(即 Yet Another Mkinitrd)工具,可以對 initrd 構建過程的各個方面進行定製。



回頁首

手工構建定製的初始 RAM 磁碟

由於在很多基於 Linux 的嵌入式系統上沒有硬碟,因此 initrd 也會作為這種系統上的永久根檔案系統使用。清單 4 顯示了如何建立一個 initrd 映像檔案。我使用了一個標準的 Linux 案頭,這樣您即使沒有嵌入式平台,也可以按照下面的步驟來執行了。除了交叉編譯,其他概念(也適用於 initrd 的構建)對於嵌入式平台都是相同的。

清單 4. 建立定製 initrd 的工具(mkird)

#!/bin/bash# Housekeeping...rm -f /tmp/ramdisk.imgrm -f /tmp/ramdisk.img.gz# Ramdisk ConstantsRDSIZE=4000BLKSIZE=1024# Create an empty ramdisk imagedd 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 populatemount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0# Populate the filesystem (subdirectories)mkdir /mnt/initrd/binmkdir /mnt/initrd/sysmkdir /mnt/initrd/devmkdir /mnt/initrd/proc# Grab busybox and create the symbolic linkspushd /mnt/initrd/bincp /usr/local/src/busybox-1.1.1/busybox .ln -s busybox ashln -s busybox mountln -s busybox echoln -s busybox lsln -s busybox catln -s busybox psln -s busybox dmesgln -s busybox sysctlpopd# Grab the necessary dev filescp -a /dev/console /mnt/initrd/devcp -a /dev/ramdisk /mnt/initrd/devcp -a /dev/ram0 /mnt/initrd/devcp -a /dev/null /mnt/initrd/devcp -a /dev/tty1 /mnt/initrd/devcp -a /dev/tty2 /mnt/initrd/dev# Equate sbin with binpushd /mnt/initrdln -s bin sbinpopd# Create the init filecat >> /mnt/initrd/linuxrc 

initrd Linux 發行版

Minimax 是一個開放源碼項目,其設計目標是成為一個全部封裝在 initrd 中的 Linux 發行版。它的大小是 32MB,為了盡量小,它使用了 BusyBox 和 uClibc。除了非常小之外,它還使用了 2.6 版本的 Linux 核心,並提供了很多有用的工具。

為了建立 initrd,我們最開始建立了一個空檔案,這使用了 /dev/zero(一個由零組成的碼流)作為輸入,並將其寫入到 ramdisk.img 檔案中。所產生的檔案大小是 4MB(4000 個 1K 大小的塊)。然後使用 mke2fs 命令在這個空檔案上建立了一個 ext2(即 second extended)檔案系統。現在這個檔案變成了一個 ext2 格式的檔案系統,我們使用 loop 裝置將這個檔案掛載到 /mnt/initrd 上了。在這個掛載點上,我們現在就有了一個目錄,它以 ext2 檔案系統的形式呈現出來,我們可以對自己的 initrd 檔案進行拼裝了。接下來的指令碼提供了這種功能。

下一個步驟是建立構成根檔案系統所需要的子目錄:/bin、/sys、/dev 和 /proc。這裡只列出了所需要的目錄(例如沒有庫),但是其中包含了很多功能。

ext2 檔案系統的替代品

儘管 ext2 是一種通用的 Linux 檔案系統格式,但是還有一些替代品可以減小 initrd 映像檔案以及所掛載上來的檔案系統的大小。這種檔案系統的例子有 romfs(ROM 檔案系統)、cramfs(壓縮 ROM 檔案系統)和 squashfs(高度壓縮唯讀檔案系統)。如果我們需要暫時將資料寫入檔案系統中,ext2 可以很好地實現這種功能。最後,e2compr 是 ext2 檔案系統驅動程式的一個擴充,可以支援在線壓縮。

為了可以使用根檔案系統,我們使用了 BusyBox。這個工具是一個單一映像,其中包含了很多在 Linux 系統上通常可以找到的工具(例如 ash、awk、sed、insmod 等)。BusyBox 的優點是它將很多工具打包成一個檔案,同時還可以共用它們的通用元素,這樣可以極大地減少映像檔案的大小。這對於嵌入式系統來說非常理想。將 BusyBox 映像從自己的來源目錄中拷貝到自己根目錄下的 /bin 目錄中。然後建立了很多符號連結,它們都指向 BusyBox 工具。BusyBox 會判斷所調用的是哪個工具,並執行這個工具的功能。我們在這個目錄中建立了幾個連結來支援 init 指令碼(每個命令都是一個指向 BusyBox 的連結。)

下一個步驟是建立幾個特殊的裝置檔案。我從自己當前的 /dev 子目錄中直接拷貝了這些檔案,這使用了 -a 選項(歸檔)來保留它們的屬性。

倒數第二個步驟是產生 linuxrc 檔案。在核心掛載 RAM 磁碟之後,它會尋找 init 檔案來執行。如果沒有找到 init 檔案,核心就會調用 linuxrc 檔案作為自己的啟動指令碼。我們在這個檔案中實現對環境的基本設定,例如掛載 /proc 檔案系統。除了 /proc 之外,我還掛載了 /sys 檔案系統,並向終端列印一條訊息。最後,我們調用了 ash(一個 Bourne Shell 的複製),這樣就可以與根檔案系統進行互動了。linuxrc 檔案然後使用 chmod 命令修改成可執行檔。

最後,我們的根檔案系統就完成了。我們將其卸載掉,然後使用 gzip 對其進行壓縮。所產生的檔案(ramdisk.img.gz)被拷貝到 /boot 子目錄中,這樣就可以通過 GNU GRUB 對其進行載入了。

要構建初始 RAM 磁碟,我們可以簡單地調用 mkird,這樣就會自動建立這個映像檔案,並將其拷貝到 /boot 目錄中。



回頁首

測試定製的初始 RAM 磁碟

Linux 核心中對 initrd 的支援

對於 Linux 核心來說,要支援初始 RAM 磁碟,核心必須要使用 CONFIG_BLK_DEV_RAMCONFIG_BLK_DEV_INITRD 選項進行編譯。

新的 initrd 映像現在已經在 /boot 目錄中了,因此下一個步驟是使用預設的核心來對其進行測試。現在我們可以重新啟動 Linux 系統了。在出現 GRUB 介面時,按 C 鍵啟動 GRUB 中的命令列工具。我們現在可以與 GRUB 進行互動,從而定義要載入哪個核心和 initrd 映像檔案。kernel 命令讓我們可以指定核心檔案,initrd 命令可以用來指定 initrd 映像檔案。在定義好這些參數之後,就可以使用 boot 命令來引導核心了,如清單 5 所示。

清單 5. 使用 GRUB 手工引導核心和 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> bootUncompressing Linux... OK, booting the kernel.

在核心啟動之後,它會檢查是否有 initrd 映像檔案可用(稍後會更詳細介紹),然後將其載入,並將其掛載成根檔案系統。在清單 6 中我們可以看到這個 Linux 啟動過程最後的樣子。在啟動之後,ash shell 就可以用來輸入命令了。在這個例子中,我們將瀏覽一下根檔案系統的內容,並查看一下虛擬 proc 檔案系統中的內容。我們還展示了如何通過 touch 命令在檔案系統中建立檔案。注意所建立的第一個進程是 linuxrc(通常都是 init)。

清單 6. 使用簡單的 initrd 引導 Linux 核心

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



回頁首

使用初始 RAM 磁碟來引導系統

現在我們已經瞭解了如何構建並使用定製的初始 RAM 磁碟,本節將探索核心是如何識別 initrd 並將其作為根檔案系統進行掛載的。我們將介紹啟動鏈中的幾個主要函數,並解釋一下到底在進行什麼操作。

引導載入程式,例如 GRUB,定義了要載入的核心,並將這個核心映像以及相關的 initrd 拷貝到記憶體中。我們可以在 Linux 核心原始碼目錄中的 ./init 子目錄中找到很多這種功能。

在核心和 initrd 映像被解壓並拷貝到記憶體中之後,核心就會被調用了。它會執行不同的初始化操作,最終您會發現自己到了 init/main.c:init()(subdir/file:function)函數中。這個函數執行了大量的子系統初始化操作。此處會執行一個對 init/do_mounts.c:prepare_namespace() 的調用,這個函數用來準備名稱空間(掛載 dev 檔案系統、RAID 或 md、裝置以及最後的 initrd)。載入 initrd 是通過調用 init/do_mounts_initrd.c:initrd_load() 實現的。

initrd_load() 函數調用了 init/do_mounts_rd.c:rd_load_image(),它通過調用 init/do_mounts_rd.c:identify_ramdisk_image() 來確定要載入哪個 RAM 磁碟。這個函數會檢查映像檔案的 magic 號來確定它是 minux、etc2、romfs、cramfs 或 gzip 格式。在返回到 initrd_load_image 之前,它還會調用 init/do_mounts_rd:crd_load()。這個函數負責為 RAM 磁碟分配空間,並計算迴圈冗餘校正碼(CRC),然後對 RAM 磁碟映像進行解壓,並將其載入到記憶體中。現在,我們在一個適合掛載的塊裝置中就有了這個 initrd 映像。

現在使用一個 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() 來真正掛載根檔案系統,然後 chdir 到這個檔案系統中。這就是我們在清單 6 中所看到的熟悉訊息 VFS: Mounted root (ext2 file system). 的地方。

最後,返回到 init 函數中,並調用 init/main.c:run_init_process。這會導致調用 execve 來啟動 init 進程(在本例中是 /linuxrc)。linuxrc 可以是一個可執行程式,也可以是一個指令碼(條件是它有指令碼解譯器可用)。

這些函數的調用階層如清單 7 所示。儘管此處並沒有列出拷貝和掛載初始 RAM 磁碟所涉及的所有函數,但是這足以為我們提供一個整體流程的粗略架構。

清單 7. initrd 載入和掛載過程中所使用的主要函數的階層

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



回頁首

無盤引導

與嵌入式引導的情況類似,本地磁碟(磁碟片或 CD-ROM)對於引導核心和 ramdisk 根檔案系統來說都不是必需的。DHCP(Dynamic Host Configuration Protocol)可以用來確定網路參數,例如 IP 位址和子網路遮罩。TFTP(Trivial File Transfer Protocol)可以用來將核心映像和初始 ramdisk 映像傳輸到本地裝置上。傳輸完成之後,就可以引導 Linux 核心並掛載 initrd 了,這與本地映像引導的過程類似。



回頁首

壓縮 initrd

在構建嵌入式系統時,我們可能希望將 initrd 映像檔案做得儘可能小,這其中有一些技巧需要考慮。首先是使用 BusyBox(本文中已經展示過了)。BusyBox 可以將數 MB 的工具壓縮成幾百 KB。

在這個例子中,BusyBox 映像是靜態連結的,因此它不需要其他庫。然而,如果我們需要標準的 C 庫(我們自己定製的二進位可能需要這個庫),除了巨大的 glibc 之外,我們還有其他選擇。第一個較小的庫是 uClibc,這是為對空間要求非常嚴格的系統準備的一個標準 C 庫。另外一個適合空間緊張的環境的庫是 dietlib。要記住我們需要使用這些庫來重新編譯想在嵌入式系統中重新編譯的二進位檔案,因此這需要額外再做一些工作(但是這是非常值得的)。



回頁首

結束語

初始 RAM 磁碟最初是設計用來通過一個臨時根檔案系統來作為核心到最終的根檔案系統之間的橋樑。initrd 對於在嵌入式系統中載入到 RAM 磁碟裡的非持久性根檔案系統來說也非常有用。

參考資料

學習

  • 您可以參閱本文在 developerWorks 全球網站上的 英文原文 。
  • “Linux 引導過程內幕”(developerWorks,2006 年 5 月)探索了 Linux 從最初的 bootstrap 到啟動第一個使用者空間應用程式的過程。
  • 在 “從 FireWire 裝置引導 Linux”(developerWorks,2004 年 7 月)中,我們可以學習在各種平台的各種裝置上如何(使用 initrd)啟動 Linux。
  • cpio 檔案格式 既簡單又簡潔。因此 Fedora 團隊選擇使用它作為 initrd 的格式就沒什麼奇怪的了。
  • mkinitrd 工具非常適合建立 initrd 映像檔案。除了建立 initrd 映像之外,它還可以確定要為您的系統載入哪些模組,並將這些模組也全部加入到這個映像中。
  • loop 裝置 是一個非常有用的驅動程式,可以將映像檔案作為檔案系統掛載。
  • Network Boot and Exotic Root HOWTO 不但介紹了從網路上引導 Linux 的過程,還介紹了諸如磁碟片引導、CD-ROM 引導和嵌入式環境中的內容。
  • 在 developerWorks Linux 專區 中可以找到為 Linux 開發人員準備的更多資源。
  • 隨時關注 developerWorks 技術事件和網路廣播。

獲得產品和技術

  • cpio 檔案格式(現在可以用作 Fedora Core 的一種 initrd 映像格式)具有很長的曆史,可以在很多 UNIX 系統上使用。
  • ash shell 是 Bourne Shell 的一個複製(它們大部分是相容的),它雖然很小,但是完全可以正常工作。它非常適合在對空間要求非常嚴格的嵌入式系統上用作指令碼解譯器。
  • BusyBox 是一種縮減您下一個嵌入式 Linux 項目記憶體需求的好方法。
  • 要進一步縮減 initrd 檔案的大小,請考慮使用 glibc 的替代庫,例如 uClibc 或 dietlib。如果您喜歡使用 C++,那麼可以試用一下 uClibc++ 庫的 Alpha 版本。
  • Minimax 是一個完全封裝在 initrd 映像檔案中的 Linux 發行版!
  • 訂購免費的 SEK for Linux,這有兩張 DVD,包括最新的 IBM for Linux 的試用軟體,包括 DB2、Lotus、Rational、Tivoli 和 WebSphere。
  • 在您的下一個開發項目中採用 IBM 試用軟體,這可以從 developerWorks 上直接下載。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.