星期五, 4月 11, 2008

initramfs 簡介,一個新的 initial RAM disks 的模型

譯自
http://linuxdevices.com/articles/AT4017834659.html
https://landley.net/writing/rootfs-intro.html
by Rob Landley, TimeSys (Mar. 15, 2005)

initramfs 簡介,一個新的 initial RAM disks 的模型

問題

當 Linux 核心啟動系統,它必須找到並執行第一個使用者程式,通常是 init。使用者程式存在檔案系統,故 Linux 核心必須找到並掛上第一個(根)檔案系統,方能成功開機。

通常,可用的檔案系統都列在 /etc/fstab,所以 mount 可以找到它們。但 /etc/fstab 它本身就是一個檔案,存在檔案系統中。找到第一個檔案系統成為雞生蛋蛋生雞的問題,而且為了解決它,核心開發者建立核心命令列選項 root=,用來指定 root 檔案系統存在哪個裝置。

十五年前,root= 可以很容易地解譯。它可以是軟碟或硬碟上的分割區。如今 root 檔案系統可以存在各種不同類型的硬體(SCSI, SATA, flash MTD) ,或是由不同類型硬體所建立的 RAID 上。它的位置隨著不同啟動而不同,像是可熱插拔的 USB 裝置被插到有多個 USB 孔的系統上 - 當有多個 USB 裝置時,哪一個是正確的?root 檔案系統也可能被壓縮(如何?),被加密(用什麼 keys?),或 loopback 掛載(哪裡?)。它甚至可以存在外部的網路伺服器,需要核心去取得 DHCP 位址,完成 DNS lookup,並登入到遠端伺服器(需帳號及密碼),全部都在核心可以找到並執行第一個 userspace 程式。

如今,root= 已沒有足夠的資訊。即使將所有特殊案例的行為都放進核心也無法幫助裝置列舉,加密金鑰,或網路登入這些隨著系統不同而不同的系統。更糟的是,替核心加入這些複雜的工作,就像是用組合語言寫 web 軟體 - 可以做到,但使用適當的工具會更容易完成。核心是被設計成服從命令,而不是給命令。

為了這個不斷增加複雜度的工作, 核心開發者決定去尋求更好的方法來解決這整個問題。

解決方法

Linux 2.6 核心將一個小的 ram-based initial root filesystem(initramfs) 包進核心,且若這個檔案系統包含一隻程式 /init,核心會將它當作第一隻程式執行。此時,找尋其他檔案系統並執行其他程式已不再是核心的問題,而是新程式的工作。

initramfs 的內容不需是一般功能。若給定的系統的 root 檔案系統存在一個加密過的網路區塊裝置,且網路位址、登入、加密金鑰都存在 USB 裝置裡的檔案 "larry" (需密碼方能存取),系統的 initramfs 可以有特殊功能的程式,它知道這些事,並使這可以運作。

對系統而言,不需要很大的 root 檔案系統,也不需要定址或切換到任何其他 root 檔案系統。

這跟 initrd 有何不同?

Linux kernel 已經有方法提供 ram-based root filesystem,initrd 機制。對 2.4 及更早的 kernel 來說,initrd 仍然是唯一的方法去做這一連串的事。但 kernel 開發者為了幾個原因而選擇在 2.6 實作一個新的機制。

ramdisk 對上 ramfs

ramdisk (如 initrd) 是 ram based 區塊裝置,這表示它是一塊固定大小的記憶體,它可以被格式化及掛載,就像磁碟一樣。這表示 ramdisk 的內容需先格式化並用特別的工具(像是 mke2fs 及 losetup)做前置作業,而且如同所有的區塊裝置,它需要檔案系統驅動程式在執行時期做翻譯。這也有人工的大小限制不論是浪費空間(若 ramdisk 沒有滿,已被佔用的額外的記憶體也不能用來做其他事)或容量限制(若 ramdisk 滿了,但其他仍有閒置的記憶體,也不能不經由重新格式化將它擴展)。

但實際上 ramdisks 浪費更多的記憶體在快取上。Linux 被設計成將所有從區塊裝置讀進來或寫出去的檔案及目錄做快取,所以 Linux 會複製資料到 ramdisk 及從 ramdisk 複製資料到 "page cache"(對於檔案資料),及"dentry cache"(對於目錄)。ramdisk 偽裝成區塊裝置的最大缺點就是被當作(需要 cache 的)區塊裝置(i.e. 白白浪費了記憶體)(謝謝:Wenkui Liang)
但 ramdisk 實際上浪費更多記憶體於快取上。Linux 被設計為將所有的檔案及目錄做快取,不論是對區塊的讀出或寫入,所以 Linux 複製資料到 ramdisk及從 ramdisk 複製資料出來,page cache 給 file data 用,而 dentry cache 給目錄用。ramdisk 的下面則假裝為區塊裝置。

幾年前,Linus Torvalds 有一個靈巧的想法:Linux 的 快取是否可以被掛載像是一個檔案系統?只要保持檔案在快取中且不要將它們清除,直到它們被刪除或系統重新啟動?Linus 寫了一小段程式將快取包起來,稱它為 ramfs,而其他的 kernel 開發者建立一個加強版本稱為 tmpfs(它可以寫資料到 swap,及限制掛載點的大小,所以在它消耗完所有可用的記憶體前它會填滿)。initramfs 是 tmpfs 的實例。

這些 ram based 的檔案系統自己成長或縮小以符合資料所需的大小。增加檔案到 ramfs(或加大原有的檔案)會自動配置更多的記憶體,並刪除或截去檔案釋放記憶體。在區塊裝置及快取間沒有複製動作,因為沒有區塊裝置。在快取中的複製只是資料的複製。更棒的是這不是新的程式碼,而是已存在的 Linux 快取程式碼的新應用,這表示它幾乎沒有增加大小,非常簡單,且基於已經歷測試的基礎上。

系統使用 initramfs 作為它的 root 檔案系統甚至不需要將檔案系統驅動程式內建到 kernel,因為沒有區塊裝置要用來做檔案系統。只是存在記憶體中的檔案罷了。

initrd 對上 initramfs

在底層架構的改變是 kernel 開發者建立一個新的實作的理由,但當他們在那裡時他們清除了很多不好的行為及假設。

initrd 被設計為舊的 root= 的 root 裝置偵測程式碼的前端,而不是取代它。它執行 /linuxrc,這被用來完成設定功能(像是登入網路,決定哪個裝置含有 root 分割區,或用檔案做為 loopback 裝置),告訴 kernel 哪個區塊裝置含有真的 root 裝置(藉由寫人 de_t 數字到 /proc/sys/kernel/real-root-dev),且回傳給 kernel,所以 kernel 可以掛載真的 root 裝置及執行真的 init 程式。

這裡假設 read root device 是區塊裝置而不是網路分享出來的,同時也假設 initrd 不是它自己去做為真的 root 檔案系統。kernel 也不會執行 /linuxrc 做為特別的 process ID 1,因為這個 process ID(它有特別的屬性,像是做為唯一無法被以 kill -9 的 process) 被保留給 init,kernel 在它掛載真的 root 檔案系統後會等它執行。

用 initramfs,kernel 開發者移除所有的假設。當 kernel 啟動在 initramfs 外的 /init,kernel 即做好決定並回去等待接受命令。用 initramfs,kernel 不需要關心真的 root 檔案系統在哪裡,而在 initramfs 的 /init 被執行為真的 init,以 PID 1。(若 initramfs 的 init 需要不干涉特別的 PID 給其他程式,它可以用 exec() 系統呼叫,就像其他人一樣)

結語

傳統的 root= kernel 命令列選項仍然被支援且可用。

1 則留言:

wkliang 提到...

"The downside" 應該翻譯成:缺點。

The downside of the ramdisk pretending to be a block device is it gets treated like a block device. 可以翻譯成:ramdisk 偽裝成區塊裝置的最大缺點就是被當作(需要 cache 的)區塊裝置
(i.e. 白白浪費了記憶體)