3、Rootfs制作基础:Busybox编译与安装、构建最小根文件系统、init进程与inittab配置、系统启动流程
好,咱们今天聊聊Rootfs制作。说实话,很多做驱动的朋友,一提到根文件系统就头疼。我刚开始带项目那会儿,也踩过不少坑。其实说白了,Rootfs就是Linux系统启动后,内核挂载的第一个文件系统。它里面装着init进程、各种库、配置文件,还有你那些应用程序。
没有Rootfs,内核启动到一半就卡住了——它会直接panic,告诉你“VFS: Unable to mount root fs”。嗯,这个错误我见过太多次了。
3.1 Busybox:麻雀虽小,五脏俱全
构建最小根文件系统,首选工具就是Busybox。它把几百个常用的Linux命令打包成一个可执行文件,通过符号链接来区分不同的命令。你想想看,一个完整的ls、cp、mv、cat,在Busybox里可能只占几百KB。
我个人习惯用Busybox做嵌入式系统的Rootfs,原因很简单:省空间、易维护、社区活跃。
3.1.1 下载与配置
先下载源码:
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xjf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
配置的时候,我建议用默认配置再微调:
make defconfig
make menuconfig
在menuconfig里,有几个关键选项要注意:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| Build static binary (no shared libs) | 选中 | 静态编译,避免动态库依赖问题 |
| Cross Compiler prefix | arm-linux-gnueabihf- | 根据你的目标架构设置 |
| Installation prefix | ./_install | 安装路径,后面会用到 |
3.1.2 编译与安装
配置好之后,直接编译:
make -j$(nproc)
make install
编译完成后,_install目录下就会出现bin、sbin、usr等子目录,以及一个linuxrc文件。这个linuxrc其实就是Busybox的init程序,后面我们会用到它。
3.2 构建最小根文件系统
有了Busybox的安装目录,我们就可以开始组装Rootfs了。我一般会手动创建几个必要的目录:
mkdir -p rootfs/{bin,sbin,etc,dev,proc,sys,usr/{bin,sbin},lib,root,tmp,var/{log,run}}
然后把Busybox的_install内容拷贝进去:
cp -a busybox-1.36.1/_install/* rootfs/
接下来,创建设备节点。这是新手最容易忽略的地方。没有/dev/null、/dev/console这些设备节点,系统启动时会报错。
sudo mknod rootfs/dev/null c 1 3
sudo mknod rootfs/dev/console c 5 1
sudo mknod rootfs/dev/ttyS0 c 4 64 # 串口设备,看你的硬件
3.3 init进程与inittab配置
内核启动完成后,会尝试执行第一个用户空间程序——init。在Busybox里,这个init就是/bin/init(或者linuxrc)。init进程会读取/etc/inittab文件,根据里面的配置来启动各种服务。
一个典型的inittab文件长这样:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::restart:/sbin/init
每一行的格式是:id:runlevels:action:process。对于Busybox来说,id和runlevels通常可以留空。action字段是关键,它决定了进程在什么情况下启动。
| action | 含义 |
|---|---|
| sysinit | 系统初始化时执行,只执行一次 |
| askfirst | 在终端上显示“Please press Enter to activate this console”,等待用户按回车后才启动shell |
| respawn | 进程终止后自动重启 |
| once | 只启动一次,不重启 |
| ctrlaltdel | 按下Ctrl+Alt+Del时执行 |
| shutdown | 系统关机时执行 |
我一般会在/etc/init.d/rcS里写系统初始化脚本,比如挂载文件系统、配置网络、启动服务等:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp
mdev -s # 动态创建设备节点
echo "System initialized"
3.4 系统启动流程
理解了上面的内容,我们来看看整个启动流程。我画了一张图,帮你理清思路:
整个流程其实就五步:
- Bootloader:U-Boot加载内核,设置bootargs(比如root=/dev/mmcblk0p2 console=ttyS0,115200)
- 内核启动:内核解压、初始化各子系统、挂载根文件系统
- init进程:内核执行第一个用户程序,通常是/sbin/init
- 系统初始化:init读取inittab,执行rcS脚本,挂载虚拟文件系统
- 用户空间:启动shell,系统就绪
initcall_debug和loglevel=8,这样能看到内核初始化每个驱动的详细信息。如果卡在某个地方,基本就是那个驱动或者硬件有问题。
嗯,到这里,Rootfs制作的基础知识就讲完了。你可能会问:为什么我的系统启动后没有shell?为什么挂载根文件系统失败?别急,后面我们会专门讲这些问题的排查方法。先把今天的内容消化掉,动手做一遍,比看十遍都管用。
记住一句话:最小Rootfs的核心就是Busybox + inittab + rcS。把这三样搞明白,剩下的就是往里面加东西了。