Linux嵌入式應用介紹﹕建立一個嵌入式Linux環境

Posted by Lawpig on 8月 05, 2016 | No comments

Linux嵌入式應用介紹﹕建立一個嵌入式Linux環境

Linux嵌入式應用介紹﹕
建立一個嵌入式Linux環境
HungLin Chou.
一, 前言
嵌入式Linux應用技術,這一陣子以來一直是產業界相當熱門的話題之一,我們都知道現在的Linux核心已經可以在多種不同的硬體平台上面運作,而且憑藉著來自各地的好手,我們逐步的讓Linux的技術可以有機會應用在不同的環境中。舉凡在即時系統的應用、以及PDA或是目前流行的機頂盒﹝Set-Top Box﹞,每個不同的應用所牽涉到的硬體與處理器種類都是在解決系統問題時,相當關鍵的一環。
因為硬體的選擇,攸關到將來產品量產時的成本以及在市場上的競爭力,所以說對於嵌入式系統的開發工作者來說,選擇一個具備系統彈性以及在將來擁有廣泛硬體支援的作業系統,將是一個不可或缺的要素。
Linux由於它先天開放與自由的特性,在世界各地眾多好手的加入之下,選擇它做為我們開發的平台,我們可以順利的把環境移植到目前許多當紅和具備競爭力的硬體與處理器上。不可諱言的,Linux有一項最大的優勢就是它具備了豐富的網路協定與驅動程式資源,舉凡Ipv6Mobile IP以及當紅的藍芽﹝Bluetooth﹞,都可以在Linux上找到它們的蹤跡。因為這樣的特點,採用Linux做為嵌入式應用的廠商,可以透過整合現存的程式資源來完成目的,並且可以把主要的心思花費在如何讓產品更具備競爭力與提供更佳的服務。
目前從事Linux研發的公司相當多,不論是檯面上打著Embedded Linux旗號的公司或是在其它領域裡把Linux包含在產品當中,其實或多或少都會用到嵌入式Linux的技巧。雖然,現在有不少這方面的嵌入式Linux快速工具,可是這些工具能做到的功能其實還是有限,許多時候我們需要的是針對特定的硬體與功能來把系統完整的整合。要解決這樣的問題,就不是一般的嵌入式Linux快速開發工具所可以勝任的工作了,這是需要從事開發的人員對於嵌入式的環境有所了解,進而可以真正的利用系統化的角度來整合產品的要求。
許多的Linux公司其實角色有點類似Linux Design House,為何這樣說呢因為這類對於Linux具有專業能力的廠商,可以替周邊有意進入這領域卻沒有Linux研發能力的公司提供專業的技術支援,例如協助它們開發嵌入式Linux的產品或是支援硬體廠商開發Linux上的驅動程式。我想這應該是一個不錯的商業模式,因為Linux產業的興起,吸引許多廠商也樂於投入這樣的領域中,並且這樣會吸引更多有能力的人加入Linux的研發,從而帶動整個產業對於Linux應用的了解與發揮它的多樣性。
二, 介紹LinuxHAL
在正式進入我們的主題前,我打算先跟各位介紹Linux之所以可以在許多平台上移植的要素 ” Hardware AbstractionLayer r” ,因為有了硬體虛擬層的概念,所以作業系統可以把與硬體平台相依的部分切割出來,讓作業系統在日後更換硬體平台時,可以耗用最少的資源成本。
Linux之所以具備了移植到不同硬體平台的能力,就是在於它提供了一個硬體抽象層的概念﹝Hardware AbstractionLayer﹞,透過這樣的架構我們可以把與硬體有關的部分和與硬體無關的部分做一個切割,如下圖
當我們取得Linux核心的原始碼後,我們會發現有一個arch目錄,這個目錄所包含的內容就是Linux與硬體平台有關的程式碼,目前這目錄的內容如下
[root@hlchou arch]# pwd
/hal/linux/arch
[root@hlchou arch]# ls
alpha/ cris/ ia64/ mips/ parisc/ s390/ sh/ sparc64/
arm/ i386/ m68k/ mips64/ ppc/ s390x/ sparc/
[root@hlchou arch]#
每個目錄的名稱則是依據所屬的處理器類型,例如﹕屬於x86系列處理器的程式碼就至於 i386目錄中。
編譯過Linux核心程式碼的使用者應該都知道,Linux編譯到了最後的階段會把各核心所編譯完成的模組連結成一個vmlinux。因此,如下表所示,這是在Strong ARM1110X86平台上在連結成vmlinux時,所會一併連結而成的目的檔。
處理器
檔案名稱
i386
arch/i386/kernel/head.o
arch/i386/kernel/init_task.o
arch/i386/kernel/kernel.o
arch/i386/mm/mm.o
arch/i386/lib/lib.a
lib/lib.a
init/main.o
init/version.o
kernel/kernel.o
mm/mm.o
fs/fs.o
ipc/ipc.o
drivers/block/block.o
drivers/char/char.o
drivers/misc/misc.o
drivers/net/net.o
drivers/media/media.o
drivers/char/agp/agp.o
drivers/char/drm/drm.o
drivers/ide/idedriver.o
drivers/cdrom/driver.o
drivers/pci/driver.o
drivers/pcmcia/pcmcia.o
drivers/net/pcmcia/pcmcia_net.o
drivers/pnp/pnp.o
drivers/video/video.o
net/network.o

ARM

arch/arm/kernel/head-armv.o
arch/arm/kernel/init_task.o
arch/arm/kernel/kernel.o
arch/arm/mm/mm.o
arch/arm/nwfpe/math-emu.o
arch/arm/lib/lib.a
lib/lib.a
init/main.o
init/version.o
kernel/kernel.o
mm/mm.o
fs/fs.o
ipc/ipc.o
drivers/block/block.o
drivers/char/char.o
drivers/misc/misc.o
drivers/net/net.o
drivers/media/media.o
drivers/ide/idedriver.o
drivers/sound/sounddrivers.o
drivers/pcmcia/pcmcia.o
drivers/net/pcmcia/pcmcia_net.o
drivers/video/video.o
drivers/usb/usbdrv.o
net/network.o
之所以把i386Strong ARM放在表中做比較,是因為透過這樣的方式,我們可以發現在不同平台上面Linux核心在連結時所會引入的目的檔。而在arch目錄中我們可以發現i386Strong ARM都會引入相同功能的目的檔,所以如果我們想要查看與硬體有關的部分提供了哪些函式,就可以透過與這些目的檔有關的程式碼來得知。
由表中,我們可以看到i386vmlinux分別連結了arch/i386/kernel/head.oarch/i386/kernel/init_task.oarch/i386/kernel/kernel.oarch/i386/mm/mm.oarch/i386/lib/lib.a,而Strong ARM 1110vmlinux連結了arch/arm/kernel/head-armv.oarch/arm/kernel/init_task.oarch/arm/kernel/kernel.oarch/arm/mm/mm.oarch/arm/nwfpe/math-emu.oarch/arm/lib/lib.a。這些在arch目錄中的程式碼,都是與硬體有關聯的程式碼,也就是說在Linux的環境中必須要把arch目錄下的函式與和硬體有關的功能實作完成,透過這些與硬體有關的基本函式,在上層運作的其它模組,便可以在最小修改的情況下,適用於這樣的架構。如下圖所示


因為有了hal層與Linux上其它核心模組的運作,所以在Linux環境下運作的使用者程式,可以在不需要修改的情況下,便可以透過跨平台的編譯器來重新編譯,以使得它可以在不同的平台順利運作。
其實,同樣的概念在Windows 2000上面也一樣存在,例如﹕Windows2000有實作一個hal.dll/winnt/systen32/hal.dll﹞,這個檔案就是提供一個與下層硬體無關的介面給上層的驅動程式與Windows 2000核心呼叫,讓上層呼叫hal層的核心,可以無須考慮硬體的因素,與硬體平台有關的部分可透過hal.dll來負責處理。
如下圖,就是Windows 2000hal層提供給核心呼叫的函式示意圖


其實,我們不難發現這樣的概念對於作業系統的實作來說,著時帶來不少好處,因為上下層的關係有所區隔所以可以讓移植作業系統的人員,可以把重心放在與硬體有關的層次上,致力於把硬體相關的函式實作完整,如此便可以確保上層的系統呼叫也一併可以順利運作。
三, 函式庫環境
在嵌入式的應用上,執行環境的空間大小除了攸關系統資源的耗用外,還關係到產品在商品化時,成本的考量。而Linux使用者環境所包含的動態函式庫檔案,即是一個佔用不少執行環境空間的元件。
其實不論是否有真正的對函式庫做過縮小化的動作,我們必須體認到的一點就是,移除了動態函式庫中部分的功能或是模組,雖然可以減少執行空間的耗用,不過這也代表了該嵌入式執行環境下,將會缺少了可以執行該項函式或是模組的能力。
舉個例子來說,如果我們把Glibc套件當中的libc.so.6裡部分的函式與模組刪除,我們或許可以得到一個500Kbytes大小的動態函式庫,不過相對的這就代表了我們將無法提供一個基本的libc環境給其它的程式,例如﹕目前的PDA或是Set-Top Box將無法任意讓使用者傳入一個使用libc的執行檔,因為這樣精簡型的環境中,主要提供的函式與模組都是針對這環境所預設會執行的執行檔所需要的函式,外來的執行檔如果使用了現在不包含在libc當中的函式時,會在程式啟動時發生錯誤。
如果讀者對於Linux動態函式庫環境有興趣的話,還可以參考筆者的其它作品 http://www.linuxfab.cx/indexColumnData.php?CID=97&FIRSTHIT=1
四, 打造嵌入式的Linux環境
這篇文章主要的目的之一就是要讓各位可以親手打造一個嵌入式的Linux環境,說真的這一點也不難,相信各位在看過我舉的例子之後,應該也會有一樣的感覺。不過說在前頭的就是,這是最基本的概念,當然囉如果使用者希望應用在不同的硬體平台或是特定的周邊裝置上,那就需要具備建立Cross-Compiler與對於周邊裝置的基本概念。以便於可以把Linux嵌入式的概念移植到不同的環境下,不過這可還需要進行一些額外的步驟,需要讀者自行付出努力,親手去產生一個跨平台的編譯器,進而建構一個可以在其它平台運作的Linux環境。如下圖所示


筆者在此提供一個簡易的Script檔用來包裝一個使用者執行環境,讀者可以自行把一個使用者執行環境壓縮成一個可以透過RAMDISK載入執行的使用者執行環境壓縮檔。使用的方式如下﹕
首先可以看到Script MakeImage與目錄base,而檔案linux為筆者所提供的Linux核心,會在開機時啟動RAMDISK來載入製作完成的使用者執行環境,而syslinux.cfg 則是給Syslinux啟動時使用的設定檔
﹝讀者可以到http://www.ibiblio.org/pub/Linux/system/boot/loaders/下載Syslinux]
[root@hlchou sample]# ls
MakeImage* base/ linux* syslinux.cfg*
[root@hlchou sample]#
其中目錄base就是我們所要建構的使用者執行環境,內容如下
[root@hlchou sample]# cd base
[root@hlchou base]# du
4.0k ./lost+found
92k ./bin
4.0k ./dev
12k ./etc/rc.d
72k ./etc
1.2M ./lib
4.0k ./proc
360k ./sbin
4.0k ./tmp
4.0k ./usr
4.0k ./var
1.8M .
[root@hlchou base]#
讀者如果想要在嵌入式環境中加入檔案的話,只要直接拷貝到base目錄下的對應目錄即可,並且透過MakeImage來壓縮成使用者執行環境的檔案,如下
[root@hlchou sample]# ls
MakeImage base linux syslinux.cfg
[root@hlchou sample]# ./MakeImage
4096+0 records in
4096+0 records out
8192+0 records in
8192+0 records out
[root@hlchou sample]# ls
Image.gz MakeImage base linux syslinux.cfg
[root@hlchou sample]#
其中的 Image.gz 就是透過MakeImage所產生的壓縮檔,它是透過把base目錄下的檔案壓縮而成。
如下圖所示,這裡面包含了動態函式庫所存在的目錄﹝”/lib”﹞,以及系統設定檔目錄﹝”/etc”….…等。如果使用者希望可以精簡化整個嵌入式環境的大小,其實就可以從這個使用環境的架構下來著手,如果要加入新的伺服器或是指令,讀者只要把伺服器的程式碼針對所要執行的評台進行編譯後,再移入這個環境即可



在把程式移入到使用者執行的環境時,要注意的一點就是可以透過指令ldd來確認使用者程式使用到了哪些動態函式庫,進而把這些動態函式庫載入到我們的環境中,以確保程式在我們所配置的使用者環境中可以正確執行無誤
[root@hlchou sbin]# ldd init
libc.so.6 => /lib/libc.so.6 (0x4001c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[root@hlchou sbin]# ldd route
libc.so.6 => /lib/libc.so.6 (0x4001c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[root@hlchou sbin]#
其實,筆者滿建議各位可以透過Busybox來建構使用者環境的,因為BusyBox提供了一組基本的Linux指令。傳統上,我們在Linux使用者環境中所執行的指令都是許多個別的執行檔,不過這會產生一個問題就是這些指令所存在的執行檔,其實有許多的功能是我們所不常使用或是有部分的程式碼是可以重複利用的。而BusyBox這個免費的工具程式,就是為了解決這個問題所產生了,它把許多的Linux指令整合在同一個執行檔當中,並且針對這些執行檔提供充分的功能,讓使用者幾乎可以只利用BusyBox便完成一個實用的使用者環境。
如下圖所示,這是BusyBox的示意圖



透過這樣把指令整合的方式,我們可以達成縮減Linux使用者執行環境指令執行檔所需的空間。
當然在根目錄當中會包含了一個 /linuxrc,其中/linuxrcSymbol Link”/sbin/init”,也就是說當Linux核心啟動後第一個會執行的使用者程式就是/linuxrc。透過這個程式,我門可以讓它去讀取系統的設定檔進而對於我們所預設的使用者執行環境進行初始化,例如我們可能需要載入特定的伺服器與應用程式,就可以在這些過程中加入,並且讓系統在初始過程中可以自動的啟動
如下圖所示,這是我們Linux環境啟動的流程



由圖中我們可以看到在Linux的核心原始碼中﹝init/main.c﹞, 就有這樣一段程式碼,會在核心初始化告一段落後,去執行程式 ”/linuxrc”
如果讀者想要自行編譯核心來使用筆者所提供的範例,請記得要在核心設定時把以下的選項加入
RAM disk support (CONFIG_BLK_DEV_RAM) [Y/m/n/?] y
Default RAM disk size (CONFIG_BLK_DEV_RAM_SIZE) [4096]
Initial RAM disk (initrd) support (CONFIG_BLK_DEV_INITRD) [Y/n/?] y
重新編譯過的核心,請把它的檔名改成linux,並且連同Image.gz﹝使用者執行環境的壓縮檔﹞以及syslinux.cfg 拷貝到syslinux所安裝的磁區即可。以便於可以在核心啟動的過程中,啟動RAMDISK來載入我們的測試環境。
讀者可以到以下網址下載測試的範例檔案﹕
http//xxx.xxx.xxx.xxx
五, 結語
感謝這次編輯raymond的邀稿,讓筆者有機會可以針對嵌入式的環境做一個介紹,並且帶領各位親手做出一個嵌入式的環境,透過這樣的介紹希望可以讓更多人對於嵌入式Linux的環境能夠有一個入門的機會,各位一定會發現其實整個過程並不困難,這是因為Linux上面有許多前人已經把與硬體有關的驅動程式還有核心架構給移植完畢,所以我們現在才會有這樣一個容易使用的Linux核心。
筆者目前所服務的單位主要從事於第三代手機無線通訊系統的研發,其中RT-Linux就是我們目前所考慮的平台,我一直都相信Linux的應用將是無遠弗屆的,並不是打著Linux旗號的公司才是真正懂得應用Linux的公司。其實筆者所接觸過的許多人與團隊,雖然並沒有以Linux做為號召,不過其實產品的核心就是透過Linux來組合而成的。
我相信,Linux是產品在商品化的選擇之一,在思考產品的定位與市場時,不見得要從Linux的角度來思考,因為這樣會嚴重的侷限了我們思考的角度與窄化了我們的想像力。我們應該去思考這世界目前還需要的事物與技術,首先有了產品的點子與雛形再進一步的去思考所要達成這個目的要具備的能力以及技術條件。其中,Linux便是我們可以採用的作業系統之一,我們該選擇最適合達成目的的作業系統,例如可能在許多的即時系統應用上VxWork就比Linux好上許多。
希望台灣的軟體工業可以真正的發展起來,不論到時Linux的進展如何。


0 意見:

張貼留言