2. Rootfs基础概念:Rootfs的定义、组成结构与内核的关系

好,咱们正式开始聊Rootfs。说实话,很多刚入行的驱动工程师,对Rootfs的理解就是「一个文件系统,里面放了点东西」。嗯,这么说也没错,但太粗糙了。你想想看,内核启动之后,第一个挂载的文件系统就是Rootfs。没有它,内核就是个光杆司令,啥也干不了。

我当年第一次做嵌入式项目时,就踩过这个坑。当时内核启动后卡在VFS: Cannot open root device,折腾了两天才发现是Rootfs里缺了/dev/console这个设备节点。从那以后,我对Rootfs的敬畏心就上来了。

2.1 Rootfs到底是什么?

Rootfs,全称Root Filesystem,根文件系统。它不是一个具体的文件系统类型(比如ext4、squashfs),而是一个逻辑概念。说白了,它就是内核启动后挂载的第一个文件系统,是用户空间所有进程的起点。

内核启动到最后阶段,会调用init进程——这个init程序就在Rootfs里。如果Rootfs里没有/sbin/init或者/bin/sh,内核就会panic,直接死给你看。

核心定义:Rootfs是内核与用户空间之间的桥梁。内核提供硬件抽象,Rootfs提供用户空间运行所需的一切。

2.2 Rootfs的组成结构

一个标准的Rootfs,目录结构是有讲究的。我习惯把它分成三类:必备目录、功能目录和动态目录。

2.2.1 必备目录(少了就启动不了)

目录 作用 我的经验
/bin 存放用户空间的基本命令,如sh、ls、mount 至少要有sh和mount,否则调试都进不去
/sbin 系统管理命令,如init、fdisk /sbin/init是内核默认的init程序路径
/etc 配置文件,如inittab、fstab 我习惯把/etc/inittab作为第一个调试目标
/dev 设备节点,如console、null 缺/dev/console,内核启动后直接黑屏
/lib 共享库和内核模块 动态链接的程序依赖这里,缺库就报No such file
/proc 进程信息虚拟文件系统 启动后必须挂载,否则ps用不了
/sys 内核对象虚拟文件系统 驱动开发必备,查看设备树、驱动状态全靠它
注意:我曾经在一个项目里,为了省空间把/lib删了,结果所有动态链接的程序都跑不起来。最后老老实实加回去,还多花了半天时间排查。省空间可以,但别省核心库。

2.2.2 功能目录(按需添加)

  • /usr:用户程序和数据,嵌入式里常和/合并
  • /var:可变数据,如日志、锁文件
  • /tmp:临时文件,通常挂载为tmpfs
  • /home:用户目录,嵌入式设备一般不用
  • /mnt和/media:挂载点,用于挂载U盘、SD卡等

嗯,这里要注意:嵌入式设备资源有限,不是所有目录都要有。我见过的最小Rootfs只有/bin、/sbin、/etc、/dev、/lib、/proc、/sys这7个目录,照样跑得稳稳的。

2.3 Rootfs与内核的关系

内核和Rootfs的关系,我打个比方:内核是汽车的发动机和底盘,Rootfs就是驾驶舱和方向盘。发动机再牛,没有方向盘你也开不走。

具体来说,有这几个关键点:

2.3.1 内核如何找到Rootfs?

内核启动时,通过root=参数指定Rootfs的位置。比如:

root=/dev/mmcblk0p2   // 从SD卡第二个分区挂载
root=/dev/nfs          // 从网络挂载(NFS)
root=31:02             // 用主次设备号指定

我调试驱动时最喜欢用NFS挂载Rootfs。为什么呢?因为修改Rootfs内容后不用重新烧录,直接重启就行。开发效率提升一大截。

2.3.2 内核模块与Rootfs

驱动编译成模块(.ko文件)时,这些模块通常放在/lib/modules/$(uname -r)/目录下。内核启动后,由init进程或modprobe命令加载。

这里有个坑:内核版本必须和模块版本一致。我遇到过好几次,升级内核后忘了更新Rootfs里的模块,结果驱动加载失败,报Invalid module format。嗯,血的教训。

2.3.3 设备节点与驱动

驱动注册成功后,会在/dev下创建设备节点。但注意:静态设备节点需要提前在Rootfs里创建好。比如:

mknod /dev/console c 5 1
mknod /dev/null c 1 3
mknod /dev/ttyS0 c 4 64

现在很多系统用devtmpfs或udev自动管理设备节点,但嵌入式设备里,我仍然习惯手动创建几个关键节点,防止自动管理出问题。

2.4 一张图看懂Rootfs

下面这张SVG图,是我自己总结的Rootfs与内核的关系。你看完应该就明白了。

Rootfs与内核关系图 Linux 内核 进程调度 | 内存管理 | 文件系统 | 网络协议栈 | 设备驱动 Rootfs 根文件系统 /bin 基本命令 /sbin 系统管理 /etc 配置文件 /dev 设备节点 /lib 库和模块 /proc 进程信息 /sys 内核对象 /usr 用户程序 /var 可变数据 /tmp 临时文件 内核通过 root= 参数挂载 Rootfs → 执行 /sbin/init → 启动用户空间

2.5 驱动开发者的视角

作为驱动开发者,我关注Rootfs的这几个点:

  1. 设备节点:驱动注册后,/dev下有没有对应的节点?没有的话,应用程序怎么访问?
  2. 模块存放:驱动模块放在/lib/modules/下,版本号必须匹配内核版本。
  3. 启动脚本:/etc/init.d/或/etc/rc.d/里的脚本,负责加载驱动模块。
  4. 调试工具:/bin里有没有insmod、lsmod、dmesg?没有的话,调试驱动会很痛苦。
小技巧:我习惯在Rootfs里放一个/etc/init.d/S00debug脚本,启动时自动加载常用调试工具。比如:
mount -t debugfs none /sys/kernel/debug
这样驱动调试时可以直接看debugfs的输出。

2.6 常见误区

最后,说几个我见过的常见误区:

  • 误区一:Rootfs越大越好。错!嵌入式设备存储空间有限,Rootfs应该精简到够用就行。
  • 误区二:所有驱动都编译进内核,不需要Rootfs。错!即使驱动编译进内核,Rootfs仍然需要提供init进程和必要的设备节点。
  • 误区三:Rootfs可以随便删目录。错!删了/lib或/dev,系统可能直接崩溃。

嗯,关于Rootfs的基础概念,今天就聊这么多。记住一句话:Rootfs是用户空间的基石,驱动是内核的延伸。两者配合好了,系统才能跑得稳。

可可精品资料库