1. 程式人生 > >跟我一步一步製作一個基本的linux啟動盤

跟我一步一步製作一個基本的linux啟動盤

       原創文章,轉載請註明出處,謝謝!       
       作者:清林,部落格名:飛空靜渡

這是一篇自己很早寫的文章了,當時根據網上的一些教程一步一步製作,本來想把自己做的過程放到網上,可以西一值忙於公司專案,沒有時間,去年想放上來的時候發現csdn上傳不了圖片,我想,沒有圖片的教程就不叫一步一步的教程了。好了,下面是我一步一步製作的linux的簡單的啟動盤,複雜一些的以後再寫吧!

本文將會一步一步從基礎引導大家構建一個linux啟動盤,在這個linux啟動盤在啟動後可以執行我們特定的程式,完成特定的功能。本文力求簡單詳細,寫出製作的每一步,其中會穿插各種圖片,在引用其他文章時,會把文章貼出來,使得讀者不用去連結尋找,也同時使得沒有網路的讀者可以省去無網路之麻煩。使得初學者可以照著一步一步的完成,   
    本著尊重原創的精神,文章中如果沒有特殊說明,則所有斜體文字都引自其他文章的內容,其中大部分來自《精通initramfs構建step by step》。
    注:本文參考的主要兩篇文章是《精通initramfs構建step by step》和《深入理解 Linux 2.6 的 initramfs 機制 (上)》,文章如果引用到其他文章則在文中說明。
    工作環境:ubuntu9.10,練習用的核心是linux-2.6.31.tar.bz2,ubuntu9.10預設的是gcc4.4,我用的是gcc4.3.4(具體修改gcc版本可用update-alternatives命令)。

一、製作一個顯示hello world的啟動linux

    一、 initramfs 是什麼
    在 2.6 版本的 linux 核心中,都包含一個壓縮過的 cpio 格式 的打包檔案。當核心啟動時,會從這個打包檔案中匯出檔案到核心的 rootfs 檔案系統,然後核心檢查 rootfs 中是否包含有 init 檔案,如果有則執行 它,作為 PID 為 1 的第一個程序。這個 init 程序負責啟動系統後續的工作,包括定位、掛載“真正的”根檔案系統裝置(如果有的話)。如果核心沒有在 rootfs 中找到 init 檔案,則核心會按以前版本的方式定位、掛載根分割槽,然後執行/sbin/init 程式完成系統的後續初始化工作。
這 個壓縮過的 cpio 格式的打包檔案就是 initramfs。編譯 2.6 版本的 linux 核心時,編譯系統總會建立 initramfs,然後把它與編譯好的 核心連線在一起。核心原始碼樹中的 usr 目錄就是專門用於構建核心中的 initramfs 的,其中的 initramfs_data.cpio.gz 檔案就 是 initramfs。預設情況下, initramfs 是空的,X86 架構下的檔案大小是 134 個位元組。


   
    我在做練習時,沒有在核心原始碼樹usr中找到initramfs_data.cpio.gz,倒是有個initramfs_data.cpio檔案,不過也沒關係,initramfs_data.cpio檔案就可以了。

    二、構建第一個 initramfs : hello world
    從 C 語言開始,學習計算機程式語言的第一個程式幾乎都是 hello world,因此我們也構建一個最簡單 的 hello world 式的 initramfs,以說明 initramfs 的基本構建方法。 initramfs 的靈魂是 init 檔案(或者叫程式,因為它會被核心第一個執行),我們先寫一個簡單的 init 程式,它會在核心的 console 中打印出經典的 hello world 資訊。
hello.c:

其中的 sleep()函式語句是為了避免執行時核心很快打出 panic 的資訊,並非功能上的需要。
接著把 hello.c 編譯成靜態連線程式:
gcc -o hello_static -static -s hello.c
命令列中的-s 引數表示編譯後的程式不包含除錯定位資訊,目的是減少編譯出來的程式檔案的大小。
再建立一個 initramfs 的構建原始檔目錄 image,把 hello_static 程式拷入這個目錄,並改名為
init。

    在 image 目錄下,建立一個 dev/console 的裝置檔案,否 init 程式無法在核心 console 中輸出資訊:
mknod -m 600 dev/console c 5 1
注意,執行這個命令需要有 root 許可權。
   
    我的練習目錄是這樣的/home/fjb/home/test/initramfs/t1。我在t1目錄下建立了image目錄。其中sudo mkdo -m 600 dev/console c 5 1 這個命令是建立一個裝置檔案,-m mode, --mode=mode 為新建立的檔案設定模式,就象應用命令chmod一樣,以後仍然使用預設模式建立新目錄。    dev/console是要建立的裝置,即一個控制檯裝置,我們後面在啟動linux時其顯示的資訊會用上這個控制檯,我們可以在image目錄下先建立dev目錄(mkdir dev),c表示是字元裝置,5和1是主次版本號。注意在ubuntu上要用超級使用者許可權才可以建立。
    這樣在t1目錄下就有了hello.c、hello_static檔案和image目錄,在image目錄裡有init檔案和dev目錄,在dev目錄下有console裝置檔案。

    好,現在可以構建initramfs了。
    首先是配置linux。我把linux-2.6.31.tar.bz2拷貝到/home/fjb/test/initramfs/目錄下,執行tar -xvf linux-2.6.31.tar.bz2進行解壓,在initramfs目錄下生成一個linux-2.6.31目錄,裡面有linux的原始碼(linux的下載可以在http://kernel.org/pub/linux/kernel/ 的2.6目錄裡下載)。

    進入linux-2.6.31目錄,執行make menuconfig,會出現如下圖的配置畫面:

    在general setup的配置目錄下的選上Initial RAM filesystem and RAM disk (initramfs/initrd) support選項,選上的話,旁邊的中括號會有個*,然後在下面輸入/home/fjb/home/test/initramfs/t1/image,如下圖:


       
     因為我們的 init 程 序是 ELF 格式的,所以核心需要支援ELF 的可執行檔案,否則啟動這個 init 程式會失敗。在核心的 Executable file formats 配置目錄下,選擇 kernel support for ELF binaries,則可使核心支援 ELF 格式的可執行檔案。其他核心 配置引數根據實際需要設定即可,不過,為了減少核心編譯時間,可參考這篇文章
http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-
293122.html 設定一個最簡單的核心配置。

    為了節省大家的時間,我把這篇文章拷貝到下面(linuxman的部落格,很不錯,薦):

=============================================
X86_64-Pure64 CLFS實踐筆記(二)

三、適合CLFS臨時系統的最簡核心配置
我的host系統是32位的,無法使用chroot方式構建CLFS系統,只能先交叉編譯出一個最小的臨時系統,然後以BOOT方式完成CLFS系統的構建。在CLFS BOOK中,對臨時系統的核心的配置有下面的警告:
Warning
Here a temporary cross-compiled kernel will be built. When configuring it, select the minimal amount of options required to boot the target machine and build the final system. I.e., no support for sound, printers, etc. will be needed.
Also, try to avoid the use of modules if possible, and don't use the resulting kernel image for production systems.

那麼,怎樣得到一個適合CLFS臨時系統的最簡核心配置呢?

(1)首先,用核心的 allnoconfig 配置目標,得到一個最最基本的核心配置。即,執行下面的命令:
make ARCH=x86_64 CROSS_COMPILE=${CLFS_TARGET}- allnoconfig
核心的 allnoconfig 配置目標會把所有的核心選項都設定為no,也就是把它們既不編譯進核心,也不編譯成模組。有了這個最基本的配置,我們再新增CLFS臨時系統所必須的配置項:再執行
make ARCH=x86_64 CROSS_COMPILE=${CLFS_TARGET}- menuconfig
命令,按下面的步驟新增其他的配置——

(2)按CLFS BOOK的建議,把 Executable file formats 下的ELF 和 emulations for 32bit ELF 選項編譯進核心。

(3)在 Processor type and features 下面,選擇合適的CPU型別。例如,我的CPU型別是Intel EM64T

(4)選擇PCI/PCI-Express支援,位於Bus options (PCI, PCMCIA, EISA, MCA, ISA) 配置目錄下。

(5)加入對根檔案系統所在磁碟控制器的驅動,詳細方法可參考:http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-280649.html。例如,我的機器的配置是:
Device Driver
|---->SCSI device support
|---->SCSI disk support
|----->SCSI low-level drivers
  |---->Serial ATA (SATA) support
  |---->intel PIIX/ICH SATA support

(6)加入Ext2檔案系統的支援:在 File systems 配置目錄下,選擇 Second extended fs support。如果根檔案系統是Ext3,則選擇 Ext3 journalling file system support。

(7)為了是 Udev 正常工作,需要核心支援 Unix domain sockets。此配置選項位於 Networking 配置目錄中的 Networking support ---> Networking options 下。

(8) 使核心支援 /proc 虛擬檔案系統和 tmpfs 檔案系統: File systems ---> Pseudo filesystems ---> /proc file system support / Virtual memory file system support (former shm fs)

(9)支援 swap 分割槽: General setup ---> Support for paging of anonymous memory (swap)

(10)支援 RTC 裝置: Device Drivers ---> Character devices ---> Enhanced Real Time Clock Support

(11) 為了充分發揮我的雙核CPU的能力,我又加入了對SMP的支援: Processor type and features ---> Symmetric multi-processing support。這一特性,對CLFS系統來說,並不是必須的。

這樣一個最簡的核心配置,編譯出來的核心映像檔案的大小隻有744KB,但卻能完全滿足CLFS系統後續的構建工作需要。

=============================================

    由於我的31核心配置和他的有些不一樣,我就把sata和ata硬碟等的選項都選上了,(第一次製作,還不清楚很多選項,沒辦法了)。
    之後我們就可以儲存選項,然後執行make bzImage之後在執行make就ok了(編譯比較長時間,慢慢等吧,我沒有咖啡泡,只能跑去吃飯了)。

二、實驗環境的搭建
    試驗 initramfs 需要經常重啟系統,所以使用 CPU 模擬器是不錯的選擇。我們可以選用 qemu,它支援直接啟動 linux 核心,無需在模擬器中安裝 OS。   
    如果在在命令列上用qemu的話會有問題,那是qemu的一個bug,對於27後的核心支援不好,在執行後會出現apic的錯誤,如果要用命令列上用qemu的話,執行qemu -kernel linux-2.6.31/arch/i386/boot/bzImage -append noapic -hda /dev/zero可以禁止核心的apic。這樣就不會有這個問題。但這裡有個更好的方法是用Qemu Luancher,既簡單又方便好用。
    Qemu的安裝可參考Linuxman的文章《在Linux系統下安裝圖形化的QEMU》,連結:http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-612280.html。同樣為了大家的方便,我把他的文章貼在下面(擺明是力求詳細嘛)。


================================================================================
在Linux系統下安裝圖形化的QEMU
QEMU是一款經典的CPU模擬器軟體,但它是命令列方式程式,引數眾多,使用起來不是很方便。QEMU有一個圖形化的前端程式qemu-laucher,可方便地設定QEMU的命令列引數。
我的系統是Debian 4.0 Amd64,軟體庫中已包含了qemu-laucher,可以方便地安裝。安裝完整的圖形化QEMU環境,需要安裝下面的軟體包:
qemu:QEMU主程式
qemu-launcher:圖形化的QEMU啟動器
kqemu-common:QEMU的加速模組程式
qemuctl:QEMU的執行時選項的設定程式,可作為qemu-laucher 的外掛程式。
安裝完成後,在 Application/Accessories 系統選單下,添加了qemu-laucher 的啟動項。

我還有一個openSuse 10.3 的系統,遺憾的是,它的軟體庫中沒有包含qemu-laucher,無法直接安裝。
================================================================================
   
    ubuntu就方便了,在新立得裡就可以把這些全裝上了。裝完後會生成一個選單:應用程式-》附件-》Qemu Launcher。點選一個選單啟動 Qemu Launcher,如下圖。


    其中,由於我們在啟動linux時需要硬碟,雖然我們不是必須,但為了啟動不出錯,我們可以New一個disk出來,如上圖的Hand disk 0,我建了一個100M的虛擬硬碟t1。
    然後點選linux boot選項,勾上Boot Linux kernel directly選項,在下面的Kernel image中找到我們剛才生成的bzImage檔案,在 linux-2.6.31/arch/i386/boot/bzImage資料夾裡。在Initial RAM disk選擇中選擇我們剛才生成的那個硬碟。好了,到此我們已可以啟動我們剛才建立的linux了,點選Launch可以執行我們的linux了(記得點選Save按鈕,不然下次啟動Qemu Launcher時又的重新去找那些檔案)。
    核心輸出一堆核心執行資訊後,最後打出了 hello world, from initramfs。如下圖:



三、什麼是initramfs

      1.initramfs 的前世今生
      1.1什麼是 rootfs 和 ramfs 所有的 2.6 版本 linux 核心都有一個特殊的檔案系統 rootfs,是核心啟動的初始始根檔案系統, initramfs 的檔案會複製到 rootfs。如果把 initramfs 比作種子,那麼 rootfs 就是 它生長的土壤。大部分 linux 系統正常執行後都會安裝另外的檔案系統,然後忽略 rootfs。rootfs 是 ramfs 檔案系統的一個特殊例項。ramfs 是一種非常簡單的檔案系統,是基於記憶體的檔案系統。ramfs 檔案系統沒有容量大小的限制,它可以根據需要動態增加容量。ramfs 直接利用了核心的磁碟快取記憶體機制。所有的檔案的讀寫資料都會在記憶體中做快取記憶體(cache),當系統再次使用檔案資料時,可以直接從記憶體中讀寫,以提 供系統的 I/O 效能。快取記憶體中的寫入資料會在適當的時候回寫到對應的檔案系統裝置(如磁碟等)中,這時它的狀態就標識為clean,這樣系統在必要時可 以釋放掉這些記憶體。ramfs 沒有對應檔案系統裝置,所以它的資料永遠都不會回寫回去,也就不會標識為 clean,因此係統也永遠不會釋放 ramfs 所佔 用的記憶體。因為 ramfs 直接使用了核心已有的磁碟快取記憶體機制,所以它的實現程式碼非常小。也由於這個原因, ramfs 特性不能通過核心配置引數刪除,它是核心的天然特性。
    1.2ramfs 不是 ramdisk
ramdisk 是在一塊記憶體區域中建立的塊裝置,用於存放檔案系統。ramdisk 的容量是固定的,不能象
ramfs 一樣動態增長。ramdisk 需要核心的檔案系統驅 動程式(如 ext2)來操作其上的資料,而ramfs 則是核心的天然特性,無需額外的驅動程式。ramdisk 也象其他檔案系統裝置一樣,需要在塊設
備和 記憶體中的磁碟快取記憶體之間複製資料,而這種資料複製實際不必要的。
       1.3從 ramfs 派生的檔案系統 tmpfs
ramfs 的一個缺點是它可能不停的動態增長直到耗盡系統的全部記憶體,所以只有 root 或授權使用者允許
使用 ramfs。為了解決這個問題,從 ramfs 派生出了 tmpfs 檔案系統,增加了容量大小的限制,而且允
許把資料寫入交換分割槽。由於增加了這兩個特性,所以 tmpfs 允許普通使用者使用。關於 tmpfs 檔案系統更多的資訊,可以看核心原始碼中的 Documentation/filesystems/tmpfs.txt 文件。
    綜上所述,initramfs 是一種 ramfs 檔案系統,在核心啟動完成後把它複製到 rootfs 中,作為核心初始的根檔案系統,它的任務是掛載系統真正的根檔案系統。這就是 initramfs 的前世今生。


四、使用busybox構建
    busybox 號稱是嵌入式 Linux 中的瑞士軍刀——小巧、功能齊 全。它把許多常用的 Linux 命令都整合到一個單一的可執行程式中,只用這一個可執行程式(即 busybox)加上 Linux 核心就可以構建一個基本的 Linux 系統。busybox 程式非常小巧,包含全部命令可執行檔案大小也只有 750 多。busybox 是完全模組化的,可以很容易地在編譯時增加、 刪除其中包含的命令。
    由於 busybox 的這些特點,它廣泛應用於 LiveCD、應急修復盤、安裝盤等系統中。我們也是以它為基礎,構建 initramfs 。   


4.1編譯busybox
    我們可以去http://busybox.net 上下載最新的busybox原始碼,我在上面下載的是最新的busybox-1.15.2.tar.bz2版本。把它解壓到initramfs目錄下,可以用make menuconfig進行配置,另外兩個配置命令是
make allyesconfig——最大配置
make allnoconfig——最小配置
    這裡用 make defconfig 做預設配置。

     用make CONFIG_PREFIX=<安裝目錄> install 命令安裝。如果在命令列中省略 CONFIG_PREFIX 變數的賦值,則會安裝預設值 ./_install 目錄下。CONFIG_PREFIX 可以在 make menuconfig 的配置介面中修改。
    在busybox-1.15.2目錄下執行
    make defconfig
    make CONFIG_PREFIX=/home/fjb/home/test/initramfs/t1/image install
    那麼我們就可以把busybox安裝在image目錄下了,image目錄下會多出幾個資料夾和檔案,如下圖:



       預設配置下,busybox 動態連結到 glibc,所以要把它用到的動態庫複製到 initramfs 的構建目錄中。用 ldd 命令檢視 busybox 用到了哪些動態庫檔案及相應的檔案路徑,然後把它們複製到相應的目錄下即可。
    在image/bin 目錄下,我們執行
    ldd busybox
    得到的結果如下:
    linux-gate.so.1 =>  (0x00137000)
    libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x009ce000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00138000)
    /lib/ld-linux.so.2 (0x00f04000)
    我們可以在image下建立lib目錄,把這幾個苦拷貝到lib目錄下。
    這裡要注意了,我在這裡就浪費了很多時間。
    首先: linux-gate.so.1這個庫我們是找不到的,因為linux下根本沒有這個庫,具體來說就google一下咯,上面有解釋,一句話,這個不需要。
     libm.so.6、 libc.so.6和/ld-linux.so.2這三個其實在/lib目錄下,在/lib/tls/i686/cmov/下也有。他們都是連結檔案,剛開始我用cp命令拷貝他們,拷貝他們的連結檔案也可以順這連結找到原檔案並拷貝過去,但在最後啟動的時候老是提示/bin/sh: error while loading shared libraries: /libm.so.6: cannot open shared object file: No such file or directory
    我在網上找了很久也沒找到答案,明明在在image/lib目錄下有這個庫,為什麼老是提示說找不到呢,最後沒辦法,只好老老實實的把libm.so.6、 libc.so.6和/ld-linux.so.2這三個庫所連結的檔案ld-2.10.1.so、libc-2.10.1.so、libm-2.10.1.so拷貝到lib目錄下,然後建立這三個檔案的連結檔案:
    ln -s ld-2.10.1.so  ld-linux.so.2 
    ln -s libc-2.10.1.so  libc.so.6 
    ln -s libm-2.10.1.so  libm.so.6
    這樣後,最後啟動成功,具體什麼原因我還沒去弄清楚,望知道的朋友告訴我,謝謝!
4.2在 image 下建立必要的目錄和裝置檔案
    在image目錄下執行
    mkdir proc etc sys mnt
    生成proc etc sys mnt這4個目錄。
    hello world 已經建立了 console 裝置檔案,我們再用
    mknod -m 600 dev/null c 1 3
    命令建立另一個基本的裝置檔案。
4.3製作一個最簡單的linux系統
    我們可以試著做一個最簡單的可執行的 linux 系統了:
    首先刪除image目錄下的init檔案,然後在image目錄下寫個init 的指令碼,過程如下:
    (1)在 image 目錄下寫一個最簡單的 init 指令碼。
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
/bin/sh
    (2)為 init 指令碼設定可執行許可權,否則核心不會去執行它。
    chmod +x init
    (3)有些 busybox 配置中,mdev 命令需要讀取/etc/mdev.conf 檔案,為了避免出錯資訊,我們建立一個空檔案。
    touch etc/mdev.conf
    (4)在核心原始碼目錄下,執行
    make
    命令,重新編譯核心,生成新的 initramfs。

    好了,在 QEMU 模擬環境下啟動這個新的核心,系統初始化後,會進入 SHELL 環境。在這個 SHELL 環境下,試驗一些常用命令,看看是否可以正常執行。我的執行如下圖所示:



    上一步建立的簡單 linux 系統在進入 SHELL 環境時,會打出下面這一句出錯資訊:
    /bin/sh: can't access tty; job controll off
    雖然不影響使用,但終究不夠完美。產 生這個錯誤的原因是我們的 SHELL 是直接執行在核心的 console 上的,而 console 是不能提供控制終端(terminal)功能的,所以必須把 SHELL 執行在 tty 裝置上,才能消除這個錯誤。解決問題的辦法是使用正規 init 機制,在執行 SHELL 前開啟 tty 裝置。
另外,這個簡單系統的 reboot、halt 等命令是不起作用的,也必須通過 init 方式解決。

   
    我們可以嘗試在這個linux下執行tty這個命令,它打印出/dev/console,這說明它是執行在console上的。

4.4 busybox 的預設 init 模式
    busybox 支援 init 功能,當系統沒有/etc/inittab 檔案時,它有一套預設的模式,按下面配置執行:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
如果 busybox 檢測到/dev/console 不是串列埠控制檯,init 還要執行下面的動作:
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
    我們試試這種模式是否可以解決我們的問題。

   
    說明一下,上面說的/etc/inittab是image/etc/inittab檔案。並且是busybox工作。好了,我們開始下一個實驗。將init指令碼後面的/bin/sh那行刪除,並在etc目錄下建立init.d目錄,再把init檔案移到etc/init.d目錄下更名為rcS
    mv init etc/init.d/rcS
         重新編譯核心,生成新的 initramfs
    用 QEMU 試驗一下新編譯的核心。我的Qemu執行如下圖:

    系統啟動後,會打出一句話“please press Enter to active this console”——感覺還不錯。但是按下回車鍵後,系統依然會打出錯誤資訊“-/bin/sh:
can't access tty; job controll off ”。用 tty 命令看看當前的終端裝置檔名:
# tty
/dev/console
    它還是 console,不是 tty 裝置,所以問題沒有解決。不過,reboot 和 halt 命令倒是可以正常工作了。
    經過驗證,busybox 的預設 init 模式無法滿足我們的要求,我們還是要寫 inittab,定製自己的 init 初始化流程。


4.5 busybox 的 inittab 檔案格式說明
    要寫自己的 inittab,需要理解 busybox 的 inittab 檔案格式。
busybox 的 inittab 檔案與通常的 inittab 不同,它沒有 runlevel 的概念,語句功能上也有限制。 inittab 語句的標準格式是
    <id>:<runlevels>:<action>:<process>
    各欄位的含義如下
    <id>:
    id 欄位與通常的 inittab 中的含義不同,它代表的是這個語句中 process 執行所在的 tty 裝置,內容就是/dev 目錄中 tty 裝置的檔名。由於是執行 process 的 tty 裝置的檔名,所以也不能象通常的inittab 那樣要求每條語句 id 的值唯一。
    <runlevels>:
    busybox 不支援 runlevel,所以此欄位完全被忽略。
    <action>:
    為下列這些值之一:
    sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown
其 含義與通常的 inittab 的定義相同。特別提一下 askfirst,它的含義與 respawn 相同,只是在運
行 process 前,會打出一句話 “please press Enter to active this console”,然後等使用者在
終端上敲入回車鍵後才執行 process。
    <process>:
    指定要執行的 process 的命令列。

4.6編寫inittab
理解了 busybox 的 inittab 格式,我們就可以寫 mini linux 的 inittab:
::sysinit:/etc/init.d/rcS
tty1::askfirst:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
tty5::askfirst:/bin/sh
tty6::askfirst:/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
    把這個檔案放到 image 的 etc 目錄下。為了執行 reboot 命令時避擴音示找不到/etc/fstab 檔案,我們再在 etc 目錄下建立一個空檔案
    touch fstab

   
    在image/etc目錄下建立inittab檔案,並將上面的內容寫入這個檔案。那麼etc裡的檔案內容就如下了:fstab  init.d  inittab
    做好了這些,就可以重新編譯核心,生成新的 initramfs 了。在 QEMU 試驗環境下驗證新生成的 linux,系統執行正常,而且象通常的 linux 系統一樣,用 ALT+F1~F6 鍵可以在 6 個終端間切換。     注意:要在qemu裡點選滑鼠才可以用 ALT+F1~F6 鍵進行終端間切換。要釋放滑鼠,可以同時按住ctrl+alt並移動滑鼠。


4.7配置核心支援initrd
    到目前為止,我們的 initramfs 都由核心編譯系統生成的,並連結到核心中。其實我們也可以用 cpio 命令生成單獨的 initramfs,與核心編譯脫鉤,在核心執行時以 initrd 的形式載入到核心,以增加靈活性。首 先配置核心使用單獨的 initrd:在 Device Driver / Block device / 配置目錄下,選擇 RAM filesystem and RAMdisk ( initramfs/initrd ) support 配置項;再到 General Setup 配置目錄項下,將 initramfs source file(s) 配置項原有的內容清空。然後把核心原始碼樹的 usr 目錄下已由核心編譯生成的 initramfs 檔案 initramfs_data.cpio.gz 拷貝到 ~/initramfs-test 目錄下,我們先直接用這個檔案試驗一下 initrd 方式的 initramfs 的效果。最後,執行 make 命令重新編譯核心後,在 QEMU 試驗環境中,把 initrd 配置框(linux 配置框的下面)的內容 寫為
~/initramfs-test/initramfs_data.cpio.gz,指定 initrd 的檔案路徑。

    好了,試驗一下新的 initrd 方式的 initramfs 吧,效果跟先前的完全一樣。
    以下是我做的步驟,因為我在根據《精通initramfs構建step by step》文後製作,由於裡面說的不是很清楚,並且在Qemu Launch上的選項也沒有,所以花了比較多的時間,並且我也把其中一些沒有的步驟加上,多做實驗,多瞭解一下。
    先做個實驗。
    到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support選項下的的Initramfs source file(s)的內容清空。然後make,只好在啟動Qemu Launch。
    然後你會看到什麼,我這裡的啟動後的介面如下圖:

    由於我們沒有指定裝置,所以啟動到這裡就沒了,可以參考啟動上面的資訊。並且我們生成的bzImage檔案也小了很多。我這裡只有1.2M,原來的要2.2M。
    好,然後我們再做下一個實驗。
     到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support選項下的的Initramfs source file(s)的內容填上原來的資訊。然後make,在make的過程中我們可以看到
    GEN     usr/initramfs_data.cpio
    這樣的資訊。然後我們到linux-2.6.31/usr目錄下把initramfs_data.cpio檔案拷貝出來,我把它拷貝到了t1目錄下。然後呢,然後就 到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support選項下的的Initramfs source file(s)的內容清空,然後make。這樣生成一個沒有指定裝置的bzImage。根據上面的實驗我們知道這個bzImage是不能完全啟動的。然後我們在Qemu Launch的Linux boot的Init RAM disk裡選擇我們剛才拷貝出來的initramfs_data.cpio檔案,注意,原來這裡是我們自己用Qemu Launch生成的一個裸硬碟,現在不用這個裸硬碟,而是用initramfs_data.cpio檔案。現在的Linux boot為這樣的
    Kernel image:/home/fjb/home/test/initramfs/linux-2.6.31/arch/i386/boot/bzImage
    Initial RAM disk:/home/fjb/home/test/initramfs/t1/initramfs_data.cpio
    然後我們就可以啟動Qemu Launch,現在的啟動後又回到和我們可以用 ALT+F1~F6 鍵可以在 6 個終端間切換時的系統了。

4.8用cpio命令生成initramfs
    cpio 命令有三種操作模式:copy-out、copy-in、copy-pass,生成 initramfs 用的是它的
copy-out 模式,即把檔案打包的操 作模式。cpio 的 copy-out 操作模式使用 -o 命令列選項指定。
預設情況下,cpio 從標準輸入讀取輸入資料,向標準輸出寫入輸出資料。使用 -I 選項可以指定檔名
代替標準輸入,使用 -O 選項可以指定檔名代替標準輸出,而 -F 選項指定的檔名則根據 cpio 操作
模式的不同可代替標準輸入或標準輸出。
   
    把t1/image目錄下的檔案打包成 initramfs,進入t1/image目錄,執行下面的命令:
    find . | cpio -o -H newc | gzip > ../image.cpio.gz
    命令執行完畢後,在~/t1 目錄下就會生成檔名為 imgae.cpio.gz 的initramfs。
    上 面 cpio 命令的 -H 選項指定打包檔案的具體格式,要生成 initramfs,只能用 newc 格式,如果使用其他格式,核心會打出這樣的出錯資訊:Unpacking initramfs...<0> kernel panic - not syncing: no cpio magic 。

    在 QEMU 試驗環境下試驗一下把Initial RAM disk:選擇改成
    /home/fjb/home/test/initramfs/t1/imgae.cpio.gz 。啟動,效果跟先前的完全一樣。
 
       cpio 命令的其他用法
    如 果我們要解開一個 cpio 格式的打包檔案,則要使用 cpio 命令的 copy-in 操作模式。cpio 的 copy -out 操作模式使用 -i 命令列選項指定。例如,我們想把前一步從核心原始碼樹 usr 目錄下拷貝的initramfs_data.cpio 展開到~/t1/initramfs_data 目錄下,則使用下列命令:
    mkdir ~/t1/initramfs_data
    cd ~/t1/initramfs_data
    cpio -i -F ../initramfs_data.cpio --no-absolute-filename
    命令執行完畢後,initramfs_data 目錄下出現多個目錄和檔案,用 diff 命令比較 initramfs_data 與 image 目錄,兩者的完全一樣。
    diff -yr --suppress-common-lines image initramfs_data
    我的輸出是:
檔案 image/dev/console 是 字元特殊檔案,而 initramfs_data/dev/console 是 字元特殊檔案
檔案 image/dev/null 是 字元特殊檔案,而 initramfs_data/dev/null 是 字元特殊檔案
    所以這兩個資料夾的內容是完全一樣的。(說明一下diff的引數: -r 或--recursive  比較子目錄中的檔案。-y 或--side-by-side  以並列的方式顯示檔案的異同之處。--suppress-common-lines  在使用-y 引數時,僅顯示不同之處。)


    上面 cpio 命令的 --no-absolute-filename 選項的作用是展開檔案時,去掉檔案路徑最前面的"/", 把絕對路徑名變為相對路徑名。核心編譯時生成的 initramfs 使用了絕對路徑名,所以這個選項 必須使用,否則 initramfs 內檔案展開到"/"目錄去了,如果你是 root 使用者或有"/"目錄的寫許可權,那麼展開的檔案就有可能覆蓋同名的檔案(在 檔案修改時間新於原有檔案),那就糟糕了!
    展開檔案前,你可能會想先看看打包檔案裡都有哪些檔案,這時就要用 -t 選項了。例如,我們想看看核心編譯時生成的 initramfs_data.cpio.gz 中都有哪些檔案,我們就可以用下面的命令:
    zcat initramfs_data.cpio.gz | cpio -t
    在標準輸出中打出檔名列表。
    使用 -v 選項可以在 cpio 命令執行時輸出詳細資訊:在打包或展開檔案時,輸出已處理的檔名;與 - t 選項連用時,則顯示檔案的詳細資訊,類似 ls -l 的輸出內容。-V 選項則用打點的方式,顯示
cpio 命令的執行進度資訊,一個點代表處理一個檔案。


4.9 switch_root
    除了基於 initramfs 的系統(如第四節的 mini linux),通常 initramfs 都是為安裝最終的根檔案系統做準備工作,它的最後一步需要安裝最終的根檔案系統,然後切換到新根檔案系統上去。以往 的基於 ramdisk 的 initrd 使用 pivot_root 命令切換到新的根檔案系統,然後解除安裝 ramdisk。但是
initramfs 是 rootfs,而 rootfs 既不能 pivot_root,也不能 umount。為了從 initramfs 中切換
到新根檔案系統,需要作如下處理:
    (1)刪除 rootfs 的全部內容,釋放空間
    find -xdev / -exec rm '{}' ';'
    (2)安裝新的根檔案系統,並切換
    cd /newmount; mount --move . /; chroot .
    (3)把 stdin/stdout/stderr 附加到新的/dev/console,然後執行新檔案系統的 init 程式
上述步驟比較麻煩,而且要解決一個重要的問題:第一步刪除 rootfs 的所有內容也刪除了所有的命令,
那麼後續如何再使用這些命令完成其他步驟?busybox 的解決方案是,提供了 switch_root 命令,完
成全部的處理過程,使用起來非常方便。
    switch_root 命令的格式是:
    switch_root [-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT]
    其中 NEW_ROOT 是實際的根檔案系統的掛載目錄,執行 switch_root 命令前需要掛載到系統中; NEW_INIT 是實際根檔案系統的 init 程式的路徑,一般是/sbin/init;-c /dev/console 是可選參
數,用於重定向實際的根檔案系統的裝置檔案,一般情況我們不會使用;而 ARGUMENTS_TO_INIT 則是傳遞給實際的根檔案系統的 init 程式的引數,也是可選的。
    需要特別注意的是:switch_root 命令必須由 PID=1 的程序呼叫,也就是必須由 initramfs 的 init 程式直接呼叫,不能由 init 派生的其他程序呼叫,否則會出錯,提示:
    switch_root: not rootfs
    也是同樣的原因,init 指令碼呼叫 switch_root 命令必須用 exec 命令呼叫,否則也會出錯,提示:
    switch_root: not rootfs



什麼是mdev
    這是摘自hugerat的部落格裡的一些對mdev的說明(http://blog.csdn.net/hugerat/archive/2008/12/03/3437099.aspx):
    mdev是busybox自帶的一個簡化版的udev,適合於嵌入式的應用埸合。其具有使用簡單的特點。它的作用,就是在系統啟動和熱插拔或動態載入驅動程式時,自動產生驅動程式所需的節點檔案。在以busybox為基礎構建嵌入式linux的根檔案系統時,使用它是最優的選擇。
    更詳細的mdev的使用說明在mdev.txt文件中,你可以在busybox原始碼樹的docs目錄下找到它。
    下面是摘自mdev.txt裡的mdev的基本使用:
-----------
 Basic Use
-----------
Mdev has two primary uses: initial population and dynamic updates.  Both
require sysfs support in the kernel and have it mounted at /sys.  For dynamic
updates, you also need to have hotplugging enabled in your kernel.

Here's a typical code snippet from the init script:
[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /bin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s

Alternatively, without procfs the above becomes:
[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/bin/mdev
[3] mdev -s


Of course, a more "full" setup would entail executing this before the previous
code snippet:
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts

The simple explanation here is that [1] you need to have /sys mounted before
executing mdev.  Then you [2] instruct the kernel to execute /bin/mdev whenever
a device is added or removed so that the device node can be created or
destroyed.  Then you [3] seed /dev with all the device nodes that were created
while the system was booting.
For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem
(assuming you're running out of flash).  Then you want to [5] create the
/dev/pts mount point and finally [6] mount the devpts filesystem on it.
   
    我在這裡就不翻譯了,mdev的基本用法大概就是這樣。

本文的pdf版下載:

相關推薦

製作一個基本linux啟動

       原創文章,轉載請註明出處,謝謝!               作者:清林,部落格名:飛空靜渡 這是一篇自己很早寫的文章了,當時根據網上的一些教程一步一步製作,本來想把自己做的過程放到網上,可以西一值忙於公司專案,沒有時間,去年想放上來的時候發現csdn上傳不

學Flask()-知識鋪墊

本文為pythonWeb之flask系列第一篇,主要為後續課程打好鋪墊 一.訪問網站的流程 1.使用者在瀏覽器輸入url後,瀏覽器會訪問DNS域名解析伺服器,解析ip地址,埠號 2.客戶端與伺服器三次握手建立連線 3.客戶端向伺服器傳送請求報文,常用的請求方法有g

EasyBoot教程製作WIN7原版多重啟動方法

EasyBoot教程一:製作WIN7原版多重啟動盤方法 1.安裝EasyBoot、UltraISO軟體 2.用UltraISO開啟下載好的WIN7原版映象 3.選擇啟動,儲存引導檔案 4.儲存為win7x32.bif,以後會用到 5.開啟EasyBoot的安

一起學docker(四)--容器的基本操作

mage test soft 概念 一起 終端 class 文件系統 caf 1.創建容器Docker的容器十分輕量級,用戶可以隨時創建或刪除容器。 新建容器:docker create Example:docker create –ti ubuntu說明:使用

一起學Redis之五種基本型別及其應用場景舉例(幹了6個小時)

**前言** 來啦,老弟?來啦,上一篇就當嘮嘮嗑,接下來就開始進行實操擼命令,計劃是先整體單純說說Redis的各種用法和應用,最後再結合程式碼歸納總結。 Redis預設有16個數據庫(編號為0~15),預設使用第0個,通過命令select任意切換資料庫,和MySql切換資料庫一個道理;各資料庫之間的資

【老驥伏櫪-原創】製作黑威聯通啟動:進階篇

原文網址:http://www.nasyun.com/forum.php?mod=viewthread&tid=39748&fromuid=106494 (出處: NAS雲論壇)   前言   本文是【老驥伏櫪-狗年大禮包】的續篇進階篇。關於破解韌體,

製作Linux啟動的方法

Linux的啟動軟盤有boot盤和boot/root盤之分,所謂的boot盤只能用來啟動已經安裝在硬碟上的Linux系統,而boot/root盤本身就是一個迷你Linux系統。Linux啟動軟盤是系統修復的必備工具,因此掌握啟動軟盤的製作方法是很有用處的,下面我就向大家介紹boot盤的四種不同的製作方法: 1

製作Linux啟動並安裝Linux系統到實體機

對於通過U盤安裝系統想必大家都知道怎麼做,通過老毛桃、大白菜什麼的製作一個U盤啟動盤,開機進入BIOS設定第一啟動項為USB,不知道的童鞋看看這個http://blog.csdn.net/poetic_vienna/article/details/4518162

基於Ulinux啟動製作

本次設計是構建一個基於U盤儲存的linux啟動盤,詳細介紹了具體的構建過程。主要步驟如下: (1)   在U盤上安裝系統載入程式grub。 (2)   製作Linux根檔案系統,建立系統必要的目錄、命令和裝置。 (3)   Linux核心配置、編譯。 我的製作環境:   

win7下製作ubuntu系統安裝啟動和U安裝ubuntu全過程

這一步大家不要疏忽了,可能很多人都會選第一個吧,其實是第三個哦,點繼續 下面就進入最重要的環節了,磁碟分割槽 磁碟分割槽的原則:在一個MBR分割槽表型別的硬碟中最多隻能存在4個主分割槽。如果一個硬碟上需要超過4個以上的磁碟分塊的話,那麼就需要使用擴充套件分割槽了。如果使用擴充套件分割槽,那麼一

木木大哥學Simulink Real Time之- 建立啟動

    本文介紹用U盤建立一個啟動盤,從目標主機(Target)上啟動的詳細步驟。 一、製作準備     準備一個U盤(後面生成的檔案不到2MB),確保裡面沒有重要的檔案,因為製作啟動盤的過程中可能會格式化U盤。     啟動MATLAB,在命令列輸入sl

學習hadoop(5)----hadoop Map/Reduce教程(2)

submit calc run submitjob des conf sam ner 打開 Map/Reduce用戶界面 本節為用戶採用框架要面對的各個環節提供了具體的描寫敘述,旨在與幫助用戶對實現、配置和調優進行具體的設置。然而,開發時候還是要相應著API進行

webpack4配置詳解之一

前言   經常會有群友問起webpack、react、redux、甚至create-react-app配置等等方面的問題,有些是我也不懂的,慢慢從大家的相互交流中,也學到了不少。 ​  今天就嘗試著一起來聊聊Webpack吧,旨在幫大家加深理解、新手更容易上路,都能從0到1搭建配置自定屬於自己的腳手架

學習lucene(6)---lucene索引優化之多執行緒建立索引

這兩天工作有點忙,部落格更新不及時,請大家見諒; 前面瞭解到lucene在索引建立的時候一個IndexWriter獲取到一個讀寫鎖,這樣勢在lucene建立大資料量的索引的時候,執行效率低下的問題; 磁碟空間大小,這個直接影響索引的建立,甚至會造成索引寫入提示完成,但是沒

學習hadoop(2)----hadoop eclipse外掛安裝和執行wordcount程式

本部落格hadoop版本是hadoop  0.20.2。 安裝hadoop-0.20.2-eclipse-plugin.jar 下載hadoop-0.20.2-eclipse-plugin.jar檔案,並新增到eclipse外掛庫,新增方法很簡單:找到eclipse安裝目錄

學習lucene(11)---lucene搜尋之高亮顯示highlighter

highlighter介紹 這幾天一直加班,部落格有三天沒有更新了,望見諒;我們在做查詢的時候,希望對我們自己的搜尋結果與搜尋內容相近的地方進行著重顯示,就如下面的效果 這裡我們搜尋的內容是“一步一步跟我學習lucene”,搜尋引擎展示的結果中對使用者的輸入資訊進行了配色方

學習lucene(7)---lucene搜尋之IndexSearcher構建過程

最近一直在寫一步一步跟我學習lucene系列(http://blog.csdn.net/wuyinggui10000/article/category/3173543),個人的部落格也收到了很多的訪問量,謝謝大家的關注,這也是對我個人的一個激勵,O(∩_∩)O哈哈~,個人感

xmppmini 專案詳解:從原理學實用 xmpp 技術開發 2.登入的實現

第二章登入的實現   金庸《倚天屠龍記》   張三丰緩緩搖頭,說道:“少林派累積千年,方得達成這等絕技,決非一蹴而至,就算是絕頂聰明之人,也無法自創。”他頓了一頓,又道:“我當年在少林寺中住過,只是未蒙傳授武功,直到此時,也不明白尋常血肉之軀如何能練到這般指力。&

xmppmini 專案詳解:從原理學實用 xmpp 技術開發 4.字串解碼祕笈與訊息包

    這一節寫得比較長,不過如果您確實要手工解碼 xmpp 訊息,還是建議您仔細看看,而且事實上並不複雜。     登入成功後,我們就可以收發訊息了。訊息包的格式大致如下: 1 <message id="JgXN5-33" to="[email protected]

#使用abp框架與vue是月老的小工具(2) 後臺搭建初體驗

使用 IT UC 文件 情況 base https 檢查 目標 #使用abp框架與vue一步一步寫我是月老的小工具(2) 後臺搭建初體驗 一、續上前言 關於這個小玩意的產品思考,假設我暫時把他叫我是月老熱心人 這是一個沒有中心的關系鏈,每個人進入以後都