從頭開始構建一個嵌入式 Linux 發行版

Peter Seebach, 自由作家, Plethora.net
學習如何構建一個在嵌入式環境中使用定製 Linux 發行版,以驅動 Technologic Systems TS-7800 單板計算機。在這篇教程中,將學習交叉編譯、啓動裝載器、文件系統、根文件系統、磁盤鏡像和啓動過程,您可以在構建系統和創建發行版時選擇它們。

開始之前

目標

本教程展示如何在一個目標系統上安裝 Linux。這不是一個預先構建的 Linux 發行版,而是您從頭構建發行版。雖然在不同目標系統上安裝 Linux 的過程在細節上有差異,但總的原則是相同的。

本教程幫助您構建(如果您有一個合適的目標系統)一個有效的 Linux 系統,您可以在這個系統上使用 shell 提示符。

關於本教程

本教程首先討論交叉編譯問題,然後討論 Linux 系統的組成部分,以及它們是如何結合在一起的。本教程還談到了構建和安裝,以及目標系統的配置。

本教程討論一個特定的目標 Technologic Systems TS-7800,它使用自己的默認啓動和 bring-up 行爲;其他系統將使用其他的機制,本文不詳細地討論每種可能的啓動裝載器。

先決條件和系統需求

本教程針對對目標嵌入式系統感興趣,或者想學習更多關於 Linux 系統的開發人員。他們將從本教程獲益不淺。

本教程使用的主機環境是 Ubuntu,但其他系統也可以。本教程假定用戶基本熟悉 UNIX® 或 Linux 系統管理,並且有主機系統的根訪問權限。

本教程假定您 shell 是 Bourne shell 的變體;如果您使用的是 C shell 變體,那麼提示符可能會不同,需要使用不同的命令來設置環境變量。

對於交叉編譯(在嵌入式系統中比較有用),我使用了 2008 年 5 月發行的 crosstool-ng version 1.1.0。您可以從發行站點下載它(參見 參考資料)。後面有關於 安裝和配置它 的詳細信息。





關於目標和架構

目標

我選擇的目標是一個 Technologic Systems TS-7800(詳細信息請參閱 參考資料)。這是一個小型的嵌入式 ARM 系統,同時具有內置的和可移動的 flash 存儲,還有一個 SATA 控制器。本教程引導您啓動到一個登錄提示符,而不需要依賴預先構建的二進制文件。

架構

我選擇了 ARM 架構,這便於檢查一個給定的二進制文件是主機還是目標,並且便於查看是否發生主機污染。使用一臺總功率爲 5W,能夠安靜運行的機器也不錯。




交叉編譯

什麼是交叉編譯?

交叉編譯是在一個系統上使用編譯器來開發在另一個系統上運行的代碼。交叉編譯對於偶爾使用 UNIX 的用戶而言比較少見,因爲在默認情況下,只在本系統上安裝需要使用的編譯器。然而,當以嵌入式系統爲目標時,交叉編譯就相當常見。即使主機和目標具有相同的架構,也必須區分它們的編譯器。它們可能有不同版本的庫,或者使用不同的編譯器選項構建的庫,所以用主機編譯器編譯的東西在目標系統上不能運行,或者不能像預期的那樣運行。

獲取交叉編譯工具

理論上,可以自己構建一個交叉編譯器,但這很不實際。因爲所需的一系列啓動階段(bootstrap stage)很複雜耗時,而且常常需要構建一個非常小的編譯器,用來部分地配置和構建庫,然後使用這些庫的頭文件重新構建編譯器,使新的編譯器能夠使用它們,等等。有很多商業源代碼可以實現在不同架構組合上使用交叉編譯器,也有一些免費的交叉編譯工具包。

crosstool-ng 簡介

Dan Kegel 的 crosstool(詳細信息請參閱 參考資料)收集了多種不同的專門技術和一些專用的補丁,用於爲許多系統自動構建工具鏈。crosstool 有一段時間沒有更新了,但新的 crosstool-ng 項目是它的擴展。對於本教程,我使用了 2008 年 5 月發行的 crosstool-ng version 1.1.0。可以從發行站點下載它(參見 參考資料)。

安裝 crosstool-ng

crosstool-ng 有一個 configure 腳本。要配置它,只需使用 --prefix 來運行這個腳本,然後設置一個位置。例如:

$ ./configure --prefix=$HOME/7800/ctng

完成配置之後,分別使用 makemake install 來構建它。構建過程將在包含 crosstool-ng 構建腳本到 7800 工作目錄下創建一個 ctng 目錄。將 ctng/bin 子目錄添加到路徑中:

$ PATH=$PATH:$HOME/7800/ctng/bin

配置 crosstool-ng

crosstool-ng 使用一個 .config 文件,該文件類似於 Linux 內核使用的文件。爲了使用 crosstool-ng,需要創建一個與目標相匹配的配置文件。爲 crosstool-ng 構建創建一個工作目錄:

$ mkdir toolchain-build
$ cd toolchain-build

現在,複製一個默認配置。雖然可以手動配置 crosstool-ng,但是有一個示例配置剛好符合要求:

$ cp ../ctng/lib/ct-ng-1.1.0/samples/arm-unknown-linux-uclibc/* .

最後,重命名 crosstool.config 文件:

$ mv crosstool.config .config

這將拷入一個配置文件,其目標是一個 armv5te 處理器,這是在 TS-7800 上使用的型號。它用 uClibc 構建,這是用於嵌入式系統的 libc 變種。但是,這個配置文件有一個地方需要修改。

修復配置路徑

crosstool-ng 構建的默認目標目錄是 $HOME/x-tools/$TARGET。例如,對於這個構建,這個目錄是 x-tools/arm-unknown-linux-uclibc。如果爲很多目標進行構建,這非常有用,但如果只是爲一個目標進行構建,就不是很有用。編輯 .config 文件,將 CT_PREFIX_DIR 改爲 ${HOME}/7800/toolchain

構建工具鏈

要構建工具鏈,用 build 參數運行 ct-ng 腳本。爲了提高性能,尤其是在多核系統上,可能需要運行多個任務,這些任務用 build.# 來指定。例如,下面的命令用 4 個任務進行構建:

$ ct-ng build.4

取決於主機系統,這可能需要較長時間。完成後,工具鏈被安裝在 $HOME/7800/toolchain 中。該目錄和它的內容被標記爲只讀。如果需要刪除或移動它們,可以使用 chmod u+wct-ng 腳本還帶有其他一些參數,例如 help。注意,ct-ng 是用於標準 make 實用程序的腳本,因此,--help 的輸出只是標準的 make 幫助;可以使用 ct-ng help 獲得關於 crosstool-ng 的幫助。

如果您以前沒見過這個技巧,就會覺得它很巧妙。現代 UNIX 系統將第一行以 #! 開頭的可執行文件解釋爲腳本,用於以第一行剩下部分命名的程序。例如,很多 shell 腳本以 #!/bin/sh 開頭。該文件的文件名被傳遞給程序。對於將第一個參數當作要運行的腳本的程序,這樣就足夠了。雖然 make 不會自動那樣做,但是可以使用 -f 標誌爲它提供一個可運行的文件。ct-ng 的第一行是 #!/usr/bin/make -rf-r 標誌禁用 make 內建的默認構造規則,而 -f 標誌則告訴它接下來的單詞(腳本的文件名)是一個文件的文件名,而不是一個有名稱的 Makefile。結果得到一個使用 make 語法而不是 shell 語法的可執行腳本。

使用工具鏈

對於初學者,將包含編譯器的目錄添加到路徑中:

$ PATH=~/7800/toolchain/bin:$PATH

目錄在路徑中之後,現在便可以編譯程序了:

$ arm-unknown-linux-uclibc-gcc -o hello hello.c $ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for
GNU/Linux 2.4.17, dynamically linked (uses shared libs), not stripped

庫在哪裏?

工具鏈用於鏈接二進制文件的庫存儲在 toolchain 目錄下的 arm-unknown-linux-uclibc/sys-root 中。這個目錄構成最終的根文件系統的基礎,我們將在構建內核之後在 文件系統 中討論這個話題。

內核設置

供應商提供的內核發行版樹已經配置了交叉編譯。在最簡單的情況下(就像這裏),交叉編譯 Linux 內核惟一要做的就是在頂級 Makefile 中設置 CROSS_COMPILE 變量。這是一個前綴,放在構建期間使用的各個不同程序(gcc、as、ld)的名稱前面。例如,如果將 CROSS_COMPILE 設置爲 arm-,則編譯器將嘗試在路徑中發現一個名爲 arm-gcc 的程序。對於這個構建,正確的值是 arm-unknown-linux-uclibc。如果不想依賴於路徑設置,也可以指定完整的路徑,例如:

CROSS_COMPILE ?= $(HOME)/7800/toolchain/bin/arm-unknown-linux-uclibc-




構建內核

下載源代碼

下載 Technologic 的 Linux 源代碼和 TS-7800 配置文件,並將它們解壓縮到適當位置。

內核配置

內核配置的詳細討論超出了本教程的範圍。在這裏,ts7800_defconfig 目標給出一個可用於 7800 的默認配置,但有一個小小的缺點:CONFIG_DMA_ENGINE 設置應該開啓的時候卻關閉了。

調試內核

通常最好使用 make menuconfig 編輯內核,它提供了一個半圖形化的界面用於內核配置。在這個界面中,可以使用箭頭鍵移動光標,使用 Tab 鍵選擇屏幕底部的選項,並使用空格鍵或 Enter 鍵確定選項。例如,要不保存任何更改退出,可以按 Tab 鍵,直到屏幕底部的 <Exit> 高亮顯示,然後按下 Enter。再次運行 make menuconfig 將重新打開編輯器。

更改默認的控制檯

TS-7800 一般是靜默啓動的,因爲默認內核配置指定了一個空控制檯設備,它使顯示屏保持安靜。要改變這一點,可以使用箭頭鍵導航到 “Boot options”,並按下 Enter 鍵。第三行顯示默認的內核選項,它選擇 ramdisk、啓動腳本和控制檯。使用箭頭鍵導航到這一行,按下 Enter 鍵,將 console none 改爲 console ttyS0,115200。然後,按 Tab 鍵將光標移動到面板底端的 <Ok>,並按下 Enter 鍵。現在按 Tab 鍵選擇 <Exit>,並按下 Enter 鍵回到主菜單。

控制檯設備對快速啓動沒有任何幫助,而且以較高的波特率發送內核消息要花費不少時間,這段時間要計入系統啓動時間。但是,爲了調試和測試,您需要這個控制檯。

啓用 DMA 引擎

向下導航到 “Device drivers”,按下 Enter 鍵。這個列表比通常顯示的列表要長,所以必須向下滾動到快結束的地方纔能到達 “DMA Engines” 選項。用箭頭鍵導航到該選項,按下 Enter 鍵。在這個頁面的頂端有兩個帶方括號的選項,表明這是布爾選項。在我使用的下載中,第二個選項 “Support for DMA engines” 在默認情況下沒有啓用。用箭頭鍵導航到該選項,按下空格鍵切換它的狀態。現在使用 TabEnter 鍵從屏幕中選擇 <Exit>,退回到程序的頂層,然後再次選擇 <Exit> 離開程序。當被詢問是否要保存新的內核配置時,用 Tab 鍵選擇 <Yes> 並按下 Enter 鍵。

編譯內核

輸入 make。沒錯,就這麼簡單!這將構建一個內核,以及一個模塊集合。同樣,多核用戶可能需要多個任務,例如 make -j 5。對於本項目,我打算忽略內核模塊,只編譯需要的特性。前面用於將所需的驅動器裝入內核的啓動 ramdisk 技術看上去有些繁瑣,而構建一個根文件系統就已經夠複雜了。當然,這又引出如何讓內核啓動的問題,這是下一節的主題。




啓動裝載器

什麼是啓動裝載器?

啓動裝載器(或啓動類裝載器)是一個裝載另一個程序的程序。啓動裝載器是一小塊專用代碼,它特定於一個目標系統,具有足夠的複雜性來發現並裝載內核,但不是一個包含全部功能的內核。不同的系統使用不同的啓動裝載器,從桌面 PC 上常見的大型的、複雜的 BIOS 程序,到嵌入式系統上常見的非常小的、簡單的程序。

一個簡單的啓動裝載器

TS-7800 使用一個非常簡單的啓動裝載器,這個裝載器只是從 SD 卡或主板 flash 一個預先確定的分區中獲取內核。通過一個跳線(jumper)可以確定主板先查看主板 flash 還是先查看 SD 卡。這裏沒有其他配置設置。更復雜的啓動裝載器(例如桌面 PC 上常見的 grub)則有用於在啓動時配置內核選項的選項。如前所述,在這個系統上,內核的默認選項必須編譯進來。

在嵌入式系統中,決定在內核選項編譯是有意義的,但是在桌面 PC 上則不合適。

內核格式

內核可以以很多不同的格式存儲。最初的 Linux 內核二進制文件(名爲 vmlinux)很少是啓動裝載器可直接使用的文件。在 TS-7800 上,啓動裝載器可以使用兩種文件,一種是 Image(無壓縮),另一種是 zImage(經過壓縮)。這些文件是在內核樹中的 arch/arm/boot 目錄中創建的。

人們常將 zImage 內核描述爲被壓縮的內核,所以覺得啓動裝載器需要提供解壓功能。實際上,它比想象的更聰明。zImage 內核是一個無壓縮的可執行文件,其中包含特別大的靜態數據對象,後者是一個壓縮的內核鏡像。當啓動裝載器裝載並運行 zImage 可執行文件時,可執行文件就會解壓內核鏡像,並執行它。這樣便可以最大限度地受益於壓縮,而又不必在啓動裝載器上付出額外的努力。

設置 SD 卡

SD 卡需要一個包含 DOS 樣式分區信息的 MBR 表。Technologic 站點上提供了一個可下載的示例 MBR 表。這個 MBR 表是用於 512MB 卡的,但是很容易通過編輯第 4 個分區,使之適用於任何大小的卡。前三個分區大小都是 4MB;第一個分區在較小的卡上不使用,第二和第三個分區分別存放內核和初始的 ramdisk 鏡像。

對於本教程,我使用了 sfdisk 實用程序,這個實用程序並不總是值得推薦,但是當創建一個不在分區邊界上的分區時,它的表現令我信服。

安裝初始內核

用於 TS-7800 的內核被直接轉儲到 SD 卡的第二個分區中。這張 SD 卡的確切路徑取決於如何訪問它;通常,如果將這張卡放在一個 USB 讀卡器上,那麼它將被檢測爲一個 SCSI 設備。注意,這裏沒有涉及對文件系統的訪問,只是將原始內核轉儲到分區。dd 命令將原始數據從一個源複製到另一個源,如下面的例子所示:

$ dd if=zImage of=/dev/sdd2 bs=16k
93+1 records in
93+1 records out
1536688 bytes (1.5 MB) copied, 0.847047 s, 1.8 MB/s

該命令將原始數據以 16KB 的塊從 zImage 文件轉儲到 /dev/sdd 的第二個分區。

關於塊的大小

這個命令的輸出有點隱祕(和它的輸入一樣)。當 dd 運行時,它將數據複製到 “記錄” 中,這些記錄默認情況下是 512 字節的塊。這個命令指定塊大小爲 16k(dd 理解爲 16*1024)。

dd 命令報告首先複製到塊中的數據量;加號後面的數字是複製的不完整的塊。在這裏,由於 1,536,688 不是塊大小的倍數,文件中剩下的字節作爲一個不完整的塊單獨讀(寫)。該信息對於大多數現代設備是無害的,但是對於一些較老的設備則是診斷問題的關鍵。

控制塊大小(以及在傳輸數據時將數據重新分塊)的能力對於使用磁帶設備和要求以固定大小寫數據的其他專用媒介特別有用,而且對於 flash 設備的性能和可靠性也有幫助。

雖然代表 flash 媒介的內核設備常常可以接受任意大小的寫,但是對於底層設備,常見的是隻進行整塊的寫,每個塊的大小通常比較大(4KB 或更大)。如果要進行部分寫,那麼 flash 設備必須從整個塊中提取當前內容,用輸入數據修改它們,然後刷新整個塊。如果一個設備使用 4KB 的塊,並且按 512 字節的塊向該設備寫數據,那麼對於一個複製,每個設備塊要重寫 8 次。這對於設備的使用壽命和性能而言很糟糕。(對於同一個文件,按 512 字節的塊寫數據的速度是我使用的 flash 卡的一半)。

啓動內核

啓動內核非常簡單。將跳線設置爲 SD 啓動,將卡插入到系統中,然後加電。如果使用一張空白的卡,那麼這將產生一個可預測的結果:

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)

這條隱祕的消息表明,內核沒能發現它的根文件系統。這意味着現在應該創建一個根文件系統。



文件系統

根文件系統

通常,Linux 只有一個根文件系統。但是,在啓動期間,經常使用一個臨時的根文件系統來裝載和配置設備驅動器等,然後獲得實際的根文件系統,並切換到該文件系統。TS-7800 爲 SD 驅動器和多種不同的配置選項使用這個臨時文件系統(稱爲初始 ramdisk 或 initrd)。實際上,這個 ramdisk 用於可能由另一個系統上的啓動裝載器處理的事情(例如確定最終使用的根文件系統)。

操作文件系統

有一條常見的注意事項:大多數文件系統操作需要根用戶權限。但是也有一些例外和變通方法。最值得注意的是 fakeroot 實用程序/庫,它允許用 “fakeroot 支持” 構建應用程序。這樣便可以操縱一個虛擬根文件系統,並且可以控制所有權、許可等。但是,許可和相關材料是在一個單獨的數據庫中維護的,不需要真正但特權操作。這使得非根用戶可以藉助虛擬根權限操縱目錄層次結構,並最終創建包含用戶本無權限創建的文件歸檔或文件系統鏡像。對於本教程,我假定擁有根用戶權限。

更改根

chroot 命令雖然不得不提,但是對於更改根文件系統還不夠。這裏使用的工具是 pivot_root,它將一個有名稱的目錄指定爲新的根目錄,並將舊的根目錄換爲另一個名稱(實際上是更改舊的根目錄的掛載點)。

權限不是很充分的根

對於本教程,我假定主板提供的默認的 initrd 鏡像已經足夠。Linux 內核發行版具有對生成適用的 initrd 鏡像的支持,這方面的細節對於理解構建一個真正的發行版並不重要。我將介紹使系統運行所需的關鍵東西,然後關注真正的根文件系統上是如何工作的。

文件系統鏡像

大多數 Linux 用戶都熟悉包含 ISO CD-ROM 或 DVD 文件系統鏡像的文件。Linux 還支持其他類型的文件系統的創建和使用。實際上,通過使用 mount-o loop 選項,可以將任何文件當作一個磁盤分區。例如,在研究這一點時,我爲 TS-7800 使用的 initrd 鏡像做了一個副本:

$ dd if=/dev/sdd3 of=initrd.dist bs=16k
$ cp initrd.dist initrd.local

有了這個文件,就可以掛載文件系統,以查看它。名爲 initrd.local 的副本是用於本地編輯的版本,名爲 initrd.dist 的副本是安全的原始副本,以防以後內容被損壞。

$ mount -o loop initrd.local /mnt

initrd 是做什麼的

initrd 提供一個非常基本的初始文件系統,用於通過發現並掛載實際根文件系統來引導設備。默認的內核配置被硬接線爲使用一個 initrd(/dev/ram0)作爲根,並在啓動時運行 linuxrc 腳本。這個腳本有一些用於不同根文件系統的變種。很明顯,應該選擇 linuxrc-sdroot 變種。

準備 initrd

linuxrc-sdroot 程序嘗試將系統配置爲使用一個 SD 卡作爲根。假設 SD 卡經過正確的配置,且在第 4 個分區上有一個 ext3 文件系統,該腳本將掛載那個文件系統,並在它上面運行 /sbin/init。這看上去像是一個很好的策略。但還需要對 initrd 鏡像作兩處小小的修改。首先是將 linuxrc symlink 改爲指向 linuxrc-sdroot。然後是將該腳本更新爲使用 uClibc 構建,而不是之前它爲之作了配置的 Debian 構建。

SD 驅動器

用於 TS-7800 的 SD 驅動器包括一個專用的模塊,所以只需手動拷入 tssdcard.ko 文件,並在運行時裝載它。發行版 initrd 上的文件可以在新內核上運行,因此不需要更改。

切換 bootstrap 例程

內核只需運行名爲 linuxrc 的程序。默認情況下,這是程序 linuxrc-fastboot 的一個符號鏈接,該程序很快就會找到 initrd 根文件系統。如果去掉這個鏈接,而將 linuxrc 鏈接到 linuxrc-sdroot,則會導致在裝載驅動程序之後系統嘗試從 SD 上的最後一個分區啓動。

更改 bootstrap 例程

該腳本檢查錯誤,如果發現錯誤,則從內部 flash 掛載文件系統。如果沒有發現錯誤,則從 SD 卡掛載文件系統,然後嘗試更改它。bootstrap 例程實際上有一個小小的缺陷,這會影響到這個構建。在調用 pivot_root 之後,爲了進行最後的清理工作,它嘗試使用自己的 mount 實用程序,而不是根文件系統中的程序。這對於 Debian 安裝是可以的,但是對於最低限度要求的 uClibc 卻行不通。解決辦法是更改文件最後 pivot_root 命令後面的行(在 else 子句之後):

pivot_root . ./initrd
/bin/mount -n --move ./initrd/dev ./dev
/bin/mount -n --move ./initrd/sys ./sys
/bin/mount -n --move ./initrd/proc ./proc
exec /sbin/init < $CONSOLE > $CONSOLE 2>&1

這導致腳本運行您要創建的新的可執行文件(uClibc,靜態鏈接),而不是嘗試運行動態鏈接的來自另一個系統的可執行文件。(在沒有指導的情況下,您可能要等到完成所有其他設置並開始收到隱祕的錯誤消息才知道這一點)。

卸載磁盤鏡像

像下面這樣卸載 initrd 磁盤鏡像:

$ umount /mnt



填充根文件系統

將什麼裝入根文件系統?

根文件系統需要一個 /sbin/init,它將做我們期望的事情。這是一個定製的程序,而不是一個常規的 UNIX 型的 init,但是如果提供一個具有控制檯 shell 和所有東西的實用環境,系統就更易於使用。

基本庫和頭文件

crosstool-ng 構建創建了一個默認的系統根,它提供庫代碼和頭文件。爲它創建一個副本:

$ cp -r toolchain/arm-unknown-linux-uclibc/sys-root rootfs

注意,現在並沒有做什麼來修復許可;對於一個實際的發行版,可能需要對此作出更正,但是這對於演示並無大礙。這使您有了一個根文件系統的第一個基本部分,其中只包含將來的構建將使用的庫(和頭文件)。

Busybox

說到嵌入式根文件系統,busybox 可能是最好的起點。busybox 提供一套完整的核心繫統實用程序,這些實用程序被構建在一個二進制文件中,它使用調用時提供的名稱來確定執行什麼函數。通過 busybox 和很多鏈接,數十個主系統程序得以擠進很小的空間中。這並不總是必需的,但對於我們來說比較方便。

下載 busybox

可以從下載頁面下載 busybox(參見 參考資料)。這個歸檔文件是一個標準的 tarball,可以解壓到一個構建目錄中。我使用的是 1.10.2 版。

配置 busybox

busybox 圍繞類似的配置工具構建成用於 crosstool-ng 和 Linux 的配置工具。爲了構建一個默認的配置,運行 make defconfig。這將創建默認的配置。通過後面的 make menuconfig 可以更改設置;通過 “Busybox Settings” 下的 “Build Options” 菜單,可以指定一個靜態的構建。運行該命令,因爲 uClibc 的默認的 crosstool-ng 構建需要它。有時候,您可能偏愛一個動態鏈接的二進制文件,以減少可執行文件的大小,但是對於幾乎每個二進制文件都只是一個符號鏈接的系統來說,大小問題不是那麼重要。對於這個內核,頂級的 Makefile 有一個 CROSS_COMPILE 變量,該變量存放在編譯器工具的名稱上使用的前綴。將它設置成前面使用的同一個值。

構建 busybox

要構建 busybox,只需運行 make。令人驚訝的是,在構建期間有一個編譯問題,這個編譯問題要求將 <linux/types.h> 添加到 libbb.h 頭文件中。然後,編譯很快就能完成。

安裝 busybox

要安裝 busybox,需要運行 make install,不過要指定根文件系統路徑:

$ make CONFIG_PREFIX=$HOME/7800/rootfs install

這將拷入 busybox 二進制文件,並創建所有必要的鏈接。這個根文件系統現在有了庫和所有常用的 UNIX 實用程序。還缺少什麼呢?

設備節點

您需要設備節點。有兩種方法爲根文件系統提供設備節點。一種方法是靜態地創建計劃要有的所有設備節點,另一種方法是使用 udev 之類的東西,它爲當前內核提供自動更新的設備節點樹。雖然 udev 非常優雅,但是它很慢,嵌入式系統可以預測它們的硬件組件。

創建所需的設備節點

很多程序需要訪問設備節點,例如 /dev/console/dev/null。設備節點通常是在運行時使用 busybox 中的 mdev 實用程序在 ARM 系統上創建的。實際上,這項工作已經由 initrd 文件系統做了;一個變化是將已經填充好的 /dev 掛載點移到新的根文件系統上。您可以手動創建關鍵的設備節點,但是這比較麻煩。而 mdev 則更快捷。

掛載點

有些掛載點是必需的。/sys/proc 掛載點用於移動 sysfs 和 procfs 虛擬文件系統。/dev 目錄被用作一個 tmpfs 文件系統的掛載點,該文件系統是由 mdev 填充的。這些目錄不需要任何其他內容:

$ cd $HOME/7800/rootfs
$ mkdir sys proc dev

其他

/etc 目錄雖然不是獲得 shell 提示符所必需的,但是它對於獲得一個有用的 shell 提示符十分有用。在初始啓動期間,最重要的是 init.d 子目錄,init 從該目錄中搜索系統啓動文件。創建一個普通的 rcS 腳本,並獲得初始系統啓動時執行該腳本的許可:

$ mkdir rootfs/etc/init.d

創建 rootfs/etc/init.d/rcS 文件,其中包含以下內容:

#!/bin/sh
echo "Hello, world!"

現在將它標記爲可執行:

$ chmod 755 rootfs/etc/init.d/rcS



創建一個磁盤鏡像

創建一個大型的空文件

可以通過從 /dev/zero 中將數據庫拷入一個文件中來創建一個文件系統鏡像,或者直接複製一個已有的鏡像。不管有多大的可用空間(記住,12MB 已經被預留給前三個分區),都可以用 dd 創建那麼大的一個文件。這樣將創建一個很好的空間充足的 64MB 根文件系統:

$ dd if=/dev/zero of=rootfs.local bs=1M count=64

格式化文件

不僅在磁盤上,還可以在文件上運行各種不同的 mkfs 實用程序。要構建一個 ext3 根文件系統(initrd 所需的文件系統),在磁盤鏡像上運行 mkfs.ext3

$ mkfs.ext3 rootfs.local

mkfs 實用程序提示您是否在文件不是塊專業設備時仍然繼續:

rootfs.tmp is not a block special device.
Proceed anyway? (y,n) y

掛載新的磁盤鏡像

要掛載磁盤鏡像,使用 mount 命令的 -o loop 選項將鏡像掛載在某個地方。

$ mount -o loop rootfs.local /mnt

將 rootfs 文件複製到掛載的磁盤上

pax 實用程序在此特別管用。

$ cd 7800/rootfs
$ pax -r -w -p e . /mnt

這將把前面創建的目錄樹複製到 /mnt,保留符號鏈接和專用文件(除了嵌套字(socket),但是沒有嵌套字也沒關係)。

卸載鏡像

同樣,完成鏡像上的工作後,卸載鏡像:

$ umount /mnt



前一頁 第 9 頁,共 13 頁 後一頁

文檔選項
將打印機的版面設置成橫向打印模式

打印本頁


對本教程的評價

幫助我們改進這些內容


使鏡像回到卡上

拷迴文件

您可能想知道爲什麼前面的過程不直接使用卡作爲文件系統。答案是,一般最好不要頻繁使用 flash 文件系統,因爲在重負載下 flash 設備的使用壽命遠遠短於硬盤驅動器。因此,複雜的編輯和複製都是先在磁盤鏡像上執行,然後通過一次性的寫操作將磁盤鏡像拷回到卡上。

拷入 initrd

將 initrd 鏡像拷入到 SD 卡的第三個分區:

$ dd if=initrd.local of=/dev/sdd3 bs=16k

拷入根文件系統

同樣,將文件系統鏡像拷入到 SD 卡的第四個分區。

$ dd if=rootfs.local of=/dev/sdd4 bs=16k

等待設備訪問

不要馬上從讀卡器中拔出 SD 卡。有時候,還需多等幾秒鐘,讓系統完成寫操作。一直等到所有活動指示燈穩定下來,然後再多等幾秒鐘,以確保無誤。



回顧啓動過程

init 的作用

init 程序管理系統啓動,然後使系統保持運行。例如,當進程關閉時,init 收集它們的退出狀態值,使內核可以轉存它們。具體的啓動過程因 Linux 的版本而異。在這裏,是由 busybox 版本的 init 啓動測試系統。

您可能想知道,爲什麼前面的啓動腳本使用 exec /sbin/init,而不是調用 init。原因是,啓動腳本是前面的 init,有特殊的進程 ID 1。init 程序不會成爲系統的啓動守護進程,除非它的進程 ID 是 1;exec 導致它接過調用者 shell 的進程 ID,而不是獲取一個新的 ID。

初始啓動

init 程序首先運行第一個系統啓動腳本 /etc/init.d/rcS。(如果沒有那個文件,它打印出一條警告消息,並繼續)。然後,如果有 /etc/inittab,它根據其中的指令運行。如果不存在 /etc/inittab,那麼 busybox init 在控制檯上運行一個 shell,並重新啓動和停止請求。

結束語

有了自己的內核,一個稍加定製的 initrd,以及根自己但文件系統,現在您應該可以啓動系統並進入一個 shell 提示符。如果您創建了 rcS 文件,那麼系統應該會在啓動時顯示問候語。現在,您有了一個可以使用的小型的 Linux 系統,這個系統完全從源代碼構建,並且是手工裝配的。

接下來做什麼就由您決定了。我建議安裝一些視頻遊戲,不過您可以配置這個系統,使之作爲數據庫服務器或 Web 服務器。如果您打算做任何導致大量磁盤活動的事情,應該考慮使用一個通過 USB 連接的外部硬盤驅動器。




參考資料

學習

獲得產品和技術

討論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章