嵌入式云IOT技術圈" data-alias="" data-signature="分享單片機、嵌入式Linux/Android、物聯(lián)網等相關產品項目開發(fā)經驗,打造最硬核嵌入式技術公眾號。" data-from="0">關注、星標嵌入式云IOT技術圈,精彩及時送達
來源 | Github-EmbeddedSystem
1.1 為什么需要根文件系統(tǒng)
- init進程的應用程序在根文件系統(tǒng)上
- 根文件系統(tǒng)提供了根目錄 /
- 內核啟動后的應用層配置( etc 目錄)在根文件系統(tǒng)上。幾乎可以認為:發(fā)行版=內核 rootfs
- shell命令程序在根文件系統(tǒng)上,比如 ls、cd 等命令
一套linux體系,只有內核本身是不能工作的,必須要 rootfs 上的 etc 目錄下的配置文件、/bin /sbin 等目錄下的 shell 命令,還有 /lib 目錄下的庫文件等···)相配合才能工作 。
1.2 根文件系統(tǒng)的實質
- 根文件系統(tǒng)是特殊用途的文件系統(tǒng)。
- 根文件系統(tǒng)也必須屬于某種文件系統(tǒng)格式。rootfstype=
存儲設備(塊設備,像硬盤、flash等)是分塊(扇區(qū))的,物理上底層去訪問存儲設備時是按照塊號(扇區(qū)號)來訪問的。文件系統(tǒng)是一些代碼,是一套軟件,這套軟件的功能就是對存儲設備的扇區(qū)進行管理,將這些扇區(qū)的訪問變成了對目錄和文件名的訪問。我們在上層按照特定的目錄和文件名去訪問一個文件時,文件系統(tǒng)會將這個目錄 文件名轉換成對扇區(qū)號的訪問。不同的文件系統(tǒng)的差異就在于對這些扇區(qū)的管理策略和方法不同,譬如壞塊管理、碎片管理。
1.3 根文件系統(tǒng)的形式
- 使用專用工具軟件制作的可供燒錄的鏡像文件
- 鏡像中包含了根文件系統(tǒng)中的所有文件
- 燒錄此鏡像類似于對相應分區(qū)格式化。
- 鏡像文件系統(tǒng)具有一定的格式,格式是內化的,跟文件名后綴是無關的。
以文件夾形式構成的文件系統(tǒng):
- 根文件系統(tǒng)其實就是一個包含特定內容的文件夾
- 根文件系統(tǒng)可由任何一個空文件夾添加必要文件構成而成
- 根文件系統(tǒng)的雛形就是在開發(fā)主機中構造的文件夾形式的
鏡像文件形式的根文件系統(tǒng)主要目的是用來燒錄到塊設備上,設備上的內核啟動后去掛載它。鏡像文件形式的根文件系統(tǒng)是由文件夾形式的根文件系統(tǒng)使用專用的鏡像制作工具制作而成的。最初在開發(fā)主機中隨便 mkdir 創(chuàng)建了一個空文件夾,然后向其中添加一些必要的文件(包括etc目錄下的運行時配置文件、/bin 等目錄下的可執(zhí)行程序、/lib目錄下的庫文件等···)后就形成了一個文件夾形式的 rootfs。然后這個文件夾形式的 rootfs 可以被 kernel 通過 nfs 方式來遠程掛載使用,但是不能用來燒錄塊設備。我們?yōu)榱藢⑦@個 rootfs 燒錄到塊設備中于是用一些專用的軟件工具將其制作成可供燒錄的一定格式的根文件系統(tǒng)鏡像。文件夾形式的 rootfs 是沒有格式的,制作成鏡像后就有了一定的 rootfs 格式了,格式是由我們的鏡像制作過程和制作工具來決定的。每一種格式的鏡像制作工具的用法都不同。
1.4 自己制作簡單的根文件系統(tǒng)
1.4.1 動手制作ext3格式的根文件系統(tǒng)
mke2fs 是一個應用程序,在 ubuntu 中默認是安裝了的。這個應用程序就是用來制作 ext2、ext3、ext4 等格式的根文件系統(tǒng)的。一般用來制作各種不同格式的 rootfs 的應用程序的名字都很相似,類似于 mkfs.xxx(譬如用來制作 ext2 格式的 rootfs 的工具叫 mkfs.ext2、用來制作 jffs2 格式的 rootfs 的工具就叫 mkfs.jffs2)。ubuntu14.04中的 mkfs.ext2 等都是 mke2fs 的符號鏈接而已。
- 創(chuàng)建rootfs.ext2文件并且將之掛載到一個目錄下方便訪問它 《參考資料:http://blog.csdn.net/zhengmeifu/article/details/24174513》
dd?if=/dev/zero?of=rootfs.ext2?bs=1024?count=2048
losetup??/dev/loop1?rootfs.ext2
mke2fs?-m?0?/dev/loop1?2048
mount?-t?ext2?/dev/loop1?./rootfs/
- 我們向鏡像中寫入一個普通文件linuxrc。這個文件就會成為我們制作的鏡像中的/linuxrc。內核掛載了這個鏡像后就會嘗試去執(zhí)行/linuxrc。然后執(zhí)行時必然會失敗。我們將來實驗看到的現(xiàn)象就應該是:掛載成功,執(zhí)行/linuxrc失敗。
- 將來真正去做有用的rootfs時,就要在這一步添加真正可以執(zhí)行的 linuxrc 程序,然后還要添加別的 /lib 目錄下的庫文件,/etc目錄下的配置文件等。
- 卸載掉,然后鏡像就做好了。
umount?/dev/loop1
losetup?-d?/dev/loop1
1.4.2 nfs 方式啟動 rootfs
nfs 是一種網絡通訊協(xié)議,由服務器和客戶端構成。利用 nfs 協(xié)議可以做出很多直接性應用,我們這里使用 nfs 主要是做 rootfs 掛載。開發(fā)板中運行 kernel 做 nfs 客戶端,主機 ubuntu 中搭建 nfs 服務器。在主機 ubuntu 的 nfs 服務器中導出我們制作的文件夾形式的 rootfs 目錄,則在客戶端中就可以去掛載這個文件夾形式的 rootfs 進而去啟動系統(tǒng)。
配置內核以支持 nfs 作為 rootfs,設置 nfs 啟動方式的 bootargs,在 menuconfig 中配置支持 nfs 啟動方式。
nfs 方式啟動相當于開發(fā)板上的內核遠程掛載到主機上的 rootfs,nfs 方式啟動不用制作 rootfs 鏡像。nfs 方式不適合真正的產品,一般作為產品開發(fā)階段調試使用。
setenv?bootargs?root=/dev/nfs?nfsroot=192.168.1.104:/home/SummerGift/work/samsung_kernel/rootfs/rootfs?ip=192.168.1.88:192.168.1.104:192.168.1.1:255.255.255.0::eth0:off??init=/linuxrc?console=ttySAC2,115200?
mount?-t?nfs?-o?nolock?192.168.1.104:/home/SummerGift/work/samsung_kernel/rootfs/rootfs??/opt
1.4.3 關于 linuxrc
- /linuxrc 是一個可執(zhí)行的應用程序 /linuxrc 是應用層的,和內核源碼一點關系都沒有,/linuxrc 在開發(fā)板當前內核系統(tǒng)下是可執(zhí)行的。因此在 ARM SoC 的 linux 系統(tǒng)下,這個應用程序就是用 arm-linux-gcc 編譯鏈接的;如果是在 PC 機 linux 系統(tǒng)下,那么這個程序就是用 gcc 編譯連接的。
/linuxrc如果是靜態(tài)編譯連接的那么直接可以運行;如果是動態(tài)編譯連接的那么我們還必須給他提供必要的庫文件才能運行。但是因為我們 /linuxrc 這個程序是由內核直接調用執(zhí)行的,因此用戶沒有機會去導出庫文件的路徑,因此實際上這個 /linuxrc 沒法動態(tài)連接,一般都是靜態(tài)連接的。
- /linuxrc執(zhí)行時引出用戶界面 操作系統(tǒng)啟動后在一系列的自己運行配置之后,最終會給用戶一個操作界面(也許是 cmdline,也許是 GUI),這個用戶操作界面就是由 /linuxrc 帶出來的。
用戶界面等很多事并不是在 /linuxrc 程序中負責的,用戶界面有自己專門的應用程序,但是用戶界面的應用程序是直接或者間接的被 /linuxrc 調用執(zhí)行的。用戶界面程序和其他的應用程序就是進程2、3、4·····,這就是我們說的進程1(init進程,也就是 /linuxrc)是其他所有應用程序進程的祖宗進程。
- /linuxrc 負責系統(tǒng)啟動后的配置 就好像一個房子建好之后不能直接住,還要裝修一樣;操作系統(tǒng)啟動起來后也不能直接用,要配置下。操作系統(tǒng)啟動后的應用層的配置(一般叫運行時配置,英文簡寫 etc)是為了讓我們的操作系統(tǒng)用起來更方便,更適合我個人的愛好或者實用性。
- /linuxrc在嵌入式linux中一般就是busybox busybox 是一個 C 語言寫出來的項目,里面包含了很多 .c 文件和 .h 文件。這個項目可以被配置編譯成各個平臺下面可以運行的應用程序。我們如果用 arm-linux-gcc 來編譯busybox就會得到一個可以在我們開發(fā)板 linux 內核上運行的應用程序。
busybox 這個程序開發(fā)出來就是為了在嵌入式環(huán)境下構建 rootfs 使用的,也就是說他就是專門開發(fā)的 init 進程應用程序。busybox 為當前系統(tǒng)提供了一整套的 shell 命令程序集。譬如 vi、cd、mkdir、ls 等。在桌面版的linux 發(fā)行版(譬如ubuntu、redhat、centOS等)中 vi、cd、ls 等都是一個一個的單獨的應用程序。但是在嵌入式 linux 中,為了省事我們把 vi、cd 等所有常用的 shell 命令集合到一起構成了一個shell 命令包,起名叫 busybox。
1.4.4 rootfs 中的其他目錄
在linux中一切皆是文件,因此一個硬件設備也被虛擬化成一個設備文件來訪問,在linux系統(tǒng)中/dev/xxx就表示一個硬件設備,我們要操作這個硬件時就是open打開這個設備文件,然后read/write/ioctl操作這個設備,最后close關閉這個設備。在最小 rootfs 中 /dev 目錄也是不可少的,這里面有一兩個設備文件是 rootfs 必須的。
在最小 rootfs 中也是不可省略的,但是這兩個只要創(chuàng)建了空文件夾即可,里面是沒東西的,也不用有東西。這兩個目錄也是和驅動有關的。屬于linux中的虛擬文件系統(tǒng)。
- usr是系統(tǒng)的用戶所有的一些文件的存放地,這個東西將來 busybox 安裝時會自動生成。
- etc 目錄中的所有文件全部都是運行時配置文件。/etc 目錄下的所有配置文件會直接或者間接的被 /linuxrc 所調用執(zhí)行,完成操作系統(tǒng)的運行時配置。etc 目錄是制作 rootfs 的關鍵,所以后面下一個課程專門講這個 etc 目錄。
- lib 目錄也是 rootfs 中很關鍵的一個,不能省略的一個。lib目錄下放的是當前操作系統(tǒng)中的動態(tài)和靜態(tài)鏈接庫文件。我們主要是為了其中的動態(tài)鏈接庫。
2. 用 busybox 制作一個簡單的 rootfs
2.1 移植 busybox 到開發(fā)板
busybox 是一個開源項目,所以源代碼可以直接從網上下載。版本差異不大,版本新舊無所謂。下載busybox可以去 linuxidc 等鏡像網站,也可以去 www.busybox.net官方網站下載。
ARCH?=?arm
CROSS_COMPILE?=?/usr/local/arm/arm-2009q3/bin//arm-none-linux-gnueabi-
Busybox?Settings--->
?Build?Options--->
??[*]Build?BusyBox?as?a?static?binary(no?shared?libs)
Busybox?Library?Tuning--->
?[*]vi-style?line?editing?commands
?[*]Fancy?shell?prompts
Linux?Module?Utilities--->
?[?]Simplified?modutils
?[*]insmod
?[*]rmmod
?[*]lsmod
?[*]modprobe
?[*]depmod
Linux?System?Utilities--->[*]mdev
?[*]Support?/etc/mdev.conf
?[*]Support?subdirs/symlinks
?[*]Support?regular?expressions?substitutions?when?renaming?dev
?[*]Support?command?execution?at?device?addition/removal
?[*]Support?loading?of?firmwares
make 編譯,如果有錯誤解決之,make install 執(zhí)行的時候其實是在執(zhí)行 busybox 頂層目錄下的一個目標 install。make install 在所有的 linux 下的軟件中作用都是安裝軟件。在傳統(tǒng)的 linux 系統(tǒng)中安裝軟件時都是選擇源代碼方式安裝的。我們下載要安裝的軟件源代碼,然后配置、編譯、安裝。make install的目的就是將編譯生成的可執(zhí)行程序及其依賴的庫文件、配置文件、頭文件安裝到當前系統(tǒng)中指定(一般都可以自己指定安裝到哪個目錄下,如果不指定一般都有個默認目錄)的目錄下。
- 設置 bootargs 掛載添加了busybox移植的rootfs
之前建立了一個空的文件夾然后自己 touch linuxrc 隨便創(chuàng)建了一個不能用的 /linuxrc 然后去 nfs 掛載 rootfs,實驗結果是:掛載成功,執(zhí)行 /linuxrc 失敗?,F(xiàn)在我們移植了 busybox 后 /linuxrc 就可以用了,然后再次去 nfs 掛載這個 rootfs。預計看到的效果是:掛載成功,執(zhí)行/linuxrc也能成功。uboot 的 bootargs 設置成:
setenv?bootargs?root=/dev/nfs?nfsroot=192.168.1.141:/root/porting_x210/rootfs?ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off??init=/linuxrc?console=ttySAC2,115200?
掛載成功,執(zhí)行 /linuxrc(也就是busybox)成功,但是因為找不到 /etc/init.d/rcS 和 /dev/tty2 等文件所以一直在打印錯誤提示信息,但是其實有進入命令行。
2.2 inittab 詳解
將一個典型的 inittab 文件復制到我們制作的 rootfs 的根目錄下的 /etc/ 目錄下,再次啟動內核掛載這個 rootfs 看效果,實驗現(xiàn)象是成功啟動并且掛載 rootfs 進入了控制臺命令行。當前制作的最小rootfs 成功了。
inittab 的工作原理就是被 /linuxrc(也就是busybox)執(zhí)行時所調用起作用。inittab 在 /etc 目錄下,所以屬于一個運行時配置文件,是文本格式的(內容是由一系列的遵照一個格式組織的字符組成的),實際工作的時候 busybox 會(按照一定的格式)解析這個 inittab 文本文件,然后根據解析的內容來決定要怎么工作。busybox 究竟如何完成解析并且解析結果如何去工作(busybox 中實現(xiàn) /etc/inittab 的原理)并不是我們的目標,我們的重點是 inittab 的格式究竟怎樣的?我們看到一個 inittab 后怎么去分析這個inittab 對啟動的影響。inittab 的格式在 busybox 中定義的。
第一個:#開始的行是注釋 第二個:冒號在里面是分隔符,分隔開各個部分。第三個:inittab內容是以行為單位的,行與行之間沒有關聯(lián),每行都是一個獨立的配置項,每一個配置項表示一個具體的含義。第四個:每一行的配置項都是由3個冒號分隔開的4個配置值共同確定的。這四個配置值就是id:runlevels:action:process。值得注意的
是有些配置值可以空缺,空缺后冒號不能空缺,所以有時候會看到連續(xù)2個冒號。第五個:每一行的配置項中4個配置值中最重要的是 action 和 process,action 是一個條件/狀態(tài),process 是一個可被執(zhí)行的程序的 pathname。合起來的意思就是:當滿足 action 的條件時就會執(zhí)行process 這個程序。理解 inittab 的關鍵就是明白“當滿足 action 的條件時就會執(zhí)行 process 這個程序?!?分析 busybox的源代碼就會發(fā)現(xiàn),busybox 最終會進入一個死循環(huán),在這個死循環(huán)中去反復檢查是否滿足各個action 的條件,如果某個 action 的條件滿足就會去執(zhí)行對應的 process。
3. busybox 源碼分析
3.1 分析 busybox 啟動狀況
分析一個程序,不管多龐大還是小,最好的路線都是按照程序運行時的邏輯順序來。所以找到一個程序的入口至關重要。主函數main函數就是整個程序的入口,這種情況適應于操作系統(tǒng)下工作的應用程序的情況。在 uboot 和 linux kernel 這兩個大的 C 語言的項目中,main函數都沒有,都不是入口。在我們這種裸機程序中入口不是 main 函數,而是由連接腳本來指定的。busybox 是 linux 啟動起來后工作的一個應用程序,因此其中必然有 main 函數,而且 main 就是入口。
3.2 busybox 中 main 函數全解析
busybox 入口就是 main 函數,其中有很多個 main 但是只有一個起作用了,其他的是沒起作用的。真正的busybox工作時的入口是 libbb/appletlib.c 中的 main 函數。busybox中有很多 xxx_main 函數,這些 main 函數每一個都是 busybox 支持的一個命令的真正入口。例如 ls_main 函數就是 busybox 當作 ls 函數使用時的入口程序。ls 或者 cd 等命令其實都是 busybox 一個程序,但是實際執(zhí)行時的效果卻是各自的效果。busybox 每次執(zhí)行時都是先執(zhí)行其 main,在 main 函數中識別(靠 main 函數的傳參 argv[0] 來識別)我們真正要執(zhí)行的函數(譬如ls)然后去調用相應的xxx_main(譬如ls_main)來具體實現(xiàn)這個命令。
3.3 inittab 解析與執(zhí)行
inittab 的解析是在 busybox/init/init.c/init_main 函數中。執(zhí)行邏輯是:先通過 parse_inittab 函數解析 /etc/inittab(解析的重點是將 inittab 中的各個 action 和 process 解析出來),然后后面先直接執(zhí)行 sysinit 和 wait 和 once(注意這里只執(zhí)行一遍),然后在 while(1) 死循環(huán)中去執(zhí)行 respwan 和 askfirst。
3.4 busybox 的體積優(yōu)勢原理
busybox 實際上就是把 ls、cd、mkdir 等很多個 linux 中常用的 shell 命令集成在一起了。集成在一起后有一個體積優(yōu)勢:就是 busybox 程序的大小比 busybox 中實現(xiàn)的那些命令的大小加起來要小很多。busybox 體積變小的原因主要有2個:
- 第一個是 busybox 本身提供的 shell 命令是閹割版的(busybox 中的命令支持的參數選項比發(fā)行版中要少,譬如ls在發(fā)行版中可以有幾十個 -x,但是在 busybox 中只保留了幾個常用的選項,不常用的都刪除掉了)
- 第二個是 busybox 中因為所有的命令的實現(xiàn)代碼都在一個程序中實現(xiàn),而各個命令中有很多代碼函數都是通用的(譬如 ls 和 cd、mkdir 等命令都會需要去操作目錄,因此在 busybox 中實現(xiàn)目錄操作的函數就可以被這些命令共用),共用會降低重復代碼出現(xiàn)的次數,從而減少總的代碼量和體積。
busybox 的體積優(yōu)勢是嵌入式系統(tǒng)本身的要求和特點造成的。
4. rcS 文件分析與實戰(zhàn)
4.1 rcS 文件介紹
/etc/init.d/rcS 文件是linux的運行時配置文件中最重要的一個,其他的一些配置都是由這個文件引出來的。這個文件可以很復雜也可以很簡單,里面可以有很多的配置項。
首先從shell腳本的語法角度分析,這一行定義了一個變量PATH,值等于后面的字符串,后面用export導出了這個PATH,那么PATH就變成了一個環(huán)境變量。PATH 這個環(huán)境變量是 linux 系統(tǒng)內部定義的一個環(huán)境變量,含義是操作系統(tǒng)去執(zhí)行程序時會默認到PATH 指定的各個目錄下去尋找。如果找不到就認定這個程序不存在,如果找到了就去執(zhí)行它。將一個可執(zhí)行程序的目錄導出到 PATH,可以讓我們不帶路徑來執(zhí)行這個程序。
因為我們希望一旦進入命令行下時,PATH 環(huán)境變量中就有默認的 /bin /sbin /usr/bin /usr/sbin 這幾個常見的可執(zhí)行程序的路徑,這樣我們進入命令行后就可以 ls、cd 等直接使用了。為什么rcS 文件還沒添加,系統(tǒng)啟動就有了 PATH 中的值?原因在于 busybox 自己用代碼硬編碼為我們導出了一些環(huán)境變量,其中就有 PATH。
runlevel也是一個shell變量,并且被導出為環(huán)境變量,runlevel=S表示將系統(tǒng)設置為單用戶模式。
umask 是 linux 的一個命令,作用是設置 linux 系統(tǒng)的 umask 值。umask值決定當前用戶在創(chuàng)建文件時的默認權限。
mount -a 是掛載所有的應該被掛載的文件系統(tǒng),在 busybox中 mount -a 時 busybox 會去查找一個文件 /etc/fstab 文件,這個文件按照一定的格式列出來所有應該被掛載的文件系統(tǒng)(包括了虛擬文件系統(tǒng))
mdev 是 udev 的
嵌入式簡化版本,udev/mdev 是用來配合 linux 驅動工作的一個應用層的軟件,udev/mdev 的工作就是配合 linux 驅動生成相應的 /dev 目錄下的設備文件。在 rcS 文件中沒有啟動 mdev 的時候,/dev 目錄下啟動后是空的。在 rcS 文件中添加上 mdev 有關的 2 行配置項后,再次啟動系統(tǒng)后發(fā)現(xiàn) /dev 目錄下生成了很多的設備驅動文件。(4)/dev目錄下的設備驅動文件就是mdev生成的,這就是mdev的效果和意義。
hostname 是 linux 中的一個 shell 命令。命令(hostname xxx)執(zhí)行后可以用來設置當前系統(tǒng)的主機名為 xxx,直接 hostname 不加參數可以顯示當前系統(tǒng)的主機名。/bin/hostname -F /etc/sysconfig/HOSTNAME -F 來指定了一個主機名配置文件(這個文件一般文件名叫 hostname 或者 HOSTNAME)
有時候我們希望開機后進入命令行時ip地址就是一個指定的 ip 地址(譬如192.168.1.30),這時候就可以在 rcS 文件中 ifconfig eth0 192.168.1.88
4.2 rcS 文件測試問題記錄