linux內(nèi)存初始化技術(shù)(initrd)用于支持兩階段的系統(tǒng)引導(dǎo)過(guò)程,,是在系統(tǒng)啟動(dòng)過(guò)程中被掛載的臨時(shí)root文件系統(tǒng)(譯者注:這里的root文件系統(tǒng)是指的根文件系統(tǒng)),。initrd包含很多可執(zhí)行程序和驅(qū)動(dòng),并允許在臨時(shí)的內(nèi)存磁盤根文件系統(tǒng)被卸載,,內(nèi)存被釋放后掛載真實(shí)的root文件系統(tǒng),。在許多嵌入式linux文件系統(tǒng)中,,initrd是最終的根文件系統(tǒng)。這篇文章主要講解了linux2.6內(nèi)核的initrd技術(shù),,包括在內(nèi)核中的創(chuàng)建及使用,。 1 什么是內(nèi)存磁盤初始化? initrd掛載優(yōu)先級(jí)高于真實(shí)根文件系統(tǒng),,它被邦定在內(nèi)核上,,做為內(nèi)核啟動(dòng)過(guò)程的一部分被加載(load)。然后,,做為兩階段引導(dǎo)過(guò)程的第一部分,,內(nèi)核掛載(mount)initrd,用于獲得并加載真實(shí)有效的文件系統(tǒng),。 為了達(dá)到這個(gè)目的,,initrd包含有最起碼的目錄與程序,例如insmod,,來(lái)安裝內(nèi)核模塊到內(nèi)核中,。 對(duì)于桌面或服務(wù)器linux,initrd是臨時(shí)文件系統(tǒng),,它的生存周期很短,,僅僅是做為到達(dá)真實(shí)根文件系統(tǒng)的橋梁。但對(duì)于沒有存儲(chǔ)設(shè)備的嵌入式系統(tǒng)來(lái)說(shuō),,它才是永久性的根文件系統(tǒng),。本篇文章對(duì)這兩方面均有涉及。 2 深入分析initrd initrd包含有必須的程序和系統(tǒng)文件,,用于支持系統(tǒng)的啟動(dòng)的第二階段過(guò)程,。創(chuàng)建初始化內(nèi)存的方法,,是隨著你所使用的系統(tǒng)版本而改變的,。從Fedora Core3以后,initrd就由回送設(shè)備(loop device)建立,。什么是回送設(shè)備,?它是一個(gè)設(shè)備驅(qū)動(dòng),允許你將一個(gè)文件掛載為塊設(shè)備,,并對(duì)其文件系統(tǒng)做出描述,。也許loop device并不存在與你的內(nèi)核中,但是你能夠通過(guò)內(nèi)核的配置工具(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 # 現(xiàn)在,你可以通過(guò)查看/mnt/initrd的子目錄來(lái)查看initrd的內(nèi)容,。需要注意的是,,即使你的initrd鏡像文件并不是以.gz做為后綴名,但是你同樣可以通過(guò)增加此后綴名來(lái)讓gunzip打開它。 從Fedora Core3開始,,默認(rèn)的initrd鏡像就是一個(gè)壓縮的gpio歸檔文件,。除了用掛載文件的方式以外,你同樣可以通過(guò)cpio歸檔的方式來(lái)將其掛載成使用了回送設(shè)備的壓縮鏡像,。你可以通過(guò)以下的指令來(lái)檢查這個(gè)cpio歸檔文件的內(nèi)容: # 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 drwxr-x--- drwxr-xr-x drwxr-xr-x drwxr-xr-x -rwxr-xr-x -rw-r--r-- drwxr-xr-x drwxr-xr-x drwxr-xr-x lrwxrwxrwx drwxr-xr-x drwxr-xr-x # 一些小的,但是很有必要的程序組合能在./bin目錄下得到,,包括nash(它不是一個(gè)shell,,而是一個(gè)腳本解釋工具),用于加載內(nèi)核模塊的insmod,,以及l(fā)vm等,。 上面所示目錄中,相對(duì)比較有趣的是root目錄下的初始化文件,。這些文件,,和傳統(tǒng)的linux啟動(dòng)過(guò)程中一樣,是在initrd鏡像被解壓縮到RAM中時(shí)生成的,。待會(huì)我們將繼續(xù)探討這個(gè)問(wèn)題,。 3 創(chuàng)建initrd的工具。 現(xiàn)在,,讓我們回到一開始的討論:initrd的鏡像是如何被創(chuàng)建的,?在傳統(tǒng)的linux系統(tǒng)中,initrd是在linux build的時(shí)候被創(chuàng)建的,。像mkinitrd這樣的許許多多的工具,,都能夠用于通過(guò)必須的庫(kù)和模塊來(lái)自動(dòng)構(gòu)建一個(gè)用于過(guò)渡到真實(shí)根文件系統(tǒng)的initrd。事實(shí)上,,mkinitrd工具是一個(gè)腳本文件,,因此,我們能夠很清楚得看到,,這個(gè)過(guò)程是如何進(jìn)行的,。同樣的,YAIRD (Yet Another Mkinitrd)工具,,也允許我們自定制每一個(gè)initrd被構(gòu)建的階段,。 4 自己動(dòng)手,打造自定義的初始化內(nèi)存盤 由于很多基于linux的嵌入式系統(tǒng)都沒有硬盤驅(qū)動(dòng)器,,initrd也可以做為永久性的根文件系統(tǒng),。下面我就將告訴你們,如何創(chuàng)建一個(gè)initrd鏡像,。我使用的是標(biāo)準(zhǔn)linux桌面系統(tǒng),,因此大家即使沒有嵌入式目標(biāo)設(shè)備也可以照著做,。除了交叉編譯以外,嵌入式目標(biāo)文件的構(gòu)建過(guò)程是相同的,。 #!/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 想創(chuàng)建initrd的話,,你需要首先創(chuàng)建一個(gè)空文件,將/dev/zero(0字符流)做為ramdisk.img的輸入,。得到的文件大小大約是4MB(有4000個(gè)1K的塊組成),。接下來(lái),用mke2fs命令來(lái)創(chuàng)建一個(gè)使用這個(gè)空文件的ext2文件系統(tǒng)?,F(xiàn)在,,這個(gè)文件就是一個(gè)ext2文件系統(tǒng)。ok,,接下來(lái),,以回路設(shè)備的形式掛載這個(gè)文件到/mnt/initrd,現(xiàn)在,,你就在掛載點(diǎn)擁有一個(gè)代表著ext2文件系統(tǒng)的目錄,,并用與存放你的initrd。其他大多數(shù)的腳本語(yǔ)句都是用于實(shí)現(xiàn)這個(gè)功能,。 下一步,,就是創(chuàng)建一些必須的子目錄,用于生成你的根文件系統(tǒng): /bin, /sys, /dev, 和 /pro,。這里只需要少數(shù)幾個(gè)目錄,,例如,沒有/lib,。但是它們已經(jīng)包含了大部分功能,。 如果想讓你的根文件系統(tǒng)發(fā)揮更大的作用,請(qǐng)使用 BusyBox,。這個(gè)工具是一個(gè)包含了許多獨(dú)立工具的鏡像,,這些獨(dú)立的工具你都能在linux中找到( ash, a等等wk, sed, insmod)。BusyBox的優(yōu)勢(shì)在于,,它把它們集合在了一起,,并分享了公用的部分,從而極大縮小了鏡像的體積,。這對(duì)于嵌入式系統(tǒng)來(lái)講,是非常理想的,。請(qǐng)將BustBox鏡像從它的源目錄中復(fù)制出來(lái),,到你的/bin目錄下,這樣,,很多指向BusyBox工具集的符號(hào)鏈接將被創(chuàng)建,,BusyBox能確定哪一個(gè)工具將被使用,,并自動(dòng)引用它。這個(gè)/bin目錄下被創(chuàng)建的鏈接的小型集合將用于對(duì)啟動(dòng)腳本的支持,。 再下一步,,就是一小部分特殊設(shè)備文件的創(chuàng)建。我從我的/dev文件夾中直接拷貝了出來(lái),,別忘了加上-a選項(xiàng)來(lái)保持它們?cè)械膶傩浴?BR>倒數(shù)第二步,,就是生成linuxrc文件。在內(nèi)核掛載了內(nèi)存盤之后,,它將搜索并執(zhí)行相關(guān)的啟動(dòng)文件,,如果沒有找到,內(nèi)核就將linuxrc文件做為其啟動(dòng)腳本,。你最好在這個(gè)文件中對(duì)環(huán)境變量做一些基本設(shè)置,,例如掛載/proc文件系統(tǒng)等。除了/proc外,,我還掛載了/sys文件系統(tǒng),,將消息發(fā)送給終端。最后,,我調(diào)用ash并通過(guò)它和根文件系統(tǒng)交互,。最后記住,用chmod把linuxrc文件的屬性改為可執(zhí)行,。 最后,,你的根文件系統(tǒng)算是ok了。現(xiàn)在它并沒有被掛載,,用gzip將它壓縮,,并將壓縮后的文件ramdisk.img.gz拷貝到/boot目錄下,這樣它就能被GRUB調(diào)用,。 想要構(gòu)建你的初始化ram盤的話,,你只需要調(diào)用mkird,鏡像就將自動(dòng)創(chuàng)建并拷貝到/boot目錄下,。 5 測(cè)試自定義的初始化RAM盤,。 [ Minimal BASH-like line editing is supported. For the first word, TAB grub> kernel /bzImage-2.6.1 grub> initrd /ramdisk.img.gz grub> boot Uncompressing Linux... OK, booting the kernel. 在內(nèi)核啟動(dòng)之后,,它開始檢查initrd鏡像是否可用,,如果答案是確定的,那么就作為根文件系統(tǒng)加載并掛載它,。下面就是這個(gè)特殊啟動(dòng)過(guò)程的結(jié)尾: ... 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 dev / $ cat /proc/1/cmdline /bin/ash/linuxrc / $ cd bin /bin $ ls ash busybox /bin $ touch zfile /bin $ ls ash busybox 當(dāng)啟動(dòng)之后,,可以通過(guò)ash來(lái)進(jìn)入命令模式。在本例中,,我探究了根文件系統(tǒng)并向你演示了,,你能通過(guò)新建文件來(lái)寫入這個(gè)文件系統(tǒng)。只需要注意,,第一步是要?jiǎng)?chuàng)建linuxrc,。 6 通過(guò)初始化內(nèi)存盤啟動(dòng) 現(xiàn)在,大家已經(jīng)看到了如何構(gòu)建并使用一個(gè)自定制的初始化內(nèi)存盤,,這一節(jié)則用于介紹,,內(nèi)核是如何辨認(rèn)initrd并將其作為它的根文件系統(tǒng)掛載的。我將涉及一些boot chain中的主要的函數(shù)并對(duì)發(fā)生的事件做出解釋,。 像GRUB這樣的boot loader,,通常會(huì)確認(rèn)即將加載的內(nèi)核并復(fù)制該內(nèi)核鏡像與任何相關(guān)聯(lián)的initrd到內(nèi)存中,你可以在你linux內(nèi)核源程序目錄下的./init子目錄中找到這些功能實(shí)現(xiàn),。 在內(nèi)核與initrd鏡像被解壓縮和復(fù)制到內(nèi)存后,,內(nèi)核被調(diào)用。此時(shí),,開始各種各樣的初始化過(guò)程,,最終,你會(huì)發(fā)現(xiàn)自己處于init/main.c:init() (subdir/file:function),。這個(gè)函數(shù)實(shí)現(xiàn)了很多的子系統(tǒng)初始化,。在這里,要調(diào)用init/do_mounts.c:prepare_namespace(),,用來(lái)準(zhǔn)備命名空間(掛載dev 文件系統(tǒng), RAID, 或者md, devices, 以及, 最后的initrd),。通過(guò)對(duì) init/do_mounts_initrd.c:initrd_load()的調(diào)用,最終完成對(duì)initrd的加載,。 initrd_load()調(diào)用init/do_mounts_rd.c:rd_load_image(),,來(lái)決定是否通過(guò)調(diào)用init/do_mounts_rd.c:identify_ramdisk_image()來(lái)加載內(nèi)存盤鏡像。后面這個(gè)函數(shù)通過(guò)檢查內(nèi)核的編號(hào)來(lái)確定文件究竟是是minux,,etc2,,romfs,cramfs,,還是gzip格式,,直到返回initrd_load_image后,init/do_mounts_rd:crd_load()又被調(diào)用,。這個(gè)函數(shù)負(fù)責(zé)分配空間給內(nèi)存盤,,并進(jìn)行校驗(yàn)計(jì)算,解壓縮,,最后將內(nèi)存盤鏡像加載到內(nèi)存中,。此時(shí),你就已經(jīng)擁有了一個(gè)適合于掛載的,,在塊設(shè)備中的initrd鏡像,。 現(xiàn)在,通過(guò)調(diào)用init/do_mounts.c:mount_root()將這個(gè)塊設(shè)備做為root掛載,。ok,,根設(shè)備就被創(chuàng)建了,接下來(lái)調(diào)用的函數(shù)是init/do_mounts.c:mount_block_root(),,此函數(shù)又調(diào)用fs/namespace.c:sys_mount()來(lái)掛載真實(shí)的根文件系統(tǒng)并對(duì)其進(jìn)行chdir操作,。 最后,會(huì)返回到啟動(dòng)函數(shù)中,,并調(diào)用init/main.c:run_init_process,。調(diào)用的結(jié)果是,初始化進(jìn)程開始(在這里是通過(guò)/linuxrc),。linuxrc可以是一個(gè)可執(zhí)行程序,,也可以是腳本(只要腳本解釋器能夠正常解釋它)。 函數(shù)調(diào)用的層次關(guān)系可以從下表中看出,。并不是所有與復(fù)制,、掛載初始化內(nèi)存盤的函數(shù)都被列舉出來(lái),這里僅僅是大概的,,對(duì)整體基本流程的回顧: init/main.c:init |