Linux內核學習(3) 最小系統制作2 busybox製作initrd.img和根文件系統

busybox製作initrd.img和根文件系統

參考書籍:《深度探索Linux操作系統 系統構建和原理解析》
參考博客:https://blog.csdn.net/mao0514/article/details/51248738

(一)開發環境介紹
1.使用win7_64的筆記本安裝Virtualbox虛擬機,筆記本cpu爲i5-2450m。虛擬機上安裝Ubuntu16.04系統作爲編譯環境,同時虛擬機也作爲最小系統運行環境。
2.最小系統使用Linux內核版本選擇4.15.0,內核配置使用x86_64默認配置(即使用命令make x86_64_defconfig)。busybox選擇版本1.30.0,使用busybox製作initrd.img,並且同使用busybox製作根文件系統,busybox編譯配置相同,只需要在默認配置上修改爲靜態鏈接(setting–Build static binary )。
3.在根目錄新建文件夾/vita作爲新系統啓動分區,使用gparted分配10G(大小隨便)給新分區,新分區設備文件爲/dev/sda3。將新分區掛載在/vita目錄,在此目錄下新建/boot用於存放bzImage和initrd.img。同時/vita也存放根文件系統。製作好的目錄如下:
在這裏插入圖片描述

(二)製作initrd.img
1.下載busybox包,解壓,配置,編譯,安裝等步驟之後,會在當前目錄下生產_install目錄,裏面有一些不依賴其他庫的靜態進程,可以直接運行。
在這裏插入圖片描述
我們首先將_install拷貝到initramfs目錄用於生成initrd.img

mkdir initramfs
cp -R busybox-1.30.0/_install/* initramfs/ 
cd initramfs

2.測試initrd。由於內核將initrd.img鏡像解壓到根文件系統後就將運行權交於initrd.img中的/init程序,所以我們可以自行選擇是否要加載根文件系統,可以不加載根文件系統直接使用initrd.img裏的內容做根文件系統,下面進行兩種測試,單獨運行initrd.img。
(1)測試一:使用linuxrc的內容作爲啓動腳本
① 由於我們使用initramfs,所以內核運行時是找init文件而不是linuxrc,所以直接將linuxrc改名爲init

mv linuxrc init

這裏可以稍微解釋下linuxrc的運行原理,在busybox的源碼下/init/init.c有如下內容:
在這裏插入圖片描述
表明linuxrc的作用和init一樣,也就是執行linuxrc就是執行/sbin/init。我們知道busybox使用軟鏈接將一個進程鏈接成多個進程,然後通過main函數args的第一個參數(也就是進程名)來區分具體執行哪一個,我們直接把文件名該爲init其實就相當於執行了/sbin/init程序。
② 新建文件夾,添加配置文件

mkdir dev etc lib mnt proc sys

配置文件可以選擇busybox裏面的例子,在busybox-1.30.0\examples\bootfloppy\etc目錄下,可以找到相關文件
在這裏插入圖片描述
將這些文件拷貝到initramfs/etc下,於是etc結構如下。
在這裏插入圖片描述

cp -R ../busybox-1.30.0/examples/bootfloppy/etc/* etc/

之所以要添加配置文件是因爲/init程序等同於/sbin/init,瞭解linux system V啓動方式的同學肯定知道,/sbin/bin會根據/etc/inittab的配置內容進行相關配置,如果這裏不進行配置運行內核時會首先提示找不到rcS文件,也即“can’t run ‘/etc/init.d/rcS’: No such file or directory”。
這裏稍微解釋下各個文件內容

etc/inittab

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

簡單說就是每一行表示一種執行策略,例如第一行表示在系統啓動時執行RCS,第二行表示如果sh掛了會自動重啓sh,第三行表示使用tty2啓動的sh啓動時會首先詢問,第四行表示使用ctrl+alt+del快捷鍵時執行的程序。這裏我們改下配置內容

::sysinit:/etc/init.d/rcS
console::respawn:-/bin/sh

將sh啓動的控制程序交給console,並且刪除其他操作。

etc/fstab

proc		/proc	proc	defaults    0	0

fstab表示系統啓動時默認掛載的文件系統,通常可以把一些特殊文件系統掛載下,例如/proc,/sysfs

etc/profile

# /etc/profile: system-wide .profile file for the Bourne shells

echo
echo -n "Processing /etc/profile... "

# no-op
echo "Done"
echo

profile通常是使用tty並進行登錄時纔會用到,profile是通常會調用~/.bashrc的配置修改用戶環境變量。

etc/init.d/rcS

#! /bin/sh

/bin/mount -a

rcS在這裏是作爲inittab的啓動腳本

③ 添加控制檯設備文件
內核在準備好initramfs後會判斷/dev/console是否存在,不存在就報錯,存在就會繼續執行/init並將以後的輸出輸出到控制檯中。
在這裏插入圖片描述

mknod dev/console c 5 1
mknod dev/null c 1 3

這裏我進行了刪除測試,發現即使沒有新建這兩個設備結點也能正常運行,在查閱《深度探索Linux操作系統》後,發現裏面有解答,除了我們自己創建的initrd.img外,內核會自己創建一個內置的initramfs文件系統,並在內置initramfs中執行命令創建console設備結點。所以添加設備結點這一步我們可以省略。

④ 配置grub,在/boot/grub.cfg中先新建菜單項,可以參考我的上一篇博客“Linux內核學習(3)最小系統制作”,再配置initrd的參數,表示initrd.img的位置,例如:
在這裏插入圖片描述
⑤ 生成initrd.img文件,並將此文件拷貝到/boot下,最後再重啓

find . |cpio -o -H newc|gzip ../initrd3.img
cp ../initrd3.img /vita/boot

⑥ 在grub菜單中選擇自己新建的菜單
在這裏插入圖片描述
啓動後結果如下,執行下ls,發現只有initrd.img裏面的內容,而不是根文件系統/vita下的內容

在這裏插入圖片描述
(2)測試二:自定義/init腳本
① 可以直接在上述基礎上進行,也就是修改/init的內容。

rm init
vim init

init:

#!/bin/sh
#
echo "exec initramfs init"
echo "mounting proc and sys"

mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "detect and export hardware info"
mdev -s


echo "start /bin/sh"
exec /bin/sh

這裏注意不能直接執行/bin/sh,需要先先掛載proc和sysfs,然後創建設備結點,否則會造成如下錯誤,表示init執行錯誤。mdev的作用是將sysfs文件系統下掃描的設備在/dev下創建設備結點。
在這裏插入圖片描述

② 修改init後的步驟和上述“測試一”一樣,重啓後顯示如下
在這裏插入圖片描述
結果雖然執行了sh,但是仍有提示“can’t access tty”,而且部分busybox功能失效,例如執行reboot無任何反映,應該是未執行相關初始化的原因。但是我們這裏只是測試,所以無需糾結。
3.使用initrd啓動根文件系統
(1)當initrd中充當掛載根文件系統的橋樑時,這時initramfs/etc下可以不需要配置(這裏我們依舊保留,其實沒有使用),而應當把配置文件移動到根文件系統中。
(2)修改initramfs/init啓動腳本

vim init

init:

#!/bin/sh
#
echo "exec initramfs init"
echo "mounting proc and sys"

mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "detect and export hardware info"
mdev -s

echo "Mount real rootfs to /mnt/sysroot..."
mount -t ext4 /dev/sda3 /mnt/sysroot

echo "Switch to read rootfs..."
exec switch_root /mnt/sysroot /sbin/init

這裏我們多執行了兩步操作,掛載根文件系統/dev/sda3到/mnt/sysroot目錄,執行switch_root切換根文件系統目錄到/mnt/sysroot下,並且執行/sbin/init。

(三)製作根文件系統
1.製作根文件系統和initrd.img步驟相差無幾,首先是一樣的拷貝基本文件。

cp -R ../busybox-1.30.0/_install/* /vita

2.配置文件

cp -R etc/* /vita/etc/

3.創建相關文件,刪除無用文件

cd /vita
mkdir -p dev etc lib mnt/sysroot proc sys
rm linuxrc

4.修改下fstab和rcS,使用fstab添加sysfs的掛載,使用rcS自動創建設備結點

vim etc/fstab

fstab:

proc		/proc	proc	defaults    0	0
sysfs		/sys	sysfs	defaults    0 0

修改rcS:

vim etc/init.d/rcS

rcS:

#!/bin/sh

/bin/mount -a
mdev -s

5.重啓
在這裏插入圖片描述
可以看到log都正確的打印,輸入ls後看到的目錄的確是/vita目錄,也就是正確切換到根文件系統了。

(四)常見錯誤
1.“Failed to create /dev/root:”,這種錯誤有時候會被誤解爲驅動問題,其實查看kernel源碼會發現有如下調用順序:
在這裏插入圖片描述
可以看到,如果init文件不存在(沒指定參數時默認啓動init),內核會主動掛載根文件系統,但是如果沒有進行任何先前動作就去掛載一般都會失敗,例如需要先進行掛載/proc和/sysfs。所以如果我們忘記在initrd.img中寫/init啓動腳本會出現這種錯誤。
2.“can’t run ‘/etc/init.d/rcS’: No such file or directory”,這種錯誤比較常見,但是經常不那麼容易弄懂。
在這裏插入圖片描述
我這裏說一種我出現的問題,花了比較長的時間才解決。
首先我的根文件系統明明已經寫了rcS而且也沒有任何亂碼,仔細檢查根文件系統明明沒有任何問題。這時我突然想到,如果在initrd.img/init腳本中無意間執行了initrd.img中的/sin/init程序,那麼此程序會檢查有沒有initrd.img/etc/inittab配置,如果沒有此配置文件,會默認執行initrd.img/etc/init.d/rcS腳本,所以如果initrd.img/etc下什麼文件都沒有,那麼首先報的問題就是缺少initrd.img/etc/init.d/rcS。但是後來我仔細檢查initrd.img/init內容發現並沒有執行到initrd.img/sbin/init。雖然沒有解決問題,但是我卻發現了另外一個問題,initrd.img/init文件沒有執行權限,也就是如下:
在這裏插入圖片描述
可以看到init文件的權限沒有可執行權限,但是這個問題會影響到rcS嗎,我又研究了一下kernel的源碼,發現有如下片段:

if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

ramdisk_execute_command變量在默認情況下是/init,這段代碼就是當/init執行不成功時會繼續執行/sbin/init,而這時就會執行到上述initrd.img/sbin/init中,所以就會出現上述錯誤。
明白錯誤原理後只需要修改init權限,給所有用戶添加執行:

chmod a+x init

重新重啓後就正常了,這個問題其實不難解決,難的是報的問題非常奇怪不容易弄懂原因。

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