歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> Linux操作系統內存磁盤初始化技術詳細解析

Linux操作系統內存磁盤初始化技術詳細解析

日期:2017/2/27 9:50:55   编辑:更多Linux

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

什麼是內存磁盤初始化?

Initrd掛載優先級高於真實根文件系統,它被邦定在內核上,做為內核啟動過程的一部分被加載(load)。然後,做為兩階段引導過程的第一部分,內核掛載(mount)initrd,用於獲得並加載真實有效的文件系統。

為了達到這個目的,initrd包含有最起碼的目錄與程序,例如insmod,來安裝內核模塊到內核中。對於桌面或服務器linux,initrd 是臨時文件系統,它的生存周期很短,僅僅是做為到達真實根文件系統的橋梁。但對於沒有存儲設備的嵌入式系統來說,它才是永久性的根文件系統。本篇文章對這兩方面均有涉及。

深入分析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中時生成的。待會我們將繼續探討這個問題。




創建initrd的工具

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

自己動手,打造自定義的初始化內存盤

由於很多基於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目錄下。

測試自定義的初始化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=0×1400, size=0×29672e]

grub> initrd /ramdisk.img.gz

[Linux-initrd @ 0×5f2a000, 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。

通過初始化內存盤啟動

現在,大家已經看到了如何構建並使用一個自定制的初始化內存盤,這一節則用於介紹,內核是如何辨認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

無盤啟動的應用

同很多嵌入式系統的啟動一樣,本地磁盤(軟驅或者光驅)對於啟動內核和內存盤根文件系統來說,並不是必須的。DHCP工具能被用於確認網絡參數,例如大家熟悉的IP抵制和子網掩碼等。此外,TFTP能被用於將內核鏡像以及初始化內存盤鏡像傳送到本地設備。一旦傳輸完成,linux內核就能被啟動以及掛載 initrd,和本地鏡像啟動的過程一樣。

讓你的initrd盡可能小

當你在構建嵌入式系統時,總是希望initrd的鏡像盡可能小,恩,這裡將提供一些小技巧。首先就是使用BusyBox。前面已經提到過,BusyBox包含了很多較大的工具,通常體積都以MB計算,但是它成功得將自己的體積控制在幾百KB的范圍內。

在本例中,BusyBox鏡像使用的是靜態鏈接,因此不需要提供任何庫文件。但是,如果你需要得到標准的C庫文件來滿足自己的二進制程序,除了大體積的 glibc庫,你有其他更好的選擇。第一個,小體積的uClibc庫,是專門用於有空間限制的,標准C庫的縮水版本。另一個適用於有空間限制環境的庫是 dietlib。記住,你需要在自己的嵌入式系統中,用這些庫重新編譯你的二進制程序。雖然使用它們會帶來一些附加的工作,但是,是值得的。

總結

初始化內存盤技術被創建的最初目的,是為了讓內核通過一個臨時的根文件系統來過渡到最終的根文件系統。initrd對於嵌入式linux系統同樣是很有用處的:它能做為一個非持續性的根文件系統掛載到內存盤中。



Copyright © Linux教程網 All Rights Reserved