Linux中國

定製嵌入式 Linux 發行版

便宜的物聯網板的普及意味著它不僅會控制應用程序,還會控制整個軟體平台。 那麼,如何構建一個針對特定用途的交叉編譯應用程序的自定義發行版呢? 正如 Michael J. Hammel 在這裡解釋的那樣,它並不像你想像的那麼難。

為什麼要定製?

以前,許多嵌入式項目都使用現成的發行版,然後出於種種原因,再將它們剝離到只剩下基本的必需的東西。首先,移除不需要的包以減少佔用的存儲空間。在啟動時,嵌入式系統一般不需要大量的存儲空間以及可用存儲空間。在嵌入式系統運行時,可能從非易失性內存中拷貝大量的操作系統文件到內存中。第二,移除用不到的包可以降低可能的攻擊面。如果你不需要它們就沒有必要把這些可能有漏洞的包掛在上面。最後,移除用不到包可以降低發行版管理的開銷。如果在包之間有依賴關係,意味著任何一個包請求從上游更新,那麼它們都必須保持同步。那樣可能就會出現驗證噩夢。

然而,從一個現有的發行版中去移除包並不像說的那樣容易。移除一個包可能會打破與其它包保持的各種依賴關係,以及可能在上游的發行版管理中改變依賴。另外,由於一些包原生集成在引導或者運行時進程中,它們並不能輕易地簡單地移除。所有這些都是項目之外的平台的管理,並且有可能會導致意外的開發延遲。

一個流行的選擇是使用上游發行版供應商提供的構建工具去構建一個定製的發行版。無論是 Gentoo 還是 Debian 都提供這種自下而上的構建方式。這些構建工具中最為流行的可能是 Debian 的 debootstrap 實用程序。它取出預構建的核心組件並允許用戶去精選出它們感興趣的包來構建用戶自己的平台。但是,debootstrap 最初僅在 x86 平台上可用,雖然,現在有了 ARM(也有可能會有其它的平台)選項。debootstrap 和 Gentoo 的 catalyst 仍然需要從本地項目中將依賴管理移除。

一些人認為讓別人去管理平台軟體(像 Android 一樣)要比自己親自管理容易的多。但是,那些發行版都是多用途的,當你在一個輕量級的、資源有限的物聯網設備上使用它時,你可能會再三考慮從你手中被拿走的任何資源。

系統引導的基石

一個定製的 Linux 發行版要求許多軟體組件。其中第一個就是 工具鏈 toolchain 。工具鏈是用於編譯軟體的一套工具集。包括(但不限於)一個編譯器、鏈接器、二進位操作工具以及標準的 C 庫。工具鏈是為一個特定的目標硬體設備專門構建的。如果一個構建在 x86 系統上的工具鏈想要用於樹莓派,那麼這個工具鏈就被稱為交叉編譯工具鏈。當在內存和存儲都十分有限的小型嵌入式設備上工作時,最好是使用一個交叉編譯工具鏈。需要注意的是,即便是使用像 JavaScript 這樣的需要運行在特定平台的腳本語言為特定用途編寫的應用程序,也需要使用交叉編譯工具鏈編譯。

圖 1. 編譯依賴和引導順序

交叉編譯工具鏈用於為目標硬體構建軟體組件。需要的第一個組件是 引導載入程序 bootloader 。當計算機主板加電之後,處理器(可能有差異,取決於設計)嘗試去跳轉到一個特定的內存位置去開始運行軟體。那個內存位置就是保存引導載入程序的地方。硬體可能有內置的引導載入程序,它可能直接從它的存儲位置或者可能在它運行前首先拷貝到內存中。也可能會有多個引導載入程序。例如,第一階段的引導載入程序可能位於硬體的 NAND 或者 NOR 快閃記憶體中。它唯一的功能是設置硬體以便於執行第二階段的引導載入程序——比如,存儲在 SD 卡中的可以被載入並運行的引導載入程序。

引導載入程序能夠從硬體中取得足夠的信息,將 Linux 載入到內存中並跳轉到正確的位置,將控制權有效地移交到 Linux。Linux 是一個操作系統。這意味著,在這種設計中,它除了監控硬體和向上層軟體(也就是應用程序)提供服務外,它實際上什麼都不做。Linux 內核 中通常是各種各樣的固件塊。那些預編譯的軟體對象,通常包含硬體平台使用的設備的專用 IP(知識資產)。當構建一個定製發行版時,在開始編譯內核之前,它可能會要求獲得一些 Linux 內核源代碼樹沒有提供的必需的固件塊。

應用程序保存在根文件系統中,這個根文件系統是通過編譯構建的,它集合了各種軟體庫、工具、腳本以及配置文件。總的來說,它們都提供各種服務,比如,網路配置和 USB 設備掛載,這些都是將要運行的項目應用程序所需要的。

總的來說,一個完整的系統構建要求下列的組件:

  1. 一個交叉編譯工具鏈
  2. 一個或多個引導載入程序
  3. Linux 內核和相關的固件塊
  4. 一個包含庫、工具以及實用程序的根文件系統
  5. 定製的應用程序

使用適當的工具開始構建

交叉編譯工具鏈的組件可以手工構建,但這是一個很複雜的過程。幸運的是,現有的工具可以很容易地完成這一過程。構建交叉編譯工具鏈的最好工具可能是 Crosstool-NG,這個工具使用了與 Linux 內核相同的 kconfig 菜單系統來構建工具鏈的每個細節和方面。使用這個工具的關鍵是,為目標平台找到正確的配置項。配置項通常包含下列內容:

  1. 目標架構,比如,是 ARM 還是 x86。
  2. 位元組順序:小端位元組順序(一般情況下,Intel 採用這種順序)還是大端位元組順序(一般情況下,ARM 或者其它的平台採用這種順序)。
  3. 編譯器已知的 CPU 類型,比如,GCC 可以使用 -mcpu--with-cpu
  4. 支持的浮點類型,如果有的話,比如,GCC 可以使用 -mfpu--with-fpu
  5. 二進位實用工具 binutils 、C 庫以及 C 編譯器的特定版本信息。

圖 2. Crosstool-NG 配置菜單

前四個一般情況下可以從處理器製造商的文檔中獲得。對於較新的處理器,它們可能不容易找到,但是,像樹莓派或者 BeagleBoards(以及它們的後代和分支),你可以在像 嵌入式 Linux Wiki 這樣的地方找到相關信息。

二進位實用工具、C 庫、以及 C 編譯器的版本,將與任何第三方提供的其它工具鏈分開。首先,它們中的每一個都有多個提供者。Linaro 為最新的處理器類型提供了最先進的版本,同時致力於將該支持合併到像 GNU C 庫這樣的上游項目中。儘管你可以使用各種提供者的工具,你可能依然想去使用現成的 GNU 工具鏈或者相同的 Linaro 版本。

在 Crosstool-NG 中的另外的重要選擇是 Linux 內核的版本。這個選擇將得到用於各種工具鏈組件的 頭文件 headers ,但是它沒有必要一定與你在目標硬體上將要引導的 Linux 內核相同。選擇一個不比目標硬體的內核更新的 Linux 內核是很重要的。如果可能的話,盡量選擇一個比目標硬體使用的內核更老的長周期支持(LTS)的內核。

對於大多數不熟悉構建定製發行版的開發者來說,工具鏈的構建是最為複雜的過程。幸運的是,大多數硬體平台的二進位工具鏈都可以想辦法得到。如果構建一個定製的工具鏈有問題,可以在線搜索像 嵌入式 Linux Wiki 這樣的地方去查找預構建工具鏈。

引導選項

在構建完工具鏈之後,接下來的工作是引導載入程序。引導載入程序用於設置硬體,以便于越來越複雜的軟體能夠使用這些硬體。第一階段的引導載入程序通常由目標平台製造商提供,它通常被燒錄到類似於 EEPROM 或者 NOR 快閃記憶體這類的在硬體上的存儲中。第一階段的引導載入程序將使設備從這裡(比如,一個 SD 存儲卡)開始引導。樹莓派的引導載入程序就是這樣的,它樣做也就沒有必要再去創建一個定製引導載入程序。

儘管如此,許多項目還是增加了第二階段的引導載入程序,以便於去執行一個多樣化的任務。在無需使用 Linux 內核或者像 plymouth 這樣的用戶空間工具的情況下提供一個啟動動畫,就是其中一個這樣的任務。一個更常見的第二階段引導載入程序的任務是去提供基於網路的引導或者使連接到 PCI 上的磁碟可用。在那種情況下,一個第三階段的引導載入程序,比如 GRUB,可能才是讓系統運行起來所必需的。

最重要的是,引導載入程序載入 Linux 內核並使它開始運行。如果第一階段引導載入程序沒有提供一個在啟動時傳遞內核參數的機制,那麼,在第二階段的引導載入程序中就必須要提供。

有許多的開源引導載入程序可以使用。U-Boot 項目 通常用於像樹莓派這樣的 ARM 平台。CoreBoot 一般是用於像 Chromebook 這樣的 x86 平台。引導載入程序是特定於目標硬體專用的。引導載入程序的選擇總體上取決於項目的需求以及目標硬體(可以去網路上在線搜索開源引導載入程序的列表)。

現在到了 Linux 登場的時候

引導載入程序將載入 Linux 內核到內存中,然後去運行它。Linux 就像一個擴展的引導載入程序:它進行進行硬體設置以及準備載入高級軟體。內核的核心將設置和提供在應用程序和硬體之間共享使用的內存;提供任務管理器以允許多個應用程序同時運行;初始化沒有被引導載入程序配置的或者是已經配置了但是沒有完成的硬體組件;以及開啟人機交互界面。內核也許不會配置為在自身完成這些工作,但是,它可以包含一個嵌入的、輕量級的文件系統,這類文件系統大家熟知的有 initramfs 或者 initrd,它們可以獨立於內核而創建,用於去輔助設置硬體。

內核操作的另外的事情是去下載二進位塊(通常稱為固件)到硬體設備。固件是用特定格式預編譯的對象文件,用於在引導載入程序或者內核不能訪問的地方去初始化特定硬體。許多這種固件對象可以從 Linux 內核源倉庫中獲取,但是,還有很多其它的固件只能從特定的硬體供應商處獲得。例如,經常由它們自己提供固件的設備有數字電視調諧器或者 WiFi 網卡等。

固件可以從 initramfs 中載入,也或者是在內核從根文件系統中啟動 init 進程之後載入。但是,當你去創建一個定製的 Linux 發行版時,創建內核的過程常常就是獲取各種固件的過程。

輕量級核心平台

Linux 內核做的最後一件事情是嘗試去運行一個被稱為 init 進程的專用程序。這個專用程序的名字可能是 init 或者 linuxrc 或者是由載入程序傳遞給內核的名字。init 進程保存在一個能夠被內核訪問的文件系統中。在 initramfs 這種情況下,這個文件系統保存在內存中(它可能是被內核自己放置到那裡,也可能是被引導載入程序放置在那裡)。但是,對於運行更複雜的應用程序,initramfs 通常並不夠完整。因此需要另外一個文件系統,這就是眾所周知的根文件系統。

圖 3. 構建 root 配置菜單

initramfs 文件系統可以使用 Linux 內核自身構建,但是更常用的作法是,使用一個被稱為 BusyBox 的項目去創建。BusyBox 組合許多 GNU 實用程序(比如,grep 或者 awk)到一個單個的二進位文件中,以便於減小文件系統自身的大小。BusyBox 通常用於去啟動根文件系統的創建過程。

但是,BusyBox 是特意輕量化設計的。它並不打算提供目標平台所需要的所有工具,甚至提供的工具也是經過功能簡化的。BusyBox 有一個「姊妹」項目叫做 Buildroot,它可以用於去得到一個完整的根文件系統,提供了各種庫、實用程序,以及腳本語言。像 Crosstool-NG 和 Linux 內核一樣,BusyBox 和 Buildroot 也都允許使用 kconfig 菜單系統去定製配置。更重要的是,Buildroot 系統自動處理依賴關係,因此,選定的實用程序將會保證該程序所需要的軟體也會被構建並安裝到 root 文件系統。

Buildroot 可以用多種格式去生成一個根文件系統包。但是,需要重點注意的是,這個文件系統是被歸檔的。單個的實用程序和庫並不是以 Debian 或者 RPM 格式打包進去的。使用 Buildroot 將生成一個根文件系統鏡像,但是它的內容不是單獨的包。即使如此,Buildroot 還是提供了對 opkg 和 rpm 包管理器的支持的。這意味著,雖然根文件系統自身並不支持包管理,但是,安裝在根文件系統上的定製應用程序能夠進行包管理。

交叉編譯和腳本化

Buildroot 的其中一個特性是能夠生成一個臨時樹。這個目錄包含庫和實用程序,它可以被用於去交叉編譯其它應用程序。使用臨時樹和交叉編譯工具鏈,使得在主機系統上而不是目標平台上對 Buildroot 之外的其它應用程序編譯成為可能。使用 rpm 或者 opkg 包管理軟體之後,這些應用程序可以在運行時使用包管理軟體安裝在目標平台的根文件系統上。

大多數定製系統的構建都是圍繞著用腳本語言構建應用程序的想法去構建的。如果需要在目標平台上運行腳本,在 Buildroot 上有多種可用的選擇,包括 Python、PHP、Lua 以及基於 Node.js 的 JavaScript。對於需要使用 OpenSSL 加密的應用程序也提供支持。

接下來做什麼

Linux 內核和引導載入程序的編譯過程與大多數應用程序是一樣的。它們的構建系統被設計為去構建一個專用的軟體位。Crosstool-NG 和 Buildroot 是 元構建 metabuild 。元構建是將一系列有自己構建系統的軟體集合封裝為一個構建系統。可靠的元構建包括 YoctoOpenEmbedded。Buildroot 的好處是可以將更高級別的元構建進行輕鬆的封裝,以便於將定製 Linux 發行版的構建過程自動化。這樣做之後,將會打開 Buildroot 指向到項目專用的緩存倉庫的選項。使用緩存倉庫可以加速開發過程,並且可以在無需擔心上游倉庫變化的情況下提供構建快照。

一個實現高級構建系統的示例是 PiBox。PiBox 就是封裝了在本文中討論的各種工具的一個元構建。它的目的是圍繞所有工具去增加一個通用的 GNU Make 目標架構,以生成一個核心平台,這個平台可以構建或分發其它軟體。PiBox 媒體中心和 kiosk 項目是安裝在核心平台之上的應用層軟體的實現,目的是用於去產生一個構建平台。Iron Man 項目 是為了家庭自動化的目的而擴展了這種應用程序,它集成了語音管理和物聯網設備的管理。

但是,PiBox 如果沒有這些核心的軟體工具,它什麼也做不了。並且,如果不去深入了解一個完整的定製發行版的構建過程,那麼你將無法正確運行 PiBox。而且,如果沒有 PiBox 開發團隊對這個項目的長期奉獻,也就沒有 PiBox 項目,它完成了定製發行版構建中的大量任務。

via: http://www.linuxjournal.com/content/custom-embedded-linux-distributions

作者:Michael J.Hammel 譯者:qhwdw 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國