想讓Linux內(nèi)核代碼跑起來,得先搭建編譯和運(yùn)行代碼的環(huán)境。
Linux代碼盡量在Linux環(huán)境下編譯,以減少不必要的麻煩,我選擇的是ubuntu-18.04:
1、linux源碼下載
我們依舊使用5.4版本的linux,其下載鏈接:https://codeload.github.com/torvalds/linux/tar.gz/refs/tags/v5.4
并將源碼放置如下文件,為了方便后續(xù)管理,我在gitee上創(chuàng)建了一個(gè)倉(cāng)庫(kù),為自己后續(xù)閱讀源碼添加注釋做準(zhǔn)備。
/home/damon/00_code/02_gitee/linux_5.4/linux-5.4
2、編譯內(nèi)核源碼
2.1 選擇編譯工具
ubuntu本身自帶gcc編譯器,不過是針對(duì)X86平臺(tái)的,我們現(xiàn)在要編譯ARM架構(gòu)的代碼,也就是在X8平臺(tái)上編譯ARM架構(gòu)的工具,叫做ARM GCC交叉編譯器。
我將其解壓放置在以下目錄:
/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf
2.2 編譯配置
基于ARM架構(gòu)的配置,我們選擇*./arch/arm/configs/vexpress_defconfig*配置。
直接上編譯腳本:
注意事項(xiàng):如果你的環(huán)境是第一次配置,第一次編譯的時(shí)候大概率會(huì)出錯(cuò),按照錯(cuò)誤提示按照依賴工具即可繼續(xù)編譯。
# 根據(jù)個(gè)人存放的Linux源碼目錄,修改成自己的目錄路徑
cd /home/damon/00_code/02_gitee/linux_5.4/linux-5.4
# 清理工程
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- distclean
# 配置文件選擇:./arch/arm/configs/vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- vexpress_defconfig
# 打開圖形配置界面,我們選擇默認(rèn)配置
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig
2.3 編譯內(nèi)核
編譯內(nèi)核時(shí),執(zhí)行如下命令:
# -j8,可以根據(jù)實(shí)際核數(shù)修改,可以提速編譯
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- -j8
編譯成功后,我們可以得到內(nèi)核鏡像文件和設(shè)備樹文件。內(nèi)核鏡像文件就是我們要跑代碼的執(zhí)行文件,設(shè)備樹文件就是內(nèi)核啟動(dòng)后加載它并生成設(shè)備節(jié)點(diǎn)的文件。
得到的內(nèi)核鏡像文件叫 zImage ,放在如下路徑:
/home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot
同時(shí),對(duì)應(yīng)vexpress_defconfig配置下生成的設(shè)備樹文件:vexpress-v2p-ca9.dtb
內(nèi)核編譯好了,如何將它啟動(dòng)呢?
3、如何啟動(dòng)內(nèi)核
一般地,學(xué)習(xí)嵌入式linux,手頭上要有一塊開發(fā)板,一個(gè)支持linux開發(fā)的開發(fā)板少則幾百塊,貴則上千塊。
如果你只是想玩一玩,看一看,完全沒必要花這些錢,那么沒有開發(fā)板該如何啟動(dòng)Linux內(nèi)核呢?
我們可以采用 qemu搭建運(yùn)行環(huán)境 。
qemu是“Quick Emulation”的縮寫,是一個(gè)用C語言編寫的開源虛擬化軟件。它可以模擬硬件,在指定的硬件平臺(tái)上搭建運(yùn)行開發(fā)環(huán)境。
我們暫時(shí)無需知道其工作原理,直接拿來安裝使用。
3.1 qemu安裝
安裝qemu工具
sudo apt-get install qemu
測(cè)試qemu是否安裝成功:
qemu起kernel
現(xiàn)在我們有了kernel鏡像文件和設(shè)備樹文件,我們按照qemu的指令啟動(dòng):
qemu-system-arm -M vexpress-a9 -m 512M -kernel /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/zImage -dtb /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
啟動(dòng)日志:
省略......
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.4.0+ #1
Hardware name: ARM-Versatile Express
[< 80110e90 >] (unwind_backtrace) from [< 8010c4a4 >] (show_stack+0x10/0x14)
[< 8010c4a4 >] (show_stack) from [< 80764c24 >] (dump_stack+0x90/0xa4)
[< 80764c24 >] (dump_stack) from [< 8012147c >] (panic+0x110/0x310)
[< 8012147c >] (panic) from [< 80a01584 >] (mount_block_root+0x204/0x2b4)
[< 80a01584 >] (mount_block_root) from [< 80a01758 >] (mount_root+0x124/0x148)
[< 80a01758 >] (mount_root) from [< 80a018d0 >] (prepare_namespace+0x154/0x198)
[< 80a018d0 >] (prepare_namespace) from [< 8077bd44 >] (kernel_init+0x8/0x110)
[< 8077bd44 >] (kernel_init) from [< 801010e8 >] (ret_from_fork+0x14/0x2c)
Exception stack(0x9e493fb0 to 0x9e493ff8)
3fa0: 00000000 00000000 00000000 00000000
3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
3fe0: 00000000 00000000 00000000 00000000 00000013 00000000
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
看最后一行:?jiǎn)?dòng)到一半出錯(cuò)了,Kernel Painc?。。?/p>
看到Panic,"慌不慌" ?不過kernel會(huì)將Panic的原因也打印出來:Unable to mount root fs
不能掛在到rootfs上,什么是rootfs呢?沒有它,我們?cè)撊绾巫鲆粋€(gè)呢?
4、 什么是rootfs?
root fs的中文名叫根文件系統(tǒng)。
文件我們好理解,比如我們常見的office文件、圖片、視頻、壓縮包等等都叫文件。
文件系統(tǒng)可以理解為能解析識(shí)別這些文件的文件。
昨天我老婆下載了一個(gè)壓縮包,解壓出來發(fā)現(xiàn)一個(gè)jar文件windows無法識(shí)別,這時(shí)候我們就要安裝JAVA JDK,我們可以粗略地將JAVA JDK叫做能解析jar文件的文件系統(tǒng)(這個(gè)比喻不太恰當(dāng))。
那什么是根呢?就是Linux內(nèi)核啟動(dòng)所掛載的第一個(gè)文件系統(tǒng),所以這個(gè)根字也體現(xiàn)了它的重要性!
一句話總結(jié):rootfs是Linux內(nèi)核啟動(dòng)掛載的第一個(gè)文件系統(tǒng),系統(tǒng)引導(dǎo)啟動(dòng)程序會(huì)在根文件系統(tǒng)掛載之后,從中把一些基本的初始化腳本和服務(wù)等加載到內(nèi)存中去運(yùn)行。
4.1 如何制作一個(gè)rootfs
制作rootfs我們要借用一個(gè)工具:BusyBox,忙碌盒子,這個(gè)盒子會(huì)提供大量Linux命令和工具的軟件。
BusyBox的官網(wǎng)地址為: https://busybox.net/ ,最新的版本已經(jīng)達(dá)到1.36.0。
我們不追求最新,因?yàn)樽钚碌耐鶗?huì)存在一些兼容性或未知BUG,使用我之前用過的1.32.0版本,下載鏈接:https://busybox.net/downloads/busybox-1.32.0.tar.bz2
解壓、配置、編譯BusyBox和編譯Linux的流程差不多,此處就直接貼腳本了:
cd /home/damon/00_code/02_gitee/busy_box
tar -vxjf busybox-1.32.0.tar.bz2
/home/damon/00_code/02_gitee/busy_box/busybox-1.32.0
# 手動(dòng)修改CROSS_COMPILE和ARCH
make defconfig
make menuconfig
配置完后編譯
make
# 編譯好的東西,我把它安裝到 /home/damon/00_code/02_gitee/busy_box/rootfs目錄下
make install CONFIG_PREFIX=/home/damon/00_code/02_gitee/busy_box/rootfs
安裝完畢,文件目錄顯示如下:
可以看到rootfs中有3個(gè)文件夾和一個(gè)鏈接文件,具體作用先不展開,此時(shí)busybox的工作就完成了,但是此時(shí)的rootfs還不能使用,還缺少一些東西,接下來繼續(xù)補(bǔ)充。
cd /home/damon/00_code/02_gitee/busy_box/rootfs
# 創(chuàng)建lib目錄,并添加庫(kù)文件(這些庫(kù)文件都是從GCC中獲取的)
mkdir lib
cd /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib
cp *so* /home/damon/00_code/02_gitee/busy_box/rootfs/lib/ -d
cd /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/lib
cp *so* *.a /home/damon/00_code/02_gitee/busy_box/rootfs/lib/ -d
# 向usr/lib目錄添加庫(kù)文件
cd /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib
cp *so* *.a /home/damon/00_code/02_gitee/busy_box/rootfs/usr/lib/ -d
# 創(chuàng)建其他文件目錄
cd /home/damon/00_code/02_gitee/busy_box/rootfs
mkdir dev proc mnt sys tmp etc root
此時(shí)一個(gè)可以使用的rootfs便制作完成了,rootfs目錄如下圖所示:
4.2 rootfs如何使用呢?
rootfs一般放到SD卡或者磁盤中,讓內(nèi)核去讀取啟動(dòng)。我手頭上沒有SD卡,在PC上劃出一塊磁盤又比較浪費(fèi)。我們就制作一個(gè)SD卡鏡像,它是為qemu創(chuàng)建的虛擬SD卡。執(zhí)行腳本如下:
cd /home/damon/00_code/02_gitee
# 制作rootfs.ext4.img文件
dd if=/dev/zero of=rootfs.ext4.img bs=1M count=500
# 圖片截取的是32M,后面實(shí)際應(yīng)用時(shí),發(fā)現(xiàn)32M不夠用,就修改成了500M
# 格式化
mkfs.ext4 rootfs.ext4.img
# 將rootfs.ext4.img掛載到/mnt/rootfs
mkdir -p /mnt/rootfs
mount -t ext4 -o loop rootfs.ext4.img /mnt/rootfs
# 將rootfs內(nèi)所有文件拷貝至rootfs.ext4.img
cp -a /home/damon/00_code/02_gitee/busy_box/rootfs/* /mnt/rootfs/
# 卸載
umount /mnt/rootfs
# 此時(shí)得到一個(gè)裝有rootfs的鏡像
/home/damon/00_code/02_gitee/rootfs.ext4.img
現(xiàn)在rootfs做好了,我們?nèi)バ迯?fù)上面的Panic.
5、 重新啟動(dòng)kernel
我們?cè)谠械膓emu命令基礎(chǔ)上指定rootfs的鏡像文件,重新啟動(dòng):
qemu-system-arm -M vexpress-a9 -m 512M -kernel /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/zImage -dtb /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -sd /home/damon/00_code/02_gitee/rootfs.ext4.img -append "root=/dev/mmcblk0 rw console=ttyAMA0"
啟動(dòng)日志如下:
之前的kernel panic沒有了,但是又有新的告警了,一般錯(cuò)誤是必須要修復(fù)的,而告警內(nèi)容是可以忽略的,除非影響到了使用和性能。
當(dāng)前告警在循環(huán)打印無法打開,/dev/tty2 /dev/tty3 /dev/tty4, 看到這個(gè)我們顯然知道是缺少串口設(shè)備,按照如下腳本添加,并更新鏡像文件。
sudo mknod dev/tty2 c 5 1
sudo mknod dev/tty3 c 5 1
sudo mknod dev/tty4 c 5 1
此時(shí)Linux操作系統(tǒng)起來了,完整的詳細(xì)的啟動(dòng)日志如下:
damon@ubuntu:~/00_code/02_gitee$
damon@ubuntu:~/00_code/02_gitee$
damon@ubuntu:~/00_code/02_gitee$ qemu-system-arm -M vexpress-a9 -m 512M -kernel /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/zImage -dtb /home/damon/00_code/02_gitee/linux_5.4/linux-5.4/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -sd /home/damon/00_code/02_gitee/rootfs.ext4.img -append "root=/dev/mmcblk0 rw console=ttyAMA0"
WARNING: Image format was not specified for '/home/damon/00_code/02_gitee/rootfs.ext4.img' and probing guessed raw.
Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
Specify the 'raw' format explicitly to remove the restrictions.
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument
Booting Linux on physical CPU 0x0
Linux version 5.4.0+ (damon@ubuntu) (gcc version 9.2.1 20191025 (GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10))) #1 SMP Sat Jan 14 15:12:10 CST 2023
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writeback
Reserved memory: created DMA memory