Linux-文件系統-學習筆記(15):利用busybox構建根文件系統

Linux-文件系統-學習筆記(15):利用busybox構建根文件系統

前言:一整套linux在只有內核的情況下是不能工作的,它需要由根文件系統的配置支持。同時根文件系統提供了根目錄、shell命令集和linuxrc應用程序,使得系統能夠從進程1中衍生出其他進程。所以rootfs對於操作系統來講是不可缺少的。

一、busybox移植

1、什麼是busybox?

上一篇博客中簡單介紹了busybox:
(1)busybox是一個C語言寫出來的項目,裏面包含了很多.c文件和.h文件。這個項目可以被配置編譯成各個平臺下面可以運行的應用程序。我們如果用arm-linux-gcc來編譯busybox就會得到一個可以在我們開發板linux內核上運行的應用程序。
(2)busybox這個程序開發出來就是爲了在嵌入式環境下構建rootfs使用的,也就是說他就是專門開發的init進程應用程序。
(3)busybox爲當前系統提供了一整套的shell命令程序集。譬如vi、cd、mkdir、ls等。在桌面版的linux發行版(譬如ubuntu、redhat、centOS等)中vi、cd、ls等都是一個一個的單獨的應用程序。但是在嵌入式linux中,爲了省事我們把vi、cd等所有常用的shell命令集合到一起構成了一個shell命令包,起名叫busybox。
總結:busybox 是用來製作嵌入式linux根文件系統的一個工具軟件,它裏面集成了很多基本的操作命令,在構建嵌入式Linux系統中很有用

2、busybox的移植方法

(1)從busybox官網下載它。
(2)將壓縮包解壓到你的ubuntu中。
在這裏插入圖片描述
(3)進入到busybox中,修改它的Makefile,使其對應好你的架構和交叉編譯工具鏈
在這裏插入圖片描述

ARCH = arm
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin//arm-none-linux-gnueabi-

(4)配置makeconfig中的內容。

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

後面編譯會報一個錯誤:
在這裏插入圖片描述
解決方法:在makeconfig中將sync配置爲N即可。

(5)make然後make install。
make install在所有的linux下的軟件中作用都是安裝軟件。在傳統的linux系統中安裝軟件時都是選擇源代碼方式安裝的。我們下載要安裝的軟件源代碼,然後配置、編譯、安裝。make install的目的就是將編譯生成的可執行程序及其依賴的庫文件、配置文件、頭文件安裝到當前系統中指定
我們在make install之前,最好指定一個安裝目錄:

Busybox Settings  --->  
	Installation Options ("make install" behavior)  --->(/root/porting_x210/rootfs/rootfs) BusyBox installation prefix  

在這裏插入圖片描述
(6)安裝成功。
安裝成功後切換到安裝目錄下,即可得到bin、linuxrc、sbin、usr。
在這裏插入圖片描述

二、創建根文件系統

1、修改inittab文件

(1)嘗試通過busybox生成的rootfs
上一篇博客中,我們通過自己建立了一個簡易版的rootfs,其中包含了一個空的linuxrc,最終實驗結果爲:掛載成功,執行/linuxrc失敗。
現在我們移植了busybox後,它內部包含了一個可以用的linuxrc,我們再次將將busybox生成的rootfs通過nfs去掛載,並查看結果。在啓動之前,需要注意uboot中的bootarg參數配置是否正確:

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 

實驗結果爲:掛載成功,執行/linuxrc(也就是busybox)成功,但是因爲找不到/etc/init.d/rcS和/dev/tty2等文件所以一直在打印錯誤提示信息,而且沒有進入命令行。
在這裏插入圖片描述
(2)添加inittab文件
對於沒有進入命令行的問題,最重要的原因是缺少inittab文件

inittab的工作原理就是被/linuxrc(也就是busybox)執行時所調用起作用
inittab在/etc目錄下,所以屬於一個運行時配置文件,是文本格式的(內容是由一系列的遵照一個格式組織的字符組成的),實際工作的時候busybox會(按照一定的格式)解析這個inittab文本文件,然後根據解析的內容來決定要怎麼工作。

在inittab文件中:
#開始的行是註釋。冒號在裏面是分隔符,分隔開各個部分。inittab內容是以行爲單位的,行與行之間沒有關聯,每行都是一個獨立的配置項,每一個配置項表示一個具體的含義。
每一行的配置項都是由3個冒號分隔開的4個配置值共同確定的。這四個配置值就是id:runlevels:action:process。值得注意得是有些配置值可以空缺,空缺後冒號不能空缺,所以有時候會看到連續2個冒號。
每一行的配置項中4個配置值中最重要的是action和processaction是一個條件/狀態,process是一個可被執行的程序的pathname。合起來的意思就是:當滿足action的條件時就會執行process這個程序(變成1個進程)。

通過分析busybox的源代碼就會發現,busybox最終會進入一個死循環,在這個死循環中去反覆檢查是否滿足各個action的條件,如果某個action的條件滿足就會去執行對應的processinitab在整個操作系統工作當中一直在起作用,不是檢查一次就結束了

下面是我們的inittab代碼:

::sysinit:/etc/init.d/rcS
#::askfirst:-/bin/sh
s3c2410_serial2::sysinit:/bin/login
::ctrlaltdel:-/sbin/reboot
::shutdown:/bin/umount -a -r
::restart:/sbin/init

代碼解析:
::sysinit:/etc/init.d/rcS :當系統初始化,只有系統開機或重新啓動的時候,/etc/init.d/rcS纔會被執行一次。
::askfirst:-/bin/sh:當系統初始化,直接進入命令行,不需要登錄。
s3c2410_serial2::sysinit:/bin/login:當系統初始化,只有系統開機或重新啓動的時候,/bin/login纔會被執行一次,然後登錄用戶名和密碼(與上面的方式兩者2選一)。
::ctrlaltdel:-/sbin/reboot:當按住ctrl+alt+delate時,系統執行/sbin/reboot。
::shutdown:/bin/umount -a -r:當關機時,執行/bin/umount -a -r,將所有的文件夾和文件都卸載。
::restart:/sbin/init:當重啓時,執行/sbin/init。

2、busybox源碼分析

爲了理解busybox的工作原理,我們需要對busybox的源碼進行簡單的分析。

(1)busybox的工作原理
busybox入口就是main函數,其中有很多個main但是隻有一個起作用了,其他的是沒起作用的。真正的busybox工作時的入口是libbb/appletlib.c中的main函數。
busubox中有很多xxx_main函數,這些main函數每一個都是busybox支持的一個命令的真正入口。譬如ls_main函數就是busybox當作ls函數使用時的入口程序。
在這裏插入圖片描述
進入到busybox的bin文件中,我們可以發現ls或者cd等命令其實都是busybox的一個符號鏈接。那麼busybox是如何實現一個程序化身萬千還能各自工作的?答案就是main轉xxx_main。也就是說ls或者cd等命令其實都是busybox一個程序,busybox每次執行時都是先執行其main,在main函數中識別(靠main函數的傳參argv[0]來識別)我們真正要執行的函數(譬如ls)然後去調用相應的xxx_main(譬如ls_main)來具體實現這個命令。從而通過這種傳參的方式實現一個busybox完成多種功能的效果。
在這裏插入圖片描述
(2)busybox對inittab的解析
inittab的解析是在busybox/init/init.c/init_main函數中。
執行邏輯是:先通過parse_inittab函數解析/etc/inittab(解析的重點是將inittab中的各個action和process解析出來),然後後面先直接執行sysinit和wait和once(注意這裏只執行一遍),然後在while(1)死循環中去執行respwan和askfirst
在這裏插入圖片描述
(3)busybox的體積優勢原理
busybox實際上就是把ls、cd、mkdir等很多個linux中常用的shell命令集成在一起了。集成在一起後有一個體積優勢:就是busybox程序的大小比busybox中實現的那些命令的大小加起來要小很多,因此它非常適合嵌入式這類系統。
busybox體系變小的原因主要有2個: 第一個是busybox本身提供的shell命令是閹割版的(busybox中的命令支持的參數選項比發行版中要少,譬如ls在發行版中可以有幾十個-x,但是在busybox中只保留了幾個常用的選項,不常用的都刪除掉了);第二個是busybox中因爲所有的命令的實現代碼都在一個程序中實現,而各個命令中有很多代碼函數都是通用的(譬如ls和cd、mkdir等命令都會需要去操作目錄,因此在busybox中實現目錄操作的函數就可以被這些命令共用),共用會降低重複代碼出現的次數,從而減少總的代碼量和體積。

3、添加rcS文件

/etc/init.d/rcS文件是linux的運行時配置文件中最重要的一個,其他的一些配置都是由這個文件引出來的。在inittab文件中,::sysinit:/etc/init.d/rcS實現系統啓動時對rcS文件的一次調用。這裏注意,將windows下編輯的文本導入到linux下要注意換行問題

rcS腳本內容:

PATH=/sbin:/bin:/usr/sbin:/usr/bin

runlevel=S
prevlevel=N

umask 022

export PATH runlevel prevlevel

mount -a

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

/bin/hostname -F /etc/sysconfig/HOSTNAME

ifconfig eth0 192.168.1.20

cd /root
./hello_dynamic
cd /

腳本解析—部分1:
PATH=/sbin:/bin:/usr/sbin:/usr/bin:這一行定義了一個變量PATH,值等於後面的字符串。並且後面用export導出了這個PATH,那麼PATH就變成了一個環境變量
runlevel=S:表示將系統設置爲單用戶模式
umask 022:決定當前用戶在創建文件時的默認權限,設置022時創建文件的權限爲644(相加爲666)。
mount -a:是掛載所有的應該被掛載的文件系統,在busybox中mount -a時busybox會去查找一個文件/etc/fstab文件,這個文件按照一定的格式列出來所有應該被掛載的文件系統(包括了虛擬文件系統)。

>>當寫到這裏進行掛載時,出現錯誤mount: mounting xxx on xxx failed: No such file or directory。原因是因爲根文件系統中找不到掛載點。
在這裏插入圖片描述
所謂掛載點就是我們要將目標文件系統(當然這裏都是虛擬文件系統)掛載到當前文件系統中的某一個目錄中,這個目錄就是掛載點。
這裏虛擬文件系統對應的掛載點配置在fstab文件中描述。虛擬文件系統基本上是提供與內核交互的一種通道
在這裏插入圖片描述
解決方法: 自己在製作的rootfs根目錄下創建這些掛載點目錄即可。
在這裏插入圖片描述
結果: 可以看掛載時輸出信息;還可以啓動後去看proc和sys文件夾。
在這裏插入圖片描述
腳本解析—部分2:
echo /sbin/mdev > /proc/sys/kernel/hotplug與mdev -s:udev或mdev是用來配合linux驅動工作的一個應用層的軟件,udev/mdev的工作就是配合linux驅動生成相應的/dev目錄下的設備文件。/dev目錄下的設備驅動文件就是mdev生成的,這就是mdev的效果和意義。
/bin/hostname -F /etc/sysconfig/HOSTNAME:這行代碼指定了一個主機名配置文件(這個文件一般文件名叫hostname或者HOSTNAME)。我們可以通過命令(hostname xxx)執行後可以用來設置當前系統的主機名爲xxx,直接hostname不加參數可以顯示當前系統的主機名。

4、添加profile文件

之前添加了/bin/hostname在/etc/sysconfig/HOSTNAME文件中定義了一個hostname(peter210),實際效果是:命令行下hostname命令查到的host名字確實是peter210。但是問題就是命令行的提示符是沒有顯示的。
解決方法:/etc/ 目錄下添加 profile文件。

profile文件工作原理:profile文件也是被busybox(init進程)自動調用的,所以是認名字的。
在這裏插入圖片描述
profile文件內容:

# No core files by default
ulimit -S -c 0 > /dev/null 2>&1

USER="`id -un`"
LOGNAME=$USER
PS1='[\u@\h \W]\# '
PATH=$PATH

HOSTNAME=`/bin/hostname`

export USER LOGNAME PS1 PATH

結果: 命令行提示符前面顯示:[@peter210 ]#。
在這裏插入圖片描述

5、設置登錄用戶名和密碼

/bin/login/sbin/getty在用戶名和密碼的管理上是一樣的。其實常見的所有的linux系統的用戶名和密碼的管理幾乎都是一樣的。
(1)因爲登錄在系統啓動時只需要執行一次,所以要添加到sysinit中去。
在這裏插入圖片描述
(2)添加passwd和shadow文件
在這裏插入圖片描述
linux系統中用來描述用戶名和密碼的文件是passwd和shadow文件,這兩個文件都在etc目錄下。passwd文件中存儲的是用戶的密碼設置,shadow文件中存儲的是加密後的密碼。(我們直接複製ubuntu系統中的/etc/passwd和/etc/shadow文件到當前製作的rootfs目錄下,然後再做修改即可)
在這裏插入圖片描述
Tips:平時有時候我們忘記了自己的操作系統的密碼,怎麼辦?有一種解決方法就是用其他系統(WindowsPE系統或者ubuntu的單用戶模式等···)來引導啓動,啓動後掛載到我們的硬盤上,然後找到/etc/shadow文件,去掉密文密碼後保存。然後再重啓系統後密碼就沒了。

6、添加開機啓動項

本實驗通過編寫最簡單的兩個打印helloworld的C程序,並分別進行靜態鏈接編譯和動態鏈接編譯,使其實現開機自動打印helloworld來完成測試。

Makefile文件:
在這裏插入圖片描述
通過==du -h *==來查看編譯後生成的可執行文件大小,可以發現動態鏈接文件遠小於靜態鏈接文件
在這裏插入圖片描述
由於動態鏈接文件調用了printf函數,而printf函數在動態連接時要在運行時環境(開發板的rootfs)中去尋找對應的庫文件(開發板rootfs中部署的動態鏈接庫中包含了printf函數的那個庫文件)。所以我們需要將arm-linux-gcc的動態鏈接庫文件複製到開發板rootfs的/lib目錄下
我們用的arm-2009q3這個交叉編譯工具鏈的動態鏈接庫在 /usr/local/arm/arm-2009q3/arm-none-linux-gnueabi/libc/lib 目錄下。通過複製命令:cp lib/so /root/porting_x210/rootfs/rootfs/lib/ -rdf來實現複製。
在這裏插入圖片描述
最後在rcS文件中添加cd /root 和./hello_dynamic和 cd /實現開機自啓動。注意:默認./爲前臺運行模式,如果想要讓出控制檯,就要在./hello_dynamic後面加個&( ./xxx &),即可實現後臺運行。

7、製作ext2格式鏡像並燒錄啓動

(1)設置bootargs爲nfs啓動方式,然後從主機ubuntu中做好的文件夾格式的rootfs去啓動,然後看啓動效果,作爲將來的參照物。注:調試時候爲了方便,一般都是在nfs方式下完成好之後,再製作鏡像文件燒錄
在這裏插入圖片描述
(2)動手製作ext2格式的鏡像
製作前要先通過du -h …/rootfs/ 查看我們完成的rootfs文件大小,這裏爲6.5MB,所以我們給的ext2文件大小要大於它,這裏給10MB。
在這裏插入圖片描述

#block size爲1024 數量爲10240個,則整體ext2大小爲10M
dd if=/dev/zero of=rootfs.ext2 bs=1024 count=10240
losetup  /dev/loop1 rootfs.ext2
mke2fs -m 0 /dev/loop1 10240
mount -t ext2 /dev/loop1 ./ext2_rootfs/

#然後將rootfs中的所有內容複製到我們創建的ext2_rootfs(rootfs.ext2的入口)中
cp ../rootfs/* ./ -rf
#卸載
umount /dev/loop1
losetup -d /dev/loop1

(3)將製作好的ext2文件拿去燒錄
在這裏插入圖片描述
在這裏插入圖片描述

#燒linux kernel
fastboot flash kernel peter/zImage		
#燒rootfs		
fastboot flash system peter/rootfs.ext2
#設置bootargs爲從鏡像中找
set bootargs console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext2

最後成功。
在這裏插入圖片描述

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