Linux2.6 核心的 Initrd 機制解析
initrd 的英文含義是 boot loader initialized RAM disk,就是由 boot loader 初始化的記憶體盤。在 linux核心啟動前, boot loader 會將儲存介質中的 initrd 檔案載入到記憶體,核心啟動時會在訪問真正的根檔案系統前先訪問該記憶體中的 initrd 檔案系統。在 boot loader 配置了 initrd 的情況下,核心啟動被分成了兩個階段,第一階段先執行 initrd 檔案系統中的"某個檔案",完成載入驅動模組等任務,第二階段才會執行真正的根檔案系統中的 /sbin/init 程序。這裡提到的"某個檔案",Linux2.6 核心會同以前版本核心的不同,所以這裡暫時使用了"某個檔案"這個稱呼,後面會詳細講到。第一階段啟動的目的是為第二階段的啟動掃清一切障愛,最主要的是載入根檔案系統儲存介質的驅動模組。我們知道根檔案系統可以儲存在包括IDE、SCSI、USB在內的多種介質上,如果將這些裝置的驅動都編譯進核心,可以想象核心會多麼龐大、臃腫。
Initrd 的用途主要有以下四種:
1. linux 發行版的必備部件
linux 發行版必須適應各種不同的硬體架構,將所有的驅動編譯進核心是不現實的,initrd 技術是解決該問題的關鍵技術。Linux 發行版在核心中只編譯了基本的硬體驅動,在安裝過程中通過檢測系統硬體,生成包含安裝系統硬體驅動的 initrd,無非是一種即可行又靈活的解決方案。
2. livecd 的必備部件
同 linux 發行版相比,livecd 可能會面對更加複雜的硬體環境,所以也必須使用 initrd。
3. 製作 Linux usb 啟動盤必須使用 initrd
usb 裝置是啟動比較慢的裝置,從驅動載入到裝置真正可用大概需要幾秒鐘時間。如果將 usb 驅動編譯進核心,核心通常不能成功訪問 usb 裝置中的檔案系統。因為在核心訪問 usb 裝置時, usb 裝置通常沒有初始化完畢。所以常規的做法是,在 initrd 中載入 usb 驅動,然後休眠幾秒中,等待 usb裝置初始化完畢後再掛載 usb 裝置中的檔案系統。
4. 在 linuxrc 指令碼中可以很方便地啟用個性化 bootsplash。
|
為了使讀者清晰的瞭解Linux2.6核心initrd機制的變化,在重點介紹Linux2.6核心initrd之前,先對linux2.4核心的initrd進行一個簡單的介紹。Linux2.4核心的initrd的格式是檔案系統映象檔案,本文將其稱為image-initrd,以區別後面介紹的linux2.6核心的cpio格式的initrd。 linux2.4核心對initrd的處理流程如下:
1. boot loader把核心以及/dev/initrd的內容載入到記憶體,/dev/initrd是由boot loader初始化的裝置,儲存著initrd。
2. 在核心初始化過程中,核心把 /dev/initrd 裝置的內容解壓縮並拷貝到 /dev/ram0 裝置上。
3. 核心以可讀寫的方式把 /dev/ram0 裝置掛載為原始的根檔案系統。
4. 如果 /dev/ram0 被指定為真正的根檔案系統,那麼核心跳至最後一步正常啟動。
5. 執行 initrd 上的 /linuxrc 檔案,linuxrc 通常是一個指令碼檔案,負責載入核心訪問根檔案系統必須的驅動, 以及載入根檔案系統。
6. /linuxrc 執行完畢,真正的根檔案系統被掛載。
7. 如果真正的根檔案系統存在 /initrd 目錄,那麼 /dev/ram0 將從 / 移動到 /initrd。否則如果 /initrd 目錄不存在, /dev/ram0 將被解除安裝。
8. 在真正的根檔案系統上進行正常啟動過程 ,執行 /sbin/init。 linux2.4 核心的 initrd 的執行是作為核心啟動的一箇中間階段,也就是說 initrd 的 /linuxrc 執行以後,核心會繼續執行初始化程式碼,我們後面會看到這是 linux2.4 核心同 2.6 核心的 initrd 處理流程的一個顯著區別。
|
linux2.6 核心支援兩種格式的 initrd,一種是前面第 3 部分介紹的 linux2.4 核心那種傳統格式的檔案系統映象-image-initrd,它的製作方法同 Linux2.4 核心的 initrd 一樣,其核心檔案就是 /linuxrc。另外一種格式的 initrd 是 cpio 格式的,這種格式的 initrd 從 linux2.5 起開始引入,使用 cpio 工具生成,其核心檔案不再是 /linuxrc,而是 /init,本文將這種 initrd 稱為 cpio-initrd。儘管 linux2.6 核心對 cpio-initrd和 image-initrd 這兩種格式的 initrd 均支援,但對其處理流程有著顯著的區別,下面分別介紹 linux2.6 核心對這兩種 initrd 的處理流程。
1. boot loader 把核心以及 initrd 檔案載入到記憶體的特定位置。
2. 核心判斷initrd的檔案格式,如果是cpio格式。
3. 將initrd的內容釋放到rootfs中。
4. 執行initrd中的/init檔案,執行到這一點,核心的工作全部結束,完全交給/init檔案處理。
1. boot loader把核心以及initrd檔案載入到記憶體的特定位置。
2. 核心判斷initrd的檔案格式,如果不是cpio格式,將其作為image-initrd處理。
3. 核心將initrd的內容儲存在rootfs下的/initrd.image檔案中。
4. 核心將/initrd.image的內容讀入/dev/ram0裝置中,也就是讀入了一個記憶體盤中。
5. 接著核心以可讀寫的方式把/dev/ram0裝置掛載為原始的根檔案系統。
6. .如果/dev/ram0被指定為真正的根檔案系統,那麼核心跳至最後一步正常啟動。
7. 執行initrd上的/linuxrc檔案,linuxrc通常是一個指令碼檔案,負責載入核心訪問根檔案系統必須的驅動, 以及載入根檔案系統。
8. /linuxrc執行完畢,常規根檔案系統被掛載
9. 如果常規根檔案系統存在/initrd目錄,那麼/dev/ram0將從/移動到/initrd。否則如果/initrd目錄不存在, /dev/ram0將被解除安裝。
10. 在常規根檔案系統上進行正常啟動過程 ,執行/sbin/init。
通過上面的流程介紹可知,Linux2.6核心對image-initrd的處理流程同linux2.4核心相比並沒有顯著的變化, cpio-initrd的處理流程相比於image-initrd的處理流程卻有很大的區別,流程非常簡單,在後面的原始碼分析中,讀者更能體會到處理的簡捷。
沒有找到正式的關於cpio-initrd同image-initrd對比的文獻,根據筆者的使用體驗以及核心程式碼的分析,總結出如下三方面的區別,這些區別也正是cpio-initrd的優勢所在:
cpio-initrd的製作非常簡單,通過兩個命令就可以完成整個製作過程
|
而傳統initrd的製作過程比較繁瑣,需要如下六個步驟
|
本文不對上面命令的含義作細節的解釋,因為本文主要介紹的是linux核心對initrd的處理,對上面命令不理解的讀者可以參考相關文件。
通過上面initrd處理流程的介紹,cpio-initrd的處理流程顯得格外簡單,通過對比可知cpio-initrd的處理流程在如下兩個方面得到了簡化:
1. cpio-initrd並沒有使用額外的ramdisk,而是將其內容輸入到rootfs中,其實rootfs本身也是一個基於記憶體的檔案系統。這樣就省掉了ramdisk的掛載、解除安裝等步驟。
2. cpio-initrd啟動完/init程序,核心的任務就結束了,剩下的工作完全交給/init處理;而對於image-initrd,核心在執行完/linuxrc程序後,還要進行一些收尾工作,並且要負責執行真正的根檔案系統的/sbin/init。通過圖1可以更加清晰的看出處理流程的區別:
圖1核心對cpio-initrd和image-initrd處理流程示意圖
如圖1所示,cpio-initrd不再象image-initrd那樣作為linux核心啟動的一箇中間步驟,而是作為核心啟動的終點,核心將控制權交給cpio-initrd的/init檔案後,核心的任務就結束了,所以在/init檔案中,我們可以做更多的工作,而不比擔心同核心後續處理的銜接問題。當然目前linux發行版的cpio-initrd的/init檔案的內容還沒有本質的改變,但是相信initrd職責的增加一定是一個趨勢。
|
上面簡要介紹了Linux2.4核心和2.6核心的initrd的處理流程,為了使讀者對於Linux2.6核心的initrd的處理有一個更加深入的認識,下面將對Linuxe2.6核心初始化部分同initrd密切相關的程式碼給予一個比較細緻的分析,為了講述方便,進一步明確幾個程式碼分析中使用的概念:
rootfs: 一個基於記憶體的檔案系統,是linux在初始化時載入的第一個檔案系統,關於它的進一步介紹可以參考文獻[4]。
initramfs: initramfs同本文的主題關係不是很大,但是程式碼中涉及到了initramfs,為了更好的理解程式碼,這裡對其進行簡單的介紹。Initramfs是在 kernel 2.5中引入的技術,實際上它的含義就是:在核心映象中附加一個cpio包,這個cpio包中包含了一個小型的檔案系統,當核心啟動時,核心將這個cpio包解開,並且將其中包含的檔案系統釋放到rootfs中,核心中的一部分初始化程式碼會放到這個檔案系統中,作為使用者層程序來執行。這樣帶來的明顯的好處是精簡了核心的初始化程式碼,而且使得核心的初始化過程更容易定製。Linux 2.6.12核心的 initramfs還沒有什麼實質性的東西,一個包含完整功能的initramfs的實現可能還需要一個緩慢的過程。對於initramfs的進一步瞭解可以參考文獻[1][2][3]。
cpio-initrd: 前面已經定義過,指linux2.6核心使用的cpio格式的initrd。
image-initrd: 前面已經定義過,專指傳統的檔案映象格式的initrd。
realfs: 使用者最終使用的真正的檔案系統。
核心的初始化程式碼位於 init/main.c 中的 static int init(void * unused)函式中。同initrd的處理相關部分函式呼叫層次如下圖,筆者按照這個層次對每一個函式都給予了比較詳細的分析,為了更好的說明,下面列出的程式碼中刪除了同本文主題不相關的部分:
圖2 initrd相關程式碼的呼叫層次關係圖
init函式是核心所有初始化程式碼的入口,程式碼如下,其中只保留了同initrd相關部分的程式碼。
|
程式碼[1]:populate_rootfs函式負責載入initramfs和cpio-initrd,對於populate_rootfs函式的細節後面會講到。
程式碼[2]:如果rootfs的根目錄下中包含/init程序,則賦予execute_command,在init函式的末尾會被執行。否則執行prepare_namespace函式,initrd是在該函式中被載入的。
程式碼[3]:將控制檯設定為標準輸入,後續的兩個sys_dup(0),則複製標準輸入為標準輸出和標準錯誤輸出。
程式碼[4]:如果rootfs中存在init程序,就將後續的處理工作交給該init程序。其實這段程式碼的含義是如果載入了cpio-initrd則交給cpio-initrd中的/init處理,否則會執行realfs中的init。讀者可能會問:如果載入了cpio-initrd, 那麼realfs中的init程序不是沒有機會運行了嗎?確實,如果載入了cpio-initrd,那麼核心就不負責執行realfs的init程序了,而是將這個執行任務交給了cpio-initrd的init程序。解開fedora core4的initrd檔案,會發現根目錄的下的init檔案是一個指令碼,在該指令碼的最後一行有這樣一段程式碼:
|
就是switchroot語句負責載入realfs,以及執行realfs的init程序。
對cpio-initrd的處理位於populate_rootfs函式中。
|
程式碼[1]:載入initramfs, initramfs位於地址__initramfs_start處,是核心在編譯過程中生成的,initramfs的是作為核心的一部分而存在的,不是 boot loader載入的。前面提到了現在initramfs沒有任何實質內容。
程式碼[2]:判斷是否載入了initrd。無論哪種格式的initrd,都會被boot loader載入到地址initrd_start處。
程式碼[3]:判斷載入的是不是cpio-initrd。實際上 unpack_to_rootfs有兩個功能一個是釋放cpio包,另一個就是判斷是不是cpio包, 這是通過最後一個引數來區分的, 0:釋放 1:檢視。
程式碼[4]:如果是cpio-initrd則將其內容釋放出來到rootfs中。
程式碼[5]:如果不是cpio-initrd,則認為是一個image-initrd,將其內容儲存到/initrd.image中。在後面的image-initrd的處理程式碼中會讀取/initrd.image。
對image-initrd的處理在prepare_namespace函式裡,包含了對image-initrd進行處理的程式碼,相關程式碼如下:
|
程式碼[1]:執行initrd_load函式,將initrd載入,如果載入成功的話initrd_load函式會將realfs的根設定為當前目錄。
程式碼[2]:將當前目錄即realfs的根mount為Linux VFS的根。initrd_load函式執行完後,將真正的檔案系統的根設定為當前目錄。
initrd_load函式負責載入image-initrd,程式碼如下:
|
程式碼[1]:如果載入initrd則建立一個ram0裝置 /dev/ram。
程式碼[2]:/initrd.image檔案儲存的就是image-initrd,rd_load_image函式執行具體的載入操作,將image-nitrd的檔案內容釋放到ram0裡。判斷ROOT_DEV!=Root_RAM0的含義是,如果你在grub或者lilo裡配置了 root=/dev/ram0 ,則實際上真正的根裝置就是initrd了,所以就不把它作為initrd處理 ,而是作為realfs處理。
handle_initrd()函式負責對initrd進行具體的處理,程式碼如下:
|
handle_initrd函式的主要功能是執行initrd的linuxrc檔案,並且將realfs的根目錄設定為當前目錄。
程式碼[1]:real_root_dev,是一個全域性變數儲存的是realfs的裝置號。
程式碼[2]:呼叫mount_block_root函式將initrd檔案系統掛載到了VFS的/root下。
程式碼[3]:提取rootfs的根的檔案描述符並將其儲存到root_fd。它的作用就是為了在chroot到initrd的檔案系統,處理完initrd之後要,還能夠返回rootfs。返回的程式碼參考程式碼[7]。
程式碼[4]:chroot進入initrd的檔案系統。前面initrd已掛載到了rootfs的/root目錄。
程式碼[5]:執行initrd的linuxrc檔案,等待其結束。
程式碼[6]:initrd處理完之後,重新chroot進入rootfs。
程式碼[7]:如果real_root_dev在 linuxrc中重新設成Root_RAM0,則initrd就是最終的realfs了,改變當前目錄到initrd中,不作後續處理直接返回。
程式碼[8]:在linuxrc執行完後,realfs裝置已經確定,呼叫mount_root函式將realfs掛載到root_fs的 /root目錄下,並將當前目錄設定為/root。
程式碼[9]:後面的程式碼主要是做一些收尾的工作,將initrd的記憶體盤釋放。
到此程式碼分析完畢。
|
通過本文前半部分對cpio-initrd和imag-initrd的闡述與對比以及後半部分的程式碼分析,我相信讀者對Linux 2.6核心的initrd技術有了一個較為全面的瞭解。在本文的最後,給出兩點最重要的結論:
1. 儘管Linux2.6既支援cpio-initrd,也支援image-initrd,但是cpio-initrd有著更大的優勢,在使用中我們應該優先考慮使用cpio格式的initrd。
2. cpio-initrd相對於image-initrd承擔了更多的初始化責任,這種變化也可以看作是核心程式碼的使用者層化的一種體現,我們在其它的諸如FUSE等專案中也看到了將核心功能擴充套件到使用者層實現的嘗試。精簡核心程式碼,將部分功能移植到使用者層必然是linux核心發展的一個趨勢。
|
從下面三篇文章中,可以獲得更多的關於initramfs的知識:
從下面這篇文章中讀者可以瞭解到關於linux VSF、rootfs的相關知識:
下面是一些initrd的參考資料:
|
|
李大治,軟體工程師,目前從事Linux平臺下網路安全產品的開發工作,您可以通過[email protected]同他取得聯絡。 |
相關推薦
Linux2.6 核心的 Initrd 機制解析
initrd 的英文含義是 boot loader initialized RAM disk,就是由 boot loader 初始化的記憶體盤。在 linux核心啟動前, boot loader 會將儲存介質中的 initrd 檔案載入到記憶體,核心啟動時會在訪問真正的根檔
Linux2.6核心--中斷下半部實現方法 工作佇列
工作佇列子系統是一個用於建立核心執行緒的介面,通過它建立的程序負責執行由核心其他部分排到佇列裡的任務。它建立的這些核心執行緒稱作工作者執行緒。工作佇列可以讓你的驅動程式建立一個專門的工作者執
在Linux2.6核心(CentOS)中編譯核心模組的一個例子
使用的簡單測試記憶體原始檔hello.c: ------------------------------------------------------------------------- #include <linux/module.h> /* Needed by all mod
Linux 2.6核心中新的鎖機制--RCU
一、 引言 眾所周知,為了保護共享資料,需要一些同步機制,如自旋鎖(spinlock),讀寫鎖(rwlock),它們使用起來非常簡單,而且是一種很有效的同步機制,在UNIX系統和Linux系統中得到了廣泛的使用。但是隨著計算機硬體的快速發展,獲得這種鎖的開銷相對於CPU
5.Linux核心設計與實現 P39---linux2.6 CFS排程演算法分析(轉)
1.概述 CFS(completely fair schedule)是最終被核心採納的排程器。它從RSDL/SD中吸取了完全公平的思想,不再跟蹤程序的睡眠時間,也不再企圖區分互動式程序。它將所有的程序都統一對待,這就是公平的含義。CFS的演算法和實現都相當簡單,眾多的測試表明其效能也非常優越。
iscsitarget 在Linux2.6.32核心上編譯 若干編譯錯誤與解決方法(備忘)
#make make -C usr make[1]: Entering directory `/work/iscsitarget-0.4.16/usr' cc -O2 -fno-inline -Wall -Wstrict-prototypes -g -I../include
Linux 核心網路協議棧 ----- Linux 核心路由機制(一) (2.6.25)
核心的路由部分是是網路中重要部分,目前在Linux核心中預設的路由查詢演算法使用的是Hash查詢,所以你會看到很多的資料結構是XXX_hash什麼之類(例如fn_hash)。Linux核心從2.1開始就支援基於策略的路由,那麼什麼是基於策略的路由呢?我們一般
mini6410基於linux2.6.36核心通過NFS啟動根檔案系統總結(四製作根檔案系統及通過NFS掛載檔案系統)
本系列文章有本人yinjiabin製作,轉載請註明出處: http://blog.csdn.net/yinjiabin/article/details/7489563根檔案系統一般包括: 1)基本的檔案系統結構,包含一些必須的目錄,比如:/dev,/proc,/bin,/
PHP核心之旅-6.垃圾回收機制
回收PHP 核心之旅系列 一、引用計數 只有使用引用計數的變數才需要回收。引用計數就是用來標記變數的引用次數的。 當有新的變數zval指向value時,計數器加1,當變數zval銷燬時,計數器減一。當引用計數為0時,表示此value沒有被任何變數指向,可以對value進行釋放。 下面的例子說明引用
Dubbo原理和原始碼解析之“微核心+外掛”機制
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { //...... BufferedReader reader = new BufferedReader(new
inotify -- Linux 2.6 核心中的檔案系統變化通知機制
一、 引言 眾所周知,Linux 桌面系統與 MAC 或 Windows 相比有許多不如人意的地方,為了改善這種狀況,開源社群提出使用者態需要核心提供一些機制,以便使用者態能夠及時地得知核心或底層硬體裝置發生了什麼,從而能夠更好地管理裝置,給使用者提供更好的服務,如 hotplug、udev 和 ino
ETCD核心機制解析
ETCD整體機制 etcd 是一個分散式的、可靠的 key-value 儲存系統,它適用於儲存分散式系統中的關鍵資料。 etcd 叢集中多個節點之間通過Raft演算法完成分散式一致性協同,演算法會選舉出一個主節點作為 leader,由 leader 負責資料的同步與分發。當 leader 出現故障後系統會自動
Oracle SCN機制解析
丟失 self pen 必須 發出 span system 幫助 不同的 SCN(System Chang Number)作為oracle中的一個重要機制,在數據恢復、Data Guard、Streams復制、RAC節點間的同步等各個功能中起著重要作用。理解SCN的運作機制
筆記:XML-解析文檔-流機制解析器(SAX、StAX)
輸入 tex 字符數 表示 getname 重要 樹形 puts ron DOM 解析器完整的讀入XML文檔,然後將其轉換成一個樹型的數據結構,對於大多數應用,DOM 都運行很好,但是,如果文檔很大,並且處理算法又非常簡單,可以在運行時解析節點,而不必看到完整的樹形
Quartz定時任務調度機制解析(CronTirgger、SimpleTrigger )
sched 它的 main 目前 可選值 -- 實現接口 不能 三種 一、Quartz的介紹 Quartz 是 OpenSymphony 開源組織在任務調度領域的一個開源項目,完全基於 Java 實現。該項目於 2009 年被 Terracotta 收購,目前是 Te
vuex所有核心概念完整解析State Getters Mutations Actions
function 鉤子 action 元素事件 getter 參數 pst isp 文件中 vuex是解決vue組件和組件間相互通信而存在的,vuex理解起來稍微復雜,但一旦看懂則即為好用: 安裝: npm install --save vuex 引入 import
centos 6.8模板機制作
centos第一章虛擬化NAT網絡設置使用DHCP自動獲取IP地址第二章創建虛擬機第三章安裝CentOS-6.8-x86_64-bin-DVD1操作系統第四章模板機優化開機後使用命令ifup eth0獲取到IP地址後。用SecureCRT連接。4.1SecureCRT設置4.2系統優化優化開機網卡啟動sed
3.6 C++繼承機制下的構造函數
需要 類的構造函數 color view ice 如果 itl 不難 tor 參考:http://www.weixueyuan.net/view/6363.html 總結: 在codingbook類中新增了一個language成員變量,為此必須重新設計新的構造函數。在本
3.1_SpringBoot內部處理機制解析
ext tom list process eba 版本 代碼 http eric 前言 目前基於SpringBoot進行Web應用開發已經成為一個趨勢,Spring官網內部的很多入門Demo都是基於SpringBoot的,因此掌握SpringBoot成為當下基於Java的W
JAVA動態代理機制解析
定義 book lang 並不是 stat 控制 () highlight 什麽 1. 概述 首先,我們來思考如下兩個問題: 什麽是代理模式?為什麽要使用代理模式? 簡單總結一下,所謂的代理模式就是在原有的服務上多加一個占位,通過這個占位去控制服務的訪問。通過代理模